remove scapy & add sample
This commit is contained in:
parent
c716d9cd3a
commit
5f3a990994
354 changed files with 1027 additions and 164150 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -6,5 +6,6 @@
|
||||||
],
|
],
|
||||||
"python.autoComplete.extraPaths": [
|
"python.autoComplete.extraPaths": [
|
||||||
"libs"
|
"libs"
|
||||||
]
|
],
|
||||||
|
"python.pythonPath": "runtime/install/bin/python3"
|
||||||
}
|
}
|
|
@ -13,15 +13,11 @@ import click
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from colorama import Fore, init
|
from colorama import Fore, init
|
||||||
|
from scapy.layers.bluetooth import HCI_Hdr, HCI_PHDR_Hdr
|
||||||
sys.path.insert(0, os.getcwd() + '/libs')
|
|
||||||
|
|
||||||
from scapy.layers.bluetooth import HCI_Hdr, ESP32_BREDR, BT_Baseband, BT_ACL_Hdr, BT_LMP, HCI_PHDR_Hdr
|
|
||||||
from scapy.utils import wrpcap, PcapWriter
|
from scapy.utils import wrpcap, PcapWriter
|
||||||
from ESP32BTDriver import ESP32BTDriver
|
# Custom libs
|
||||||
|
from src.layers_bredr import ESP32_BREDR, BT_Baseband, BT_ACL_Hdr, BT_LMP
|
||||||
|
from src.ESP32BTDriver import ESP32BTDriver
|
||||||
init(autoreset=True)
|
|
||||||
|
|
||||||
|
|
||||||
class SnifferBREDR:
|
class SnifferBREDR:
|
||||||
|
@ -262,4 +258,5 @@ def sniffer(port, host, target, live_wireshark, live_terminal, bridge_only):
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
init(autoreset=True)
|
||||||
sniffer()
|
sniffer()
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
|
|
||||||
#define TLV_DB_PATH_PREFIX "/tmp/btstack_"
|
#define TLV_DB_PATH_PREFIX "/tmp/btstack_"
|
||||||
#define TLV_DB_PATH_POSTFIX ".tlv"
|
#define TLV_DB_PATH_POSTFIX ".tlv"
|
||||||
#define LOG_FILE_PATH "logs/Bluetooth/hci_dump.pklg"
|
#define LOG_FILE_PATH "logs/hci_dump.pklg"
|
||||||
|
|
||||||
static char tlv_db_path[100];
|
static char tlv_db_path[100];
|
||||||
static const btstack_tlv_t *tlv_impl;
|
static const btstack_tlv_t *tlv_impl;
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
python2.dev0
|
|
|
@ -1,119 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Scapy: create, send, sniff, dissect and manipulate network packets.
|
|
||||||
|
|
||||||
Usable either from an interactive console or as a Python library.
|
|
||||||
https://scapy.net
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from scapy.compat import AnyStr
|
|
||||||
|
|
||||||
|
|
||||||
_SCAPY_PKG_DIR = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
|
|
||||||
def _version_from_git_describe():
|
|
||||||
# type: () -> AnyStr
|
|
||||||
"""
|
|
||||||
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'
|
|
||||||
|
|
||||||
:raises CalledProcessError: if git is unavailable
|
|
||||||
:return: Scapy's latest tag
|
|
||||||
"""
|
|
||||||
if not os.path.isdir(os.path.join(os.path.dirname(_SCAPY_PKG_DIR), '.git')): # noqa: E501
|
|
||||||
raise ValueError('not in scapy git repo')
|
|
||||||
|
|
||||||
def _git(cmd):
|
|
||||||
process = subprocess.Popen(
|
|
||||||
cmd.split(),
|
|
||||||
cwd=_SCAPY_PKG_DIR,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
out, err = process.communicate()
|
|
||||||
if process.returncode == 0:
|
|
||||||
return out.decode().strip()
|
|
||||||
else:
|
|
||||||
raise subprocess.CalledProcessError(process.returncode, err)
|
|
||||||
|
|
||||||
tag = _git("git describe --always")
|
|
||||||
if not tag.startswith("v"):
|
|
||||||
# Upstream was not fetched
|
|
||||||
commit = _git("git rev-list --tags --max-count=1")
|
|
||||||
tag = _git("git describe --tags --always --long %s" % commit)
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def _version():
|
|
||||||
# type: () -> str
|
|
||||||
"""Returns the Scapy version from multiple methods
|
|
||||||
|
|
||||||
:return: the Scapy 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 fdesc:
|
|
||||||
fdesc.write(tag)
|
|
||||||
return tag
|
|
||||||
except Exception:
|
|
||||||
# failed to read the tag from git, try to read it from a VERSION file
|
|
||||||
try:
|
|
||||||
with open(version_file, 'r') as fdsec:
|
|
||||||
tag = fdsec.read()
|
|
||||||
return tag
|
|
||||||
except Exception:
|
|
||||||
# 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__ = _version()
|
|
||||||
|
|
||||||
_tmp = re.search(r"[0-9.]+", VERSION)
|
|
||||||
VERSION_MAIN = _tmp.group() if _tmp is not None else VERSION
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
from scapy.main import interact
|
|
||||||
interact()
|
|
|
@ -1,15 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
|
|
||||||
from scapy.main import interact
|
|
||||||
|
|
||||||
interact()
|
|
|
@ -1,52 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
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 *
|
|
||||||
from scapy.sendrecv import *
|
|
||||||
from scapy.sessions import *
|
|
||||||
from scapy.supersocket import *
|
|
||||||
from scapy.volatile import *
|
|
||||||
from scapy.as_resolvers import *
|
|
||||||
|
|
||||||
from scapy.automaton import *
|
|
||||||
from scapy.autorun import *
|
|
||||||
|
|
||||||
from scapy.main import *
|
|
||||||
from scapy.consts import *
|
|
||||||
from scapy.compat import raw # noqa: F401
|
|
||||||
|
|
||||||
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 *
|
|
||||||
|
|
||||||
if conf.ipv6_enabled: # noqa: F405
|
|
||||||
from scapy.utils6 import * # noqa: F401
|
|
||||||
from scapy.route6 import * # noqa: F401
|
|
||||||
|
|
||||||
from scapy.ansmachine import *
|
|
|
@ -1,131 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Answering machines.
|
|
||||||
"""
|
|
||||||
|
|
||||||
########################
|
|
||||||
# Answering machines #
|
|
||||||
########################
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import print_function
|
|
||||||
from scapy.sendrecv import send, 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):
|
|
||||||
obj = super(ReferenceAM, cls).__new__(cls, name, bases, dct)
|
|
||||||
if obj.function_name:
|
|
||||||
globals()[obj.function_name] = lambda obj=obj, *args, **kargs: obj(*args, **kargs)() # noqa: E501
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
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"] # noqa: E501
|
|
||||||
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 dct in [self.optam2, self.optam1]:
|
|
||||||
if attr in dct:
|
|
||||||
return dct[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") # noqa: E501
|
|
||||||
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)
|
|
|
@ -1,95 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Operating system specific functionality.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from scapy.consts import LINUX, SOLARIS, WINDOWS, BSD
|
|
||||||
from scapy.error import Scapy_Exception
|
|
||||||
from scapy.config import conf, _set_conf_sockets
|
|
||||||
from scapy.pton_ntop import inet_pton, inet_ntop
|
|
||||||
from scapy.data import ARPHDR_ETHER, ARPHDR_LOOPBACK, IPV6_ADDR_GLOBAL
|
|
||||||
from scapy.compat import orb
|
|
||||||
|
|
||||||
|
|
||||||
def str2mac(s):
|
|
||||||
return ("%02x:" * 6)[:-1] % tuple(orb(x) for x in s)
|
|
||||||
|
|
||||||
|
|
||||||
if not WINDOWS:
|
|
||||||
if not conf.use_pcap:
|
|
||||||
from scapy.arch.bpf.core import get_if_raw_addr
|
|
||||||
|
|
||||||
|
|
||||||
def get_if_addr(iff):
|
|
||||||
return inet_ntop(socket.AF_INET, get_if_raw_addr(iff))
|
|
||||||
|
|
||||||
|
|
||||||
def get_if_hwaddr(iff):
|
|
||||||
addrfamily, mac = get_if_raw_hwaddr(iff) # noqa: F405
|
|
||||||
if addrfamily in [ARPHDR_ETHER, ARPHDR_LOOPBACK]:
|
|
||||||
return str2mac(mac)
|
|
||||||
else:
|
|
||||||
raise Scapy_Exception("Unsupported address family (%i) for interface [%s]" % (addrfamily, iff)) # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
# 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 * # noqa F403
|
|
||||||
elif BSD:
|
|
||||||
from scapy.arch.unix import read_routes, read_routes6, in6_getifaddr # noqa: F401, E501
|
|
||||||
from scapy.arch.bpf.core import * # noqa F403
|
|
||||||
if not conf.use_pcap:
|
|
||||||
# Native
|
|
||||||
from scapy.arch.bpf.supersocket import * # noqa F403
|
|
||||||
conf.use_bpf = True
|
|
||||||
elif SOLARIS:
|
|
||||||
from scapy.arch.solaris import * # noqa F403
|
|
||||||
elif WINDOWS:
|
|
||||||
from scapy.arch.windows import * # noqa F403
|
|
||||||
from scapy.arch.windows.native import * # noqa F403
|
|
||||||
|
|
||||||
if conf.iface is None:
|
|
||||||
conf.iface = conf.loopback_name
|
|
||||||
|
|
||||||
_set_conf_sockets() # Apply config
|
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
return next((x[0] for x in in6_getifaddr()
|
|
||||||
if x[2] == iff and x[1] == IPV6_ADDR_GLOBAL), 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
|
|
|
@ -1,5 +0,0 @@
|
||||||
# Guillaume Valadon <guillaume@valadon.net>
|
|
||||||
|
|
||||||
"""
|
|
||||||
Scapy BSD native support
|
|
||||||
"""
|
|
|
@ -1,27 +0,0 @@
|
||||||
# Guillaume Valadon <guillaume@valadon.net>
|
|
||||||
|
|
||||||
"""
|
|
||||||
Scapy BSD native support - constants
|
|
||||||
"""
|
|
||||||
|
|
||||||
from ctypes import sizeof
|
|
||||||
|
|
||||||
from scapy.libs.structures import bpf_program
|
|
||||||
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 = 0x80004267 | ((sizeof(bpf_program) & 0x1fff) << 16)
|
|
||||||
BIOCSDLT = 0x80044278
|
|
||||||
BIOCSHDRCMPLT = 0x80044275
|
|
||||||
BIOCGDLT = 0x4004426a
|
|
||||||
DLT_IEEE802_11_RADIO = 127
|
|
|
@ -1,211 +0,0 @@
|
||||||
# Guillaume Valadon <guillaume@valadon.net>
|
|
||||||
|
|
||||||
"""
|
|
||||||
Scapy *BSD native support - core
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from ctypes import cdll, cast, pointer
|
|
||||||
from ctypes import c_int, c_ulong, c_char_p
|
|
||||||
from ctypes.util import find_library
|
|
||||||
import fcntl
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from scapy.arch.bpf.consts import BIOCSETF, SIOCGIFFLAGS, BIOCSETIF
|
|
||||||
from scapy.arch.common import get_if, compile_filter
|
|
||||||
from scapy.compat import plain_str
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.data import ARPHDR_LOOPBACK, ARPHDR_ETHER
|
|
||||||
from scapy.error import Scapy_Exception, warning
|
|
||||||
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.""" # noqa: E501
|
|
||||||
|
|
||||||
# Get ifconfig output
|
|
||||||
subproc = subprocess.Popen(
|
|
||||||
[conf.prog.ifconfig, ifname],
|
|
||||||
close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
stdout, stderr = subproc.communicate()
|
|
||||||
if subproc.returncode:
|
|
||||||
warning("Failed to execute ifconfig: (%s)", plain_str(stderr))
|
|
||||||
return b"\0\0\0\0"
|
|
||||||
# Get IPv4 addresses
|
|
||||||
|
|
||||||
addresses = [
|
|
||||||
line for line in plain_str(stdout).splitlines()
|
|
||||||
if "inet " in line
|
|
||||||
]
|
|
||||||
|
|
||||||
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]
|
|
||||||
if '/' in address: # NetBSD 8.0
|
|
||||||
address = address.split("/")[0]
|
|
||||||
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 == conf.loopback_name:
|
|
||||||
return (ARPHDR_LOOPBACK, NULL_MAC_ADDRESS)
|
|
||||||
|
|
||||||
# Get ifconfig output
|
|
||||||
subproc = subprocess.Popen(
|
|
||||||
[conf.prog.ifconfig, ifname],
|
|
||||||
close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
stdout, stderr = subproc.communicate()
|
|
||||||
if subproc.returncode:
|
|
||||||
raise Scapy_Exception("Failed to execute ifconfig: (%s)" %
|
|
||||||
(plain_str(stderr)))
|
|
||||||
|
|
||||||
# Get MAC addresses
|
|
||||||
addresses = [
|
|
||||||
line for line in plain_str(stdout).splitlines() if (
|
|
||||||
"ether" in line or "lladdr" in line or "address" in line
|
|
||||||
)
|
|
||||||
]
|
|
||||||
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(256):
|
|
||||||
try:
|
|
||||||
fd = os.open("/dev/bpf%i" % bpf, os.O_RDWR)
|
|
||||||
return (fd, bpf)
|
|
||||||
except OSError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
raise Scapy_Exception("No /dev/bpf handle is available !")
|
|
||||||
|
|
||||||
|
|
||||||
def attach_filter(fd, bpf_filter, iface):
|
|
||||||
"""Attach a BPF filter to the BPF file descriptor"""
|
|
||||||
bp = compile_filter(bpf_filter, iface)
|
|
||||||
# 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
|
|
||||||
subproc = subprocess.Popen(
|
|
||||||
[conf.prog.ifconfig],
|
|
||||||
close_fds=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE
|
|
||||||
)
|
|
||||||
stdout, stderr = subproc.communicate()
|
|
||||||
if subproc.returncode:
|
|
||||||
raise Scapy_Exception("Failed to execute ifconfig: (%s)" %
|
|
||||||
(plain_str(stderr)))
|
|
||||||
|
|
||||||
interfaces = [
|
|
||||||
line[:line.find(':')] for line in plain_str(stdout).splitlines()
|
|
||||||
if ": flags" in line.lower()
|
|
||||||
]
|
|
||||||
return interfaces
|
|
||||||
|
|
||||||
|
|
||||||
_IFNUM = re.compile(r"([0-9]*)([ab]?)$")
|
|
||||||
|
|
||||||
|
|
||||||
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 == conf.loopback_name:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Get interface flags
|
|
||||||
try:
|
|
||||||
result = get_if(ifname, SIOCGIFFLAGS)
|
|
||||||
except IOError:
|
|
||||||
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()[0]
|
|
||||||
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()))
|
|
||||||
except IOError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
ifnum, ifab = _IFNUM.search(ifname).groups()
|
|
||||||
interfaces.append((ifname, int(ifnum) if ifnum else -1, ifab))
|
|
||||||
finally:
|
|
||||||
# Close the file descriptor
|
|
||||||
os.close(fd)
|
|
||||||
|
|
||||||
# Sort to mimic pcap_findalldevs() order
|
|
||||||
interfaces.sort(key=lambda elt: (elt[1], elt[2], elt[0]))
|
|
||||||
|
|
||||||
return [iface[0] for iface in 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 conf.loopback_name
|
|
||||||
return ifaces[0]
|
|
|
@ -1,423 +0,0 @@
|
||||||
# Guillaume Valadon <guillaume@valadon.net>
|
|
||||||
|
|
||||||
"""
|
|
||||||
Scapy *BSD native support - BPF sockets
|
|
||||||
"""
|
|
||||||
|
|
||||||
from ctypes import c_long, sizeof
|
|
||||||
import errno
|
|
||||||
import fcntl
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
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, BIOCSDLT, DLT_IEEE802_11_RADIO
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.consts import FREEBSD, NETBSD, DARWIN
|
|
||||||
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:
|
|
||||||
# On 32bit architectures long might be 32bit.
|
|
||||||
BPF_ALIGNMENT = sizeof(c_long)
|
|
||||||
elif 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"
|
|
||||||
nonblocking_socket = True
|
|
||||||
|
|
||||||
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None,
|
|
||||||
nofilter=0, monitor=False):
|
|
||||||
self.fd_flags = None
|
|
||||||
self.assigned_interface = None
|
|
||||||
|
|
||||||
# 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)) # noqa: E501
|
|
||||||
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())) # noqa: E501
|
|
||||||
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)
|
|
||||||
|
|
||||||
# Set the interface to monitor mode
|
|
||||||
# Note: - trick from libpcap/pcap-bpf.c - monitor_mode()
|
|
||||||
# - it only works on OS X 10.5 and later
|
|
||||||
if DARWIN and monitor:
|
|
||||||
dlt_radiotap = struct.pack('I', DLT_IEEE802_11_RADIO)
|
|
||||||
try:
|
|
||||||
fcntl.ioctl(self.ins, BIOCSDLT, dlt_radiotap)
|
|
||||||
except IOError:
|
|
||||||
raise Scapy_Exception("Can't set %s into monitor mode!" %
|
|
||||||
self.iface)
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
try:
|
|
||||||
attach_filter(self.ins, filter, self.iface)
|
|
||||||
except ImportError as ex:
|
|
||||||
warning("Cannot set filter: %s" % ex)
|
|
||||||
|
|
||||||
# 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 Exception:
|
|
||||||
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.__class__.__name__
|
|
||||||
)
|
|
||||||
|
|
||||||
def recv_raw(self, x=BPF_BUFFER_LENGTH):
|
|
||||||
"""Dummy recv method"""
|
|
||||||
raise Exception(
|
|
||||||
"Can't recv anything with %s" % self.__class__.__name__
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select(sockets, remain=None):
|
|
||||||
"""This function is called during sendrecv() routine to select
|
|
||||||
the available sockets.
|
|
||||||
"""
|
|
||||||
# sockets, None (means use the socket's recv() )
|
|
||||||
return bpf_select(sockets, remain), None
|
|
||||||
|
|
||||||
|
|
||||||
class L2bpfListenSocket(_L2bpfSocket):
|
|
||||||
""""Scapy L2 BPF Listen Super Socket"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.received_frames = []
|
|
||||||
super(L2bpfListenSocket, self).__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
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)
|
|
||||||
else:
|
|
||||||
return None, None, None
|
|
||||||
|
|
||||||
@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.""" # noqa: E501
|
|
||||||
|
|
||||||
# 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:
|
|
||||||
# Unless we set BIOCSTSTAMP to something different than
|
|
||||||
# BPF_T_MICROTIME, we will get bpf_hdr on FreeBSD, which means
|
|
||||||
# that we'll get a struct timeval, which is time_t, suseconds_t.
|
|
||||||
# On i386 time_t is 32bit so the bh_tstamp will only be 8 bytes.
|
|
||||||
# We really want to set BIOCSTSTAMP to BPF_T_NANOTIME and be
|
|
||||||
# done with this and it always be 16?
|
|
||||||
if platform.machine() == "i386":
|
|
||||||
# struct bpf_hdr
|
|
||||||
bh_tstamp_offset = 8
|
|
||||||
else:
|
|
||||||
# struct bpf_hdr (64bit time_t) or struct bpf_xhdr
|
|
||||||
bh_tstamp_offset = 16
|
|
||||||
elif NETBSD:
|
|
||||||
# struct bpf_hdr 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] # noqa: E501
|
|
||||||
next_offset = bh_tstamp_offset + 4
|
|
||||||
bh_datalen = struct.unpack('I', bpf_buffer[next_offset:next_offset + 4])[0] # noqa: E501
|
|
||||||
next_offset += 4
|
|
||||||
bh_hdrlen = struct.unpack('H', bpf_buffer[next_offset:next_offset + 2])[0] # noqa: E501
|
|
||||||
if bh_datalen == 0:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get and store the Scapy object
|
|
||||||
frame_str = bpf_buffer[bh_hdrlen:bh_hdrlen + bh_caplen]
|
|
||||||
self.received_frames.append(
|
|
||||||
(self.guessed_cls, frame_str, None)
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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_raw(self, x=BPF_BUFFER_LENGTH):
|
|
||||||
"""Receive a frame from the network"""
|
|
||||||
|
|
||||||
x = min(x, BPF_BUFFER_LENGTH)
|
|
||||||
|
|
||||||
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_raw()", exc_info=True)
|
|
||||||
return None, None, None
|
|
||||||
|
|
||||||
# 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 L2bpfListenSocket.recv(self)
|
|
||||||
|
|
||||||
# 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 recv(self, x=BPF_BUFFER_LENGTH):
|
|
||||||
"""Receive on layer 3"""
|
|
||||||
r = SuperSocket.recv(self, x)
|
|
||||||
if r:
|
|
||||||
r.payload.time = r.time
|
|
||||||
return r.payload
|
|
||||||
return r
|
|
||||||
|
|
||||||
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())) # noqa: E501
|
|
||||||
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, L2bpfListenSocket, 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
|
|
|
@ -1,138 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Functions common to different architectures
|
|
||||||
"""
|
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from scapy.consts import WINDOWS
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.data import MTU, ARPHRD_TO_DLT
|
|
||||||
from scapy.error import Scapy_Exception
|
|
||||||
|
|
||||||
if not WINDOWS:
|
|
||||||
from fcntl import ioctl
|
|
||||||
|
|
||||||
# UTILS
|
|
||||||
|
|
||||||
|
|
||||||
def get_if(iff, cmd):
|
|
||||||
"""Ease SIOCGIF* ioctl calls"""
|
|
||||||
|
|
||||||
sck = socket.socket()
|
|
||||||
try:
|
|
||||||
return ioctl(sck, cmd, struct.pack("16s16x", iff.encode("utf8")))
|
|
||||||
finally:
|
|
||||||
sck.close()
|
|
||||||
|
|
||||||
|
|
||||||
def get_if_raw_hwaddr(iff):
|
|
||||||
"""Get the raw MAC address of a local interface.
|
|
||||||
|
|
||||||
This function uses SIOCGIFHWADDR calls, therefore only works
|
|
||||||
on some distros.
|
|
||||||
|
|
||||||
:param iff: the network interface name as a string
|
|
||||||
:returns: the corresponding raw MAC address
|
|
||||||
"""
|
|
||||||
from scapy.arch import SIOCGIFHWADDR
|
|
||||||
return struct.unpack("16xh6s8x", get_if(iff, SIOCGIFHWADDR))
|
|
||||||
|
|
||||||
# SOCKET UTILS
|
|
||||||
|
|
||||||
|
|
||||||
def _select_nonblock(sockets, remain=None):
|
|
||||||
"""This function is called during sendrecv() routine to select
|
|
||||||
the available sockets.
|
|
||||||
"""
|
|
||||||
# pcap sockets aren't selectable, so we return all of them
|
|
||||||
# and ask the selecting functions to use nonblock_recv instead of recv
|
|
||||||
def _sleep_nonblock_recv(self):
|
|
||||||
res = self.nonblock_recv()
|
|
||||||
if res is None:
|
|
||||||
time.sleep(conf.recv_poll_rate)
|
|
||||||
return res
|
|
||||||
# we enforce remain=None: don't wait.
|
|
||||||
return sockets, _sleep_nonblock_recv
|
|
||||||
|
|
||||||
# BPF HANDLERS
|
|
||||||
|
|
||||||
|
|
||||||
def compile_filter(filter_exp, iface=None, linktype=None,
|
|
||||||
promisc=False):
|
|
||||||
"""Asks libpcap to parse the filter, then build the matching
|
|
||||||
BPF bytecode.
|
|
||||||
|
|
||||||
:param iface: if provided, use the interface to compile
|
|
||||||
:param linktype: if provided, use the linktype to compile
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from scapy.libs.winpcapy import (
|
|
||||||
PCAP_ERRBUF_SIZE,
|
|
||||||
pcap_open_live,
|
|
||||||
pcap_compile,
|
|
||||||
pcap_compile_nopcap,
|
|
||||||
pcap_close
|
|
||||||
)
|
|
||||||
from scapy.libs.structures import bpf_program
|
|
||||||
except OSError:
|
|
||||||
raise ImportError(
|
|
||||||
"libpcap is not available. Cannot compile filter !"
|
|
||||||
)
|
|
||||||
from ctypes import create_string_buffer
|
|
||||||
bpf = bpf_program()
|
|
||||||
bpf_filter = create_string_buffer(filter_exp.encode("utf8"))
|
|
||||||
if not linktype:
|
|
||||||
# Try to guess linktype to avoid root
|
|
||||||
if not iface:
|
|
||||||
if not conf.iface:
|
|
||||||
raise Scapy_Exception(
|
|
||||||
"Please provide an interface or linktype!"
|
|
||||||
)
|
|
||||||
if WINDOWS:
|
|
||||||
iface = conf.iface.pcap_name
|
|
||||||
else:
|
|
||||||
iface = conf.iface
|
|
||||||
# Try to guess linktype to avoid requiring root
|
|
||||||
try:
|
|
||||||
arphd = get_if_raw_hwaddr(iface)[0]
|
|
||||||
linktype = ARPHRD_TO_DLT.get(arphd)
|
|
||||||
except Exception:
|
|
||||||
# Failed to use linktype: use the interface
|
|
||||||
pass
|
|
||||||
if linktype is not None:
|
|
||||||
ret = pcap_compile_nopcap(
|
|
||||||
MTU, linktype, ctypes.byref(bpf), bpf_filter, 0, -1
|
|
||||||
)
|
|
||||||
elif iface:
|
|
||||||
err = create_string_buffer(PCAP_ERRBUF_SIZE)
|
|
||||||
iface = create_string_buffer(iface.encode("utf8"))
|
|
||||||
pcap = pcap_open_live(
|
|
||||||
iface, MTU, promisc, 0, err
|
|
||||||
)
|
|
||||||
error = bytes(bytearray(err)).strip(b"\x00")
|
|
||||||
if error:
|
|
||||||
raise OSError(error)
|
|
||||||
ret = pcap_compile(
|
|
||||||
pcap, ctypes.byref(bpf), bpf_filter, 0, -1
|
|
||||||
)
|
|
||||||
pcap_close(pcap)
|
|
||||||
if ret == -1:
|
|
||||||
raise Scapy_Exception(
|
|
||||||
"Failed to compile filter expression %s (%s)" % (filter_exp, ret)
|
|
||||||
)
|
|
||||||
if conf.use_pypy and sys.pypy_version_info <= (7, 3, 0):
|
|
||||||
# PyPy < 7.3.0 has a broken behavior
|
|
||||||
# https://bitbucket.org/pypy/pypy/issues/3114
|
|
||||||
return struct.pack(
|
|
||||||
'HL',
|
|
||||||
bpf.bf_len, ctypes.addressof(bpf.bf_insns.contents)
|
|
||||||
)
|
|
||||||
return bpf
|
|
|
@ -1,642 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Linux specific functions.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
|
|
||||||
import array
|
|
||||||
from fcntl import ioctl
|
|
||||||
import os
|
|
||||||
from select import select
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import time
|
|
||||||
import re
|
|
||||||
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from scapy.compat import raw, plain_str
|
|
||||||
from scapy.consts import LINUX
|
|
||||||
import scapy.utils
|
|
||||||
import scapy.utils6
|
|
||||||
from scapy.packet import Packet, Padding
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.data import MTU, ETH_P_ALL, SOL_PACKET, SO_ATTACH_FILTER, \
|
|
||||||
SO_TIMESTAMPNS
|
|
||||||
from scapy.supersocket import SuperSocket
|
|
||||||
from scapy.error import warning, Scapy_Exception, \
|
|
||||||
ScapyInvalidPlatformException, log_runtime
|
|
||||||
from scapy.arch.common import get_if, compile_filter
|
|
||||||
import scapy.modules.six as six
|
|
||||||
from scapy.modules.six.moves import range
|
|
||||||
|
|
||||||
from scapy.arch.common import get_if_raw_hwaddr # noqa: F401
|
|
||||||
|
|
||||||
# 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 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_AUXDATA = 8
|
|
||||||
PACKET_FASTROUTE = 6 # Fastrouted frame
|
|
||||||
# Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space
|
|
||||||
|
|
||||||
# Utils
|
|
||||||
|
|
||||||
|
|
||||||
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:
|
|
||||||
try:
|
|
||||||
f.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
warning("Can't open /proc/net/dev !")
|
|
||||||
return []
|
|
||||||
lst = []
|
|
||||||
f.readline()
|
|
||||||
f.readline()
|
|
||||||
for line in f:
|
|
||||||
line = plain_str(line)
|
|
||||||
lst.append(line.split(":")[0].strip())
|
|
||||||
f.close()
|
|
||||||
return lst
|
|
||||||
|
|
||||||
|
|
||||||
def get_working_if():
|
|
||||||
"""
|
|
||||||
Return the name of the first network interfcace that is up.
|
|
||||||
"""
|
|
||||||
for i in get_if_list():
|
|
||||||
if i == conf.loopback_name:
|
|
||||||
continue
|
|
||||||
ifflags = struct.unpack("16xH14x", get_if(i, SIOCGIFFLAGS))[0]
|
|
||||||
if ifflags & IFF_UP:
|
|
||||||
return i
|
|
||||||
return conf.loopback_name
|
|
||||||
|
|
||||||
|
|
||||||
def attach_filter(sock, bpf_filter, iface):
|
|
||||||
"""
|
|
||||||
Compile bpf filter and attach it to a socket
|
|
||||||
|
|
||||||
:param sock: the python socket
|
|
||||||
:param bpf_filter: the bpf string filter to compile
|
|
||||||
:param iface: the interface used to compile
|
|
||||||
"""
|
|
||||||
bp = compile_filter(bpf_filter, iface)
|
|
||||||
sock.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.tobytes() if six.PY3 else names.tostring()
|
|
||||||
names = [names[i:i + offset].split(b'\0', 1)[0] for i in range(0, out, name_len)] # noqa: E501
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
try:
|
|
||||||
ifreq = ioctl(s, SIOCGIFADDR, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501
|
|
||||||
addrfamily = struct.unpack("h", ifreq[16:18])[0]
|
|
||||||
if addrfamily == socket.AF_INET:
|
|
||||||
ifreq2 = ioctl(s, SIOCGIFNETMASK, struct.pack("16s16x", conf.loopback_name.encode("utf8"))) # noqa: E501
|
|
||||||
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", conf.loopback_name, ifaddr, 1)) # noqa: E501
|
|
||||||
else:
|
|
||||||
warning("Interface %s: unknown address family (%i)" % (conf.loopback_name, addrfamily)) # noqa: E501
|
|
||||||
except IOError as err:
|
|
||||||
if err.errno == 99:
|
|
||||||
warning("Interface %s: no address assigned" % conf.loopback_name) # noqa: E501
|
|
||||||
else:
|
|
||||||
warning("Interface %s: failed to get address config (%s)" % (conf.loopback_name, str(err))) # noqa: E501
|
|
||||||
|
|
||||||
for line in f.readlines()[1:]:
|
|
||||||
line = plain_str(line)
|
|
||||||
iff, dst, gw, flags, _, _, metric, msk, _, _, _ = line.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"))) # noqa: E501
|
|
||||||
except IOError: # interface is present in routing tables but does not have any assigned IP # noqa: E501
|
|
||||||
ifaddr = "0.0.0.0"
|
|
||||||
ifaddr_int = 0
|
|
||||||
else:
|
|
||||||
addrfamily = struct.unpack("h", ifreq[16:18])[0]
|
|
||||||
if addrfamily == socket.AF_INET:
|
|
||||||
ifaddr = scapy.utils.inet_ntoa(ifreq[20:24])
|
|
||||||
ifaddr_int = struct.unpack("!I", ifreq[20:24])[0]
|
|
||||||
else:
|
|
||||||
warning("Interface %s: unknown address family (%i)", iff, addrfamily) # noqa: E501
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Attempt to detect an interface alias based on addresses inconsistencies # noqa: E501
|
|
||||||
dst_int = socket.htonl(int(dst, 16)) & 0xffffffff
|
|
||||||
msk_int = socket.htonl(int(msk, 16)) & 0xffffffff
|
|
||||||
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()
|
|
||||||
s.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
|
|
||||||
'iface'.
|
|
||||||
|
|
||||||
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:
|
|
||||||
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]))
|
|
||||||
fdesc.close()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def read_routes6():
|
|
||||||
try:
|
|
||||||
f = open("/proc/net/ipv6_route", "rb")
|
|
||||||
except IOError:
|
|
||||||
return []
|
|
||||||
# 1. destination network
|
|
||||||
# 2. destination prefix length
|
|
||||||
# 3. source network displayed
|
|
||||||
# 4. source prefix length
|
|
||||||
# 5. next hop
|
|
||||||
# 6. metric
|
|
||||||
# 7. reference counter (?!?)
|
|
||||||
# 8. use counter (?!?)
|
|
||||||
# 9. flags
|
|
||||||
# 10. device name
|
|
||||||
routes = []
|
|
||||||
|
|
||||||
def proc2r(p):
|
|
||||||
ret = struct.unpack('4s4s4s4s4s4s4s4s', p)
|
|
||||||
ret = b':'.join(ret).decode()
|
|
||||||
return scapy.utils6.in6_ptop(ret)
|
|
||||||
|
|
||||||
lifaddr = in6_getifaddr()
|
|
||||||
for line in f.readlines():
|
|
||||||
d, dp, _, _, nh, metric, rc, us, fl, dev = line.split()
|
|
||||||
metric = int(metric, 16)
|
|
||||||
fl = int(fl, 16)
|
|
||||||
dev = plain_str(dev)
|
|
||||||
|
|
||||||
if fl & RTF_UP == 0:
|
|
||||||
continue
|
|
||||||
if fl & RTF_REJECT:
|
|
||||||
continue
|
|
||||||
|
|
||||||
d = proc2r(d)
|
|
||||||
dp = int(dp, 16)
|
|
||||||
nh = proc2r(nh)
|
|
||||||
|
|
||||||
cset = [] # candidate set (possible source addresses)
|
|
||||||
if dev == conf.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
|
|
||||||
|
|
||||||
|
|
||||||
def get_iface_mode(iface):
|
|
||||||
"""Return the interface mode.
|
|
||||||
params:
|
|
||||||
- iface: the iwconfig interface
|
|
||||||
"""
|
|
||||||
p = subprocess.Popen(["iwconfig", iface], stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.STDOUT)
|
|
||||||
output, err = p.communicate()
|
|
||||||
match = re.search(br"mode:([a-zA-Z]*)", output.lower())
|
|
||||||
if match:
|
|
||||||
return plain_str(match.group(1))
|
|
||||||
return "unknown"
|
|
||||||
|
|
||||||
|
|
||||||
def set_iface_monitor(iface, monitor):
|
|
||||||
"""Sets the monitor mode (or remove it) from an interface.
|
|
||||||
params:
|
|
||||||
- iface: the iwconfig interface
|
|
||||||
- monitor: True if the interface should be set in monitor mode,
|
|
||||||
False if it should be in managed mode
|
|
||||||
"""
|
|
||||||
mode = get_iface_mode(iface)
|
|
||||||
if mode == "unknown":
|
|
||||||
warning("Could not parse iwconfig !")
|
|
||||||
current_monitor = mode == "monitor"
|
|
||||||
if monitor == current_monitor:
|
|
||||||
# Already correct
|
|
||||||
return True
|
|
||||||
s_mode = "monitor" if monitor else "managed"
|
|
||||||
|
|
||||||
def _check_call(commands):
|
|
||||||
p = subprocess.Popen(commands,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
stdout=subprocess.PIPE)
|
|
||||||
stdout, stderr = p.communicate()
|
|
||||||
if p.returncode != 0:
|
|
||||||
warning("%s failed !" % " ".join(commands))
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
if not _check_call(["ifconfig", iface, "down"]):
|
|
||||||
return False
|
|
||||||
if not _check_call(["iwconfig", iface, "mode", s_mode]):
|
|
||||||
return False
|
|
||||||
if not _check_call(["ifconfig", iface, "up"]):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
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, monitor=None):
|
|
||||||
self.iface = conf.iface if iface is None else iface
|
|
||||||
self.type = type
|
|
||||||
self.promisc = conf.sniff_promisc if promisc is None else promisc
|
|
||||||
if monitor is not None:
|
|
||||||
warning(
|
|
||||||
"The monitor argument is ineffective on native linux sockets."
|
|
||||||
" Use set_iface_monitor instead."
|
|
||||||
)
|
|
||||||
self.ins = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(type)) # noqa: E501
|
|
||||||
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:
|
|
||||||
try:
|
|
||||||
attach_filter(self.ins, filter, iface)
|
|
||||||
except ImportError as ex:
|
|
||||||
warning("Cannot set filter: %s" % ex)
|
|
||||||
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,
|
|
||||||
conf.bufsize
|
|
||||||
)
|
|
||||||
if not six.PY2:
|
|
||||||
# Receive Auxiliary Data (VLAN tags)
|
|
||||||
try:
|
|
||||||
self.ins.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1)
|
|
||||||
self.ins.setsockopt(
|
|
||||||
socket.SOL_SOCKET,
|
|
||||||
SO_TIMESTAMPNS,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
self.auxdata_available = True
|
|
||||||
except OSError:
|
|
||||||
# Note: Auxiliary Data is only supported since
|
|
||||||
# Linux 2.6.21
|
|
||||||
msg = "Your Linux Kernel does not support Auxiliary Data!"
|
|
||||||
log_runtime.info(msg)
|
|
||||||
if isinstance(self, L2ListenSocket):
|
|
||||||
self.outs = None
|
|
||||||
else:
|
|
||||||
self.outs = self.ins
|
|
||||||
self.outs.setsockopt(
|
|
||||||
socket.SOL_SOCKET,
|
|
||||||
socket.SO_SNDBUF,
|
|
||||||
conf.bufsize
|
|
||||||
)
|
|
||||||
sa_ll = self.ins.getsockname()
|
|
||||||
if sa_ll[3] in conf.l2types:
|
|
||||||
self.LL = conf.l2types[sa_ll[3]]
|
|
||||||
self.lvl = 2
|
|
||||||
elif sa_ll[1] in conf.l3types:
|
|
||||||
self.LL = conf.l3types[sa_ll[1]]
|
|
||||||
self.lvl = 3
|
|
||||||
else:
|
|
||||||
self.LL = conf.default_l2
|
|
||||||
self.lvl = 2
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.closed:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
if self.promisc and self.ins:
|
|
||||||
set_promisc(self.ins, self.iface, 0)
|
|
||||||
except (AttributeError, OSError):
|
|
||||||
pass
|
|
||||||
SuperSocket.close(self)
|
|
||||||
|
|
||||||
def recv_raw(self, x=MTU):
|
|
||||||
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
|
|
||||||
pkt, sa_ll, ts = self._recv_raw(self.ins, x)
|
|
||||||
if self.outs and sa_ll[2] == socket.PACKET_OUTGOING:
|
|
||||||
return None, None, None
|
|
||||||
if ts is None:
|
|
||||||
ts = get_last_packet_timestamp(self.ins)
|
|
||||||
return self.LL, pkt, ts
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
try:
|
|
||||||
return SuperSocket.send(self, x)
|
|
||||||
except socket.error as msg:
|
|
||||||
if msg.errno == 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(L2Socket):
|
|
||||||
desc = "read packets at layer 2 using Linux PF_PACKET sockets. Also receives the packets going OUT" # noqa: E501
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
raise Scapy_Exception("Can't send anything with L2ListenSocket")
|
|
||||||
|
|
||||||
|
|
||||||
class L3PacketSocket(L2Socket):
|
|
||||||
desc = "read/write packets at layer 3 using Linux PF_PACKET sockets"
|
|
||||||
|
|
||||||
def recv(self, x=MTU):
|
|
||||||
pkt = SuperSocket.recv(self, x)
|
|
||||||
if pkt and self.lvl == 2:
|
|
||||||
pkt.payload.time = pkt.time
|
|
||||||
return pkt.payload
|
|
||||||
return pkt
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
iff = x.route()[0]
|
|
||||||
if iff is None:
|
|
||||||
iff = conf.iface
|
|
||||||
sdto = (iff, self.type)
|
|
||||||
self.outs.bind(sdto)
|
|
||||||
sn = self.outs.getsockname()
|
|
||||||
ll = lambda x: x
|
|
||||||
type_x = type(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
|
|
||||||
if self.lvl == 3 and type_x != self.LL:
|
|
||||||
warning("Incompatible L3 types detected using %s instead of %s !",
|
|
||||||
type_x, self.LL)
|
|
||||||
self.LL = type_x
|
|
||||||
sx = raw(ll(x))
|
|
||||||
x.sent_time = time.time()
|
|
||||||
try:
|
|
||||||
self.outs.sendto(sx, sdto)
|
|
||||||
except socket.error as msg:
|
|
||||||
if msg.errno == 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.errno == 90:
|
|
||||||
for p in x.fragment():
|
|
||||||
self.outs.sendto(raw(ll(p)), sdto)
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
class VEthPair(object):
|
|
||||||
"""
|
|
||||||
encapsulates a virtual Ethernet interface pair
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, iface_name, peer_name):
|
|
||||||
|
|
||||||
if not LINUX:
|
|
||||||
# ToDo: do we need a kernel version check here?
|
|
||||||
raise ScapyInvalidPlatformException(
|
|
||||||
'Virtual Ethernet interface pair only available on Linux'
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ifaces = [iface_name, peer_name]
|
|
||||||
|
|
||||||
def iface(self):
|
|
||||||
return self.ifaces[0]
|
|
||||||
|
|
||||||
def peer(self):
|
|
||||||
return self.ifaces[1]
|
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
"""
|
|
||||||
create veth pair links
|
|
||||||
:raises subprocess.CalledProcessError if operation fails
|
|
||||||
"""
|
|
||||||
subprocess.check_call(['ip', 'link', 'add', self.ifaces[0], 'type', 'veth', 'peer', 'name', self.ifaces[1]]) # noqa: E501
|
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
"""
|
|
||||||
remove veth pair links
|
|
||||||
:raises subprocess.CalledProcessError if operation fails
|
|
||||||
"""
|
|
||||||
subprocess.check_call(['ip', 'link', 'del', self.ifaces[0]])
|
|
||||||
|
|
||||||
def up(self):
|
|
||||||
"""
|
|
||||||
set veth pair links up
|
|
||||||
:raises subprocess.CalledProcessError if operation fails
|
|
||||||
"""
|
|
||||||
for idx in [0, 1]:
|
|
||||||
subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "up"]) # noqa: E501
|
|
||||||
|
|
||||||
def down(self):
|
|
||||||
"""
|
|
||||||
set veth pair links down
|
|
||||||
:raises subprocess.CalledProcessError if operation fails
|
|
||||||
"""
|
|
||||||
for idx in [0, 1]:
|
|
||||||
subprocess.check_call(["ip", "link", "set", self.ifaces[idx], "down"]) # noqa: E501
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.setup()
|
|
||||||
self.up()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
self.destroy()
|
|
|
@ -1,394 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Packet sending and receiving libpcap/WinPcap.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
import socket
|
|
||||||
import struct
|
|
||||||
import time
|
|
||||||
|
|
||||||
from scapy.automaton import SelectableObject
|
|
||||||
from scapy.arch.common import _select_nonblock
|
|
||||||
from scapy.compat import raw, plain_str
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.consts import WINDOWS
|
|
||||||
from scapy.data import MTU, ETH_P_ALL
|
|
||||||
from scapy.pton_ntop import inet_ntop
|
|
||||||
from scapy.supersocket import SuperSocket
|
|
||||||
from scapy.error import Scapy_Exception, log_loading, warning
|
|
||||||
import scapy.consts
|
|
||||||
|
|
||||||
if not scapy.consts.WINDOWS:
|
|
||||||
from fcntl import ioctl
|
|
||||||
|
|
||||||
############
|
|
||||||
# COMMON #
|
|
||||||
############
|
|
||||||
|
|
||||||
# From BSD net/bpf.h
|
|
||||||
# BIOCIMMEDIATE = 0x80044270
|
|
||||||
BIOCIMMEDIATE = -2147204496
|
|
||||||
|
|
||||||
|
|
||||||
class _L2pcapdnetSocket(SuperSocket, SelectableObject):
|
|
||||||
nonblocking_socket = True
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
SelectableObject.__init__(self)
|
|
||||||
self.cls = None
|
|
||||||
|
|
||||||
def check_recv(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def recv_raw(self, x=MTU):
|
|
||||||
"""Receives a packet, then returns a tuple containing (cls, pkt_data, time)""" # noqa: E501
|
|
||||||
if self.cls is None:
|
|
||||||
ll = self.ins.datalink()
|
|
||||||
if ll in conf.l2types:
|
|
||||||
self.cls = conf.l2types[ll]
|
|
||||||
else:
|
|
||||||
self.cls = conf.default_l2
|
|
||||||
warning(
|
|
||||||
"Unable to guess datalink type "
|
|
||||||
"(interface=%s linktype=%i). Using %s",
|
|
||||||
self.iface, ll, self.cls.name
|
|
||||||
)
|
|
||||||
|
|
||||||
ts, pkt = self.ins.next()
|
|
||||||
if pkt is None:
|
|
||||||
return None, None, None
|
|
||||||
return self.cls, pkt, ts
|
|
||||||
|
|
||||||
def nonblock_recv(self):
|
|
||||||
"""Receives and dissect a packet in non-blocking mode.
|
|
||||||
Note: on Windows, this won't do anything."""
|
|
||||||
self.ins.setnonblock(1)
|
|
||||||
p = self.recv(MTU)
|
|
||||||
self.ins.setnonblock(0)
|
|
||||||
return p
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select(sockets, remain=None):
|
|
||||||
return _select_nonblock(sockets, remain=None)
|
|
||||||
|
|
||||||
##########
|
|
||||||
# PCAP #
|
|
||||||
##########
|
|
||||||
|
|
||||||
|
|
||||||
if conf.use_pcap:
|
|
||||||
if WINDOWS:
|
|
||||||
# Windows specific
|
|
||||||
NPCAP_PATH = os.environ["WINDIR"] + "\\System32\\Npcap"
|
|
||||||
from scapy.libs.winpcapy import pcap_setmintocopy
|
|
||||||
else:
|
|
||||||
from scapy.libs.winpcapy import pcap_get_selectable_fd
|
|
||||||
from ctypes import POINTER, byref, create_string_buffer, c_ubyte, cast
|
|
||||||
|
|
||||||
# Part of the Winpcapy integration was inspired by phaethon/scapy
|
|
||||||
# but he destroyed the commit history, so there is no link to that
|
|
||||||
try:
|
|
||||||
from scapy.libs.winpcapy import PCAP_ERRBUF_SIZE, pcap_if_t, \
|
|
||||||
sockaddr_in, sockaddr_in6, pcap_findalldevs, pcap_freealldevs, \
|
|
||||||
pcap_lib_version, pcap_close, \
|
|
||||||
pcap_open_live, pcap_pkthdr, \
|
|
||||||
pcap_next_ex, pcap_datalink, \
|
|
||||||
pcap_compile, pcap_setfilter, pcap_setnonblock, pcap_sendpacket, \
|
|
||||||
bpf_program
|
|
||||||
|
|
||||||
def load_winpcapy():
|
|
||||||
"""This functions calls libpcap ``pcap_findalldevs`` function,
|
|
||||||
and extracts and parse all the data scapy will need
|
|
||||||
to build the Interface List.
|
|
||||||
|
|
||||||
The date will be stored in ``conf.cache_iflist``, or accessible
|
|
||||||
with ``get_if_list()``
|
|
||||||
"""
|
|
||||||
err = create_string_buffer(PCAP_ERRBUF_SIZE)
|
|
||||||
devs = POINTER(pcap_if_t)()
|
|
||||||
if_list = {}
|
|
||||||
if pcap_findalldevs(byref(devs), err) < 0:
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
p = devs
|
|
||||||
# Iterate through the different interfaces
|
|
||||||
while p:
|
|
||||||
name = plain_str(p.contents.name) # GUID
|
|
||||||
description = plain_str(p.contents.description) # NAME
|
|
||||||
flags = p.contents.flags # FLAGS
|
|
||||||
ips = []
|
|
||||||
a = p.contents.addresses
|
|
||||||
while a:
|
|
||||||
# IPv4 address
|
|
||||||
family = a.contents.addr.contents.sa_family
|
|
||||||
ap = a.contents.addr
|
|
||||||
if family == socket.AF_INET:
|
|
||||||
val = cast(ap, POINTER(sockaddr_in))
|
|
||||||
val = val.contents.sin_addr[:]
|
|
||||||
elif family == socket.AF_INET6:
|
|
||||||
val = cast(ap, POINTER(sockaddr_in6))
|
|
||||||
val = val.contents.sin6_addr[:]
|
|
||||||
else:
|
|
||||||
# Unknown address family
|
|
||||||
# (AF_LINK isn't a thing on Windows)
|
|
||||||
a = a.contents.next
|
|
||||||
continue
|
|
||||||
addr = inet_ntop(family, bytes(bytearray(val)))
|
|
||||||
if addr != "0.0.0.0":
|
|
||||||
ips.append(addr)
|
|
||||||
a = a.contents.next
|
|
||||||
if_list[name] = (description, ips, flags)
|
|
||||||
p = p.contents.next
|
|
||||||
conf.cache_iflist = if_list
|
|
||||||
except Exception:
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
pcap_freealldevs(devs)
|
|
||||||
except OSError:
|
|
||||||
conf.use_pcap = False
|
|
||||||
if WINDOWS:
|
|
||||||
if conf.interactive:
|
|
||||||
log_loading.critical(
|
|
||||||
"Npcap/Winpcap is not installed ! See "
|
|
||||||
"https://scapy.readthedocs.io/en/latest/installation.html#windows" # noqa: E501
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if conf.interactive:
|
|
||||||
log_loading.critical(
|
|
||||||
"Libpcap is not installed!"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if WINDOWS:
|
|
||||||
# Detect Pcap version: check for Npcap
|
|
||||||
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)")
|
|
||||||
elif platform.release() != "XP":
|
|
||||||
warning("WinPcap is now deprecated (not maintained). "
|
|
||||||
"Please use Npcap instead")
|
|
||||||
elif b"npcap" in version.lower():
|
|
||||||
conf.use_npcap = True
|
|
||||||
conf.loopback_name = conf.loopback_name = "Npcap Loopback Adapter" # noqa: E501
|
|
||||||
|
|
||||||
if conf.use_pcap:
|
|
||||||
def get_if_list():
|
|
||||||
"""Returns all pcap names"""
|
|
||||||
if not conf.cache_iflist:
|
|
||||||
load_winpcapy()
|
|
||||||
return list(conf.cache_iflist)
|
|
||||||
|
|
||||||
class _PcapWrapper_libpcap: # noqa: F811
|
|
||||||
"""Wrapper for the libpcap calls"""
|
|
||||||
|
|
||||||
def __init__(self, device, snaplen, promisc, to_ms, monitor=None):
|
|
||||||
self.errbuf = create_string_buffer(PCAP_ERRBUF_SIZE)
|
|
||||||
self.iface = create_string_buffer(device.encode("utf8"))
|
|
||||||
self.dtl = None
|
|
||||||
if monitor:
|
|
||||||
if WINDOWS and not conf.use_npcap:
|
|
||||||
raise OSError("On Windows, this feature requires NPcap !")
|
|
||||||
# Npcap-only functions
|
|
||||||
from scapy.libs.winpcapy import pcap_create, \
|
|
||||||
pcap_set_snaplen, pcap_set_promisc, \
|
|
||||||
pcap_set_timeout, pcap_set_rfmon, pcap_activate
|
|
||||||
self.pcap = pcap_create(self.iface, self.errbuf)
|
|
||||||
pcap_set_snaplen(self.pcap, snaplen)
|
|
||||||
pcap_set_promisc(self.pcap, promisc)
|
|
||||||
pcap_set_timeout(self.pcap, to_ms)
|
|
||||||
if pcap_set_rfmon(self.pcap, 1) != 0:
|
|
||||||
warning("Could not set monitor mode")
|
|
||||||
if pcap_activate(self.pcap) != 0:
|
|
||||||
raise OSError("Could not activate the pcap handler")
|
|
||||||
else:
|
|
||||||
self.pcap = pcap_open_live(self.iface,
|
|
||||||
snaplen, promisc, to_ms,
|
|
||||||
self.errbuf)
|
|
||||||
error = bytes(bytearray(self.errbuf)).strip(b"\x00")
|
|
||||||
if error:
|
|
||||||
raise OSError(error)
|
|
||||||
|
|
||||||
if WINDOWS:
|
|
||||||
# Winpcap/Npcap exclusive: make every packet to be instantly
|
|
||||||
# returned, and not buffered within Winpcap/Npcap
|
|
||||||
pcap_setmintocopy(self.pcap, 0)
|
|
||||||
|
|
||||||
self.header = POINTER(pcap_pkthdr)()
|
|
||||||
self.pkt_data = POINTER(c_ubyte)()
|
|
||||||
self.bpf_program = bpf_program()
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
"""
|
|
||||||
Returns the next packet as the tuple
|
|
||||||
(timestamp, raw_packet)
|
|
||||||
"""
|
|
||||||
c = pcap_next_ex(
|
|
||||||
self.pcap,
|
|
||||||
byref(self.header),
|
|
||||||
byref(self.pkt_data)
|
|
||||||
)
|
|
||||||
if not c > 0:
|
|
||||||
return None, None
|
|
||||||
ts = self.header.contents.ts.tv_sec + float(self.header.contents.ts.tv_usec) / 1e6 # noqa: E501
|
|
||||||
pkt = bytes(bytearray(self.pkt_data[:self.header.contents.len]))
|
|
||||||
return ts, pkt
|
|
||||||
__next__ = next
|
|
||||||
|
|
||||||
def datalink(self):
|
|
||||||
"""Wrapper around pcap_datalink"""
|
|
||||||
if self.dtl is None:
|
|
||||||
self.dtl = pcap_datalink(self.pcap)
|
|
||||||
return self.dtl
|
|
||||||
|
|
||||||
def fileno(self):
|
|
||||||
if WINDOWS:
|
|
||||||
log_loading.error("Cannot get selectable PCAP fd on Windows")
|
|
||||||
return -1
|
|
||||||
else:
|
|
||||||
# This does not exist under Windows
|
|
||||||
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: # noqa: E501
|
|
||||||
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 = _PcapWrapper_libpcap
|
|
||||||
|
|
||||||
# pcap sockets
|
|
||||||
|
|
||||||
class L2pcapListenSocket(_L2pcapdnetSocket):
|
|
||||||
desc = "read packets at layer 2 using libpcap"
|
|
||||||
|
|
||||||
def __init__(self, iface=None, type=ETH_P_ALL, promisc=None, filter=None, monitor=None): # noqa: E501
|
|
||||||
super(L2pcapListenSocket, self).__init__()
|
|
||||||
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
|
|
||||||
# Note: Timeout with Winpcap/Npcap
|
|
||||||
# The 4th argument of open_pcap corresponds to timeout. In an ideal world, we would # noqa: E501
|
|
||||||
# set it to 0 ==> blocking pcap_next_ex.
|
|
||||||
# However, the way it is handled is very poor, and result in a jerky packet stream. # noqa: E501
|
|
||||||
# To fix this, we set 100 and the implementation under windows is slightly different, as # noqa: E501
|
|
||||||
# everything is always received as non-blocking
|
|
||||||
self.ins = open_pcap(iface, MTU, self.promisc, 100,
|
|
||||||
monitor=monitor)
|
|
||||||
try:
|
|
||||||
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if type == ETH_P_ALL: # Do not apply any filter if Ethernet type is given # noqa: E501
|
|
||||||
if conf.except_filter:
|
|
||||||
if filter:
|
|
||||||
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
|
|
||||||
else:
|
|
||||||
filter = "not (%s)" % conf.except_filter
|
|
||||||
if filter:
|
|
||||||
self.ins.setfilter(filter)
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
raise Scapy_Exception("Can't send anything with L2pcapListenSocket") # noqa: E501
|
|
||||||
|
|
||||||
class L2pcapSocket(_L2pcapdnetSocket):
|
|
||||||
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, # noqa: E501
|
|
||||||
monitor=None):
|
|
||||||
super(L2pcapSocket, self).__init__()
|
|
||||||
if iface is None:
|
|
||||||
iface = conf.iface
|
|
||||||
self.iface = iface
|
|
||||||
if promisc is None:
|
|
||||||
promisc = 0
|
|
||||||
self.promisc = promisc
|
|
||||||
# See L2pcapListenSocket for infos about this line
|
|
||||||
self.ins = open_pcap(iface, MTU, self.promisc, 100,
|
|
||||||
monitor=monitor)
|
|
||||||
self.outs = self.ins
|
|
||||||
try:
|
|
||||||
ioctl(self.ins.fileno(), BIOCIMMEDIATE, struct.pack("I", 1))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
if nofilter:
|
|
||||||
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
|
|
||||||
filter = "ether proto %i" % type
|
|
||||||
else:
|
|
||||||
filter = None
|
|
||||||
else:
|
|
||||||
if conf.except_filter:
|
|
||||||
if filter:
|
|
||||||
filter = "(%s) and not (%s)" % (filter, conf.except_filter) # noqa: E501
|
|
||||||
else:
|
|
||||||
filter = "not (%s)" % conf.except_filter
|
|
||||||
if type != ETH_P_ALL: # PF_PACKET stuff. Need to emulate this for pcap # noqa: E501
|
|
||||||
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)
|
|
||||||
try:
|
|
||||||
x.sent_time = time.time()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
self.outs.send(sx)
|
|
||||||
|
|
||||||
class L3pcapSocket(L2pcapSocket):
|
|
||||||
desc = "read/write packets at layer 3 using only libpcap"
|
|
||||||
|
|
||||||
def recv(self, x=MTU):
|
|
||||||
r = L2pcapSocket.recv(self, x)
|
|
||||||
if r:
|
|
||||||
r.payload.time = r.time
|
|
||||||
return r.payload
|
|
||||||
return r
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
# Makes send detects when it should add Loopback(), Dot11... instead of Ether() # noqa: E501
|
|
||||||
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) # noqa: E501
|
|
||||||
sx = raw(cls() / x)
|
|
||||||
try:
|
|
||||||
x.sent_time = time.time()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
self.outs.send(sx)
|
|
||||||
else:
|
|
||||||
# No libpcap installed
|
|
||||||
get_if_list = lambda: []
|
|
||||||
if WINDOWS:
|
|
||||||
NPCAP_PATH = ""
|
|
|
@ -1,35 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Customization for the Solaris operation system.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import socket
|
|
||||||
|
|
||||||
from scapy.config import conf
|
|
||||||
conf.use_pcap = True
|
|
||||||
|
|
||||||
# IPPROTO_GRE is missing on Solaris
|
|
||||||
socket.IPPROTO_GRE = 47
|
|
||||||
|
|
||||||
# From sys/sockio.h and net/if.h
|
|
||||||
SIOCGIFHWADDR = 0xc02069b9 # Get hardware address
|
|
||||||
|
|
||||||
from scapy.arch.pcapdnet import * # noqa: F401, F403
|
|
||||||
from scapy.arch.unix import * # noqa: F401, F403
|
|
||||||
from scapy.arch.common import get_if_raw_hwaddr # noqa: F401, F403
|
|
||||||
|
|
||||||
|
|
||||||
def get_working_if():
|
|
||||||
"""Return an interface that works"""
|
|
||||||
try:
|
|
||||||
# return the interface associated with the route with smallest
|
|
||||||
# mask (route by default if it exists)
|
|
||||||
iface = min(conf.route.routes, key=lambda x: x[1])[3]
|
|
||||||
except ValueError:
|
|
||||||
# no route
|
|
||||||
iface = conf.loopback_name
|
|
||||||
return iface
|
|
|
@ -1,355 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Common customizations for all Unix-like operating systems other than Linux
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
|
|
||||||
import scapy.config
|
|
||||||
import scapy.utils
|
|
||||||
from scapy.arch import get_if_addr
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.consts import FREEBSD, NETBSD, OPENBSD, SOLARIS
|
|
||||||
from scapy.error import warning, log_interactive
|
|
||||||
from scapy.pton_ntop import inet_pton
|
|
||||||
from scapy.utils6 import in6_getscope, construct_source_candidate_set
|
|
||||||
from scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr
|
|
||||||
|
|
||||||
|
|
||||||
##################
|
|
||||||
# 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():
|
|
||||||
"""Return a list of IPv4 routes than can be used by Scapy.
|
|
||||||
|
|
||||||
This function parses netstat.
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
refs_present = False
|
|
||||||
use_present = False
|
|
||||||
routes = []
|
|
||||||
pending_if = []
|
|
||||||
for line in f.readlines():
|
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
line = line.strip().lower()
|
|
||||||
if line.find("----") >= 0: # a separation line
|
|
||||||
continue
|
|
||||||
if not ok:
|
|
||||||
if line.find("destination") >= 0:
|
|
||||||
ok = 1
|
|
||||||
mtu_present = "mtu" in line
|
|
||||||
prio_present = "prio" in line
|
|
||||||
refs_present = "ref" in line # There is no s on Solaris
|
|
||||||
use_present = "use" in line
|
|
||||||
continue
|
|
||||||
if not line:
|
|
||||||
break
|
|
||||||
rt = line.split()
|
|
||||||
if SOLARIS:
|
|
||||||
dest, netmask, gw, netif = rt[:4]
|
|
||||||
flg = rt[4 + mtu_present + refs_present]
|
|
||||||
else:
|
|
||||||
dest, gw, flg = rt[:3]
|
|
||||||
locked = OPENBSD and rt[6] == "l"
|
|
||||||
offset = mtu_present + prio_present + refs_present + locked
|
|
||||||
offset += use_present
|
|
||||||
netif = rt[3 + offset]
|
|
||||||
if flg.find("lc") >= 0:
|
|
||||||
continue
|
|
||||||
elif dest == "default":
|
|
||||||
dest = 0
|
|
||||||
netmask = 0
|
|
||||||
elif SOLARIS:
|
|
||||||
dest = scapy.utils.atol(dest)
|
|
||||||
netmask = scapy.utils.atol(netmask)
|
|
||||||
else:
|
|
||||||
if "/" 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 "g" not 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)) # noqa: E501
|
|
||||||
else:
|
|
||||||
warning("Could not guess partial interface name: %s", netif) # noqa: E501
|
|
||||||
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:
|
|
||||||
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 # noqa: E501
|
|
||||||
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:
|
|
||||||
inet_pton(socket.AF_INET6, addr)
|
|
||||||
except (socket.error, ValueError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Get the scope and keep the address
|
|
||||||
scope = in6_getscope(addr)
|
|
||||||
ret.append((addr, scope, ifname))
|
|
||||||
|
|
||||||
f.close()
|
|
||||||
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 or SOLARIS:
|
|
||||||
if SOLARIS:
|
|
||||||
cmd = "%s -a6"
|
|
||||||
else:
|
|
||||||
cmd = "%s"
|
|
||||||
try:
|
|
||||||
f = os.popen(cmd % conf.prog.ifconfig)
|
|
||||||
except OSError:
|
|
||||||
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:
|
|
||||||
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)
|
|
||||||
f.close()
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
def read_routes6():
|
|
||||||
"""Return a list of IPv6 routes than can be used by Scapy.
|
|
||||||
|
|
||||||
This function parses netstat.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Call netstat to retrieve IPv6 routes
|
|
||||||
fd_netstat = os.popen("netstat -rn -f inet6")
|
|
||||||
|
|
||||||
# List interfaces IPv6 addresses
|
|
||||||
lifaddr = in6_getifaddr()
|
|
||||||
if not lifaddr:
|
|
||||||
fd_netstat.close()
|
|
||||||
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 "U" not 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 Exception:
|
|
||||||
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 conf.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) # noqa: E501
|
|
||||||
|
|
||||||
if len(cset):
|
|
||||||
routes.append((destination, destination_plen, next_hop, dev, cset, metric)) # noqa: E501
|
|
||||||
|
|
||||||
fd_netstat.close()
|
|
||||||
return routes
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,219 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Philippe Biondi <phil@secdev.org>
|
|
||||||
# Copyright (C) Gabriel Potter <gabriel@potter.fr>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
"""
|
|
||||||
Native Microsoft Windows sockets (L3 only)
|
|
||||||
|
|
||||||
## Notice: ICMP packets
|
|
||||||
|
|
||||||
DISCLAIMER: Please use Npcap/Winpcap to send/receive ICMP. It is going to work.
|
|
||||||
Below is some additional information, mainly implemented in a testing purpose.
|
|
||||||
|
|
||||||
When in native mode, everything goes through the Windows kernel.
|
|
||||||
This firstly requires that the Firewall is open. Be sure it allows ICMPv4/6
|
|
||||||
packets in and out.
|
|
||||||
Windows may drop packets that it finds wrong. for instance, answers to
|
|
||||||
ICMP packets with id=0 or seq=0 may be dropped. It means that sent packets
|
|
||||||
should (most of the time) be perfectly built.
|
|
||||||
|
|
||||||
A perfectly built ICMP req packet on Windows means that its id is 1, its
|
|
||||||
checksum (IP and ICMP) are correctly built, but also that its seq number is
|
|
||||||
in the "allowed range".
|
|
||||||
In fact, every time an ICMP packet is sent on Windows, a global sequence
|
|
||||||
number is increased, which is only reset at boot time. The seq number of the
|
|
||||||
received ICMP packet must be in the range [current, current + 3] to be valid,
|
|
||||||
and received by the socket. The current number is quite hard to get, thus we
|
|
||||||
provide in this module the get_actual_icmp_seq() function.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
>>> conf.use_pcap = False
|
|
||||||
>>> a = conf.L3socket()
|
|
||||||
# This will (most likely) work:
|
|
||||||
>>> current = get_current_icmp_seq()
|
|
||||||
>>> a.sr(IP(dst="www.google.com", ttl=128)/ICMP(id=1, seq=current))
|
|
||||||
# This won't:
|
|
||||||
>>> a.sr(IP(dst="www.google.com", ttl=128)/ICMP())
|
|
||||||
|
|
||||||
PS: on computers where the firewall isn't open, Windows temporarily opens it
|
|
||||||
when using the `ping` util from cmd.exe. One can first call a ping on cmd,
|
|
||||||
then do custom calls through the socket using get_current_icmp_seq(). See
|
|
||||||
the tests (windows.uts) for an example.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
|
|
||||||
from scapy.automaton import SelectableObject
|
|
||||||
from scapy.arch.common import _select_nonblock
|
|
||||||
from scapy.arch.windows.structures import GetIcmpStatistics
|
|
||||||
from scapy.compat import raw
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.data import MTU
|
|
||||||
from scapy.error import Scapy_Exception, warning
|
|
||||||
from scapy.supersocket import SuperSocket
|
|
||||||
|
|
||||||
# Watch out for import loops (inet...)
|
|
||||||
|
|
||||||
|
|
||||||
class L3WinSocket(SuperSocket, SelectableObject):
|
|
||||||
desc = "a native Layer 3 (IPv4) raw socket under Windows"
|
|
||||||
nonblocking_socket = True
|
|
||||||
__slots__ = ["promisc", "cls", "ipv6", "proto"]
|
|
||||||
|
|
||||||
def __init__(self, iface=None, proto=socket.IPPROTO_IP,
|
|
||||||
ttl=128, ipv6=False, promisc=True, **kwargs):
|
|
||||||
from scapy.layers.inet import IP
|
|
||||||
from scapy.layers.inet6 import IPv6
|
|
||||||
for kwarg in kwargs:
|
|
||||||
warning("Dropping unsupported option: %s" % kwarg)
|
|
||||||
af = socket.AF_INET6 if ipv6 else socket.AF_INET
|
|
||||||
self.proto = proto
|
|
||||||
if ipv6:
|
|
||||||
from scapy.arch import get_if_addr6
|
|
||||||
self.host_ip6 = get_if_addr6(conf.iface) or "::1"
|
|
||||||
if proto == socket.IPPROTO_IP:
|
|
||||||
# We'll restrict ourselves to UDP, as TCP isn't bindable
|
|
||||||
# on AF_INET6
|
|
||||||
self.proto = socket.IPPROTO_UDP
|
|
||||||
# On Windows, with promisc=False, you won't get much
|
|
||||||
self.ipv6 = ipv6
|
|
||||||
self.cls = IPv6 if ipv6 else IP
|
|
||||||
self.promisc = promisc
|
|
||||||
# Notes:
|
|
||||||
# - IPPROTO_RAW only works to send packets.
|
|
||||||
# - IPPROTO_IPV6 exists in MSDN docs, but using it will result in
|
|
||||||
# no packets being received. Same for its options (IPV6_HDRINCL...)
|
|
||||||
# However, using IPPROTO_IP with AF_INET6 will still receive
|
|
||||||
# the IPv6 packets
|
|
||||||
try:
|
|
||||||
self.ins = socket.socket(af,
|
|
||||||
socket.SOCK_RAW,
|
|
||||||
self.proto)
|
|
||||||
self.outs = socket.socket(af,
|
|
||||||
socket.SOCK_RAW,
|
|
||||||
socket.IPPROTO_RAW)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == 10013:
|
|
||||||
raise OSError("Windows native L3 Raw sockets are only "
|
|
||||||
"usable as administrator ! "
|
|
||||||
"Install Winpcap/Npcap to workaround !")
|
|
||||||
raise
|
|
||||||
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
self.ins.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 2**30)
|
|
||||||
self.outs.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 2**30)
|
|
||||||
# IOCTL Include IP headers
|
|
||||||
self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
|
|
||||||
self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
|
|
||||||
# set TTL
|
|
||||||
self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl)
|
|
||||||
self.outs.setsockopt(socket.IPPROTO_IP, socket.IP_TTL, ttl)
|
|
||||||
# Bind on all ports
|
|
||||||
iface = iface or conf.iface
|
|
||||||
host = iface.ip if iface.ip else socket.gethostname()
|
|
||||||
self.ins.bind((host, 0))
|
|
||||||
self.ins.setblocking(False)
|
|
||||||
# Get as much data as possible: reduce what is cropped
|
|
||||||
if ipv6:
|
|
||||||
try: # Not all Windows versions
|
|
||||||
self.ins.setsockopt(socket.IPPROTO_IPV6,
|
|
||||||
socket.IPV6_RECVTCLASS, 1)
|
|
||||||
self.ins.setsockopt(socket.IPPROTO_IPV6,
|
|
||||||
socket.IPV6_HOPLIMIT, 1)
|
|
||||||
except (OSError, socket.error):
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
try: # Not Windows XP
|
|
||||||
self.ins.setsockopt(socket.IPPROTO_IP,
|
|
||||||
socket.IP_RECVDSTADDR, 1)
|
|
||||||
except (OSError, socket.error):
|
|
||||||
pass
|
|
||||||
try: # Windows 10+ recent builds only
|
|
||||||
self.ins.setsockopt(socket.IPPROTO_IP, socket.IP_RECVTTL, 1)
|
|
||||||
except (OSError, socket.error):
|
|
||||||
pass
|
|
||||||
if promisc:
|
|
||||||
# IOCTL Receive all packets
|
|
||||||
self.ins.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
data = raw(x)
|
|
||||||
if self.cls not in x:
|
|
||||||
raise Scapy_Exception("L3WinSocket can only send IP/IPv6 packets !"
|
|
||||||
" Install Npcap/Winpcap to send more")
|
|
||||||
dst_ip = str(x[self.cls].dst)
|
|
||||||
self.outs.sendto(data, (dst_ip, 0))
|
|
||||||
|
|
||||||
def nonblock_recv(self, x=MTU):
|
|
||||||
return self.recv()
|
|
||||||
|
|
||||||
# https://docs.microsoft.com/en-us/windows/desktop/winsock/tcp-ip-raw-sockets-2 # noqa: E501
|
|
||||||
# - For IPv4 (address family of AF_INET), an application receives the IP
|
|
||||||
# header at the front of each received datagram regardless of the
|
|
||||||
# IP_HDRINCL socket option.
|
|
||||||
# - For IPv6 (address family of AF_INET6), an application receives
|
|
||||||
# everything after the last IPv6 header in each received datagram
|
|
||||||
# regardless of the IPV6_HDRINCL socket option. The application does
|
|
||||||
# not receive any IPv6 headers using a raw socket.
|
|
||||||
|
|
||||||
def recv_raw(self, x=MTU):
|
|
||||||
try:
|
|
||||||
data, address = self.ins.recvfrom(x)
|
|
||||||
except io.BlockingIOError:
|
|
||||||
return None, None, None
|
|
||||||
from scapy.layers.inet import IP
|
|
||||||
from scapy.layers.inet6 import IPv6
|
|
||||||
if self.ipv6:
|
|
||||||
# AF_INET6 does not return the IPv6 header. Let's build it
|
|
||||||
# (host, port, flowinfo, scopeid)
|
|
||||||
host, _, flowinfo, _ = address
|
|
||||||
header = raw(IPv6(src=host,
|
|
||||||
dst=self.host_ip6,
|
|
||||||
fl=flowinfo,
|
|
||||||
nh=self.proto, # fixed for AF_INET6
|
|
||||||
plen=len(data)))
|
|
||||||
return IPv6, header + data, time.time()
|
|
||||||
else:
|
|
||||||
return IP, data, time.time()
|
|
||||||
|
|
||||||
def check_recv(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if not self.closed and self.promisc:
|
|
||||||
self.ins.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
|
|
||||||
super(L3WinSocket, self).close()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select(sockets, remain=None):
|
|
||||||
return _select_nonblock(sockets, remain=remain)
|
|
||||||
|
|
||||||
|
|
||||||
class L3WinSocket6(L3WinSocket):
|
|
||||||
desc = "a native Layer 3 (IPv6) raw socket under Windows"
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(L3WinSocket6, self).__init__(ipv6=True, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def open_icmp_firewall(host):
|
|
||||||
"""Temporarily open the ICMP firewall. Tricks Windows into allowing
|
|
||||||
ICMP packets for a short period of time (~ 1 minute)"""
|
|
||||||
# We call ping with a timeout of 1ms: will return instantly
|
|
||||||
with open(os.devnull, 'wb') as DEVNULL:
|
|
||||||
return subprocess.Popen("ping -4 -w 1 -n 1 %s" % host,
|
|
||||||
shell=True,
|
|
||||||
stdout=DEVNULL,
|
|
||||||
stderr=DEVNULL).wait()
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_icmp_seq():
|
|
||||||
"""See help(scapy.arch.windows.native) for more information.
|
|
||||||
Returns the current ICMP seq number."""
|
|
||||||
return GetIcmpStatistics()['stats']['icmpOutStats']['dwEchos']
|
|
|
@ -1,585 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Philippe Biondi <phil@secdev.org>
|
|
||||||
# Copyright (C) Gabriel Potter <gabriel@potter.fr>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# flake8: noqa E266
|
|
||||||
# (We keep comment boxes, it's then one-line comments)
|
|
||||||
|
|
||||||
"""
|
|
||||||
C API calls to Windows DLLs
|
|
||||||
"""
|
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import ctypes.wintypes
|
|
||||||
from ctypes import Structure, POINTER, byref, create_string_buffer, WINFUNCTYPE
|
|
||||||
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.consts import WINDOWS_XP
|
|
||||||
|
|
||||||
ANY_SIZE = 65500 # FIXME quite inefficient :/
|
|
||||||
AF_UNSPEC = 0
|
|
||||||
NO_ERROR = 0x0
|
|
||||||
|
|
||||||
CHAR = ctypes.c_char
|
|
||||||
DWORD = ctypes.wintypes.DWORD
|
|
||||||
BOOL = ctypes.wintypes.BOOL
|
|
||||||
BOOLEAN = ctypes.wintypes.BOOLEAN
|
|
||||||
ULONG = ctypes.wintypes.ULONG
|
|
||||||
ULONGLONG = ctypes.c_ulonglong
|
|
||||||
HANDLE = ctypes.wintypes.HANDLE
|
|
||||||
LPWSTR = ctypes.wintypes.LPWSTR
|
|
||||||
VOID = ctypes.c_void_p
|
|
||||||
INT = ctypes.c_int
|
|
||||||
UINT = ctypes.wintypes.UINT
|
|
||||||
UINT8 = ctypes.c_uint8
|
|
||||||
UINT16 = ctypes.c_uint16
|
|
||||||
UINT32 = ctypes.c_uint32
|
|
||||||
UINT64 = ctypes.c_uint64
|
|
||||||
BYTE = ctypes.c_byte
|
|
||||||
UCHAR = UBYTE = ctypes.c_ubyte
|
|
||||||
SHORT = ctypes.c_short
|
|
||||||
USHORT = ctypes.c_ushort
|
|
||||||
|
|
||||||
|
|
||||||
# UTILS
|
|
||||||
|
|
||||||
|
|
||||||
def _resolve_list(list_obj):
|
|
||||||
current = list_obj
|
|
||||||
_list = []
|
|
||||||
while current and hasattr(current, "contents"):
|
|
||||||
_list.append(_struct_to_dict(current.contents))
|
|
||||||
current = current.contents.next
|
|
||||||
return _list
|
|
||||||
|
|
||||||
|
|
||||||
def _struct_to_dict(struct_obj):
|
|
||||||
results = {}
|
|
||||||
for fname, ctype in struct_obj.__class__._fields_:
|
|
||||||
val = getattr(struct_obj, fname)
|
|
||||||
if fname == "next":
|
|
||||||
# Already covered by the trick below
|
|
||||||
continue
|
|
||||||
if issubclass(ctype, (Structure, ctypes.Union)):
|
|
||||||
results[fname] = _struct_to_dict(val)
|
|
||||||
elif val and hasattr(val, "contents"):
|
|
||||||
# Let's resolve recursive pointers
|
|
||||||
if hasattr(val.contents, "next"):
|
|
||||||
results[fname] = _resolve_list(val)
|
|
||||||
else:
|
|
||||||
results[fname] = val
|
|
||||||
else:
|
|
||||||
results[fname] = val
|
|
||||||
return results
|
|
||||||
|
|
||||||
##############################
|
|
||||||
####### WinAPI handles #######
|
|
||||||
##############################
|
|
||||||
|
|
||||||
_winapi_SetConsoleTitle = ctypes.windll.kernel32.SetConsoleTitleW
|
|
||||||
_winapi_SetConsoleTitle.restype = BOOL
|
|
||||||
_winapi_SetConsoleTitle.argtypes = [LPWSTR]
|
|
||||||
|
|
||||||
def _windows_title(title=None):
|
|
||||||
"""Updates the terminal title with the default one or with `title`
|
|
||||||
if provided."""
|
|
||||||
if conf.interactive:
|
|
||||||
_winapi_SetConsoleTitle(title or "Scapy v{}".format(conf.version))
|
|
||||||
|
|
||||||
|
|
||||||
SC_HANDLE = HANDLE
|
|
||||||
|
|
||||||
class SERVICE_STATUS(Structure):
|
|
||||||
"""https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/ns-winsvc-_service_status""" # noqa: E501
|
|
||||||
_fields_ = [("dwServiceType", DWORD),
|
|
||||||
("dwCurrentState", DWORD),
|
|
||||||
("dwControlsAccepted", DWORD),
|
|
||||||
("dwWin32ExitCode", DWORD),
|
|
||||||
("dwServiceSpecificExitCode", DWORD),
|
|
||||||
("dwCheckPoint", DWORD),
|
|
||||||
("dwWaitHint", DWORD)]
|
|
||||||
|
|
||||||
|
|
||||||
OpenServiceW = ctypes.windll.Advapi32.OpenServiceW
|
|
||||||
OpenServiceW.restype = SC_HANDLE
|
|
||||||
OpenServiceW.argtypes = [SC_HANDLE, LPWSTR, DWORD]
|
|
||||||
|
|
||||||
CloseServiceHandle = ctypes.windll.Advapi32.CloseServiceHandle
|
|
||||||
CloseServiceHandle.restype = BOOL
|
|
||||||
CloseServiceHandle.argtypes = [SC_HANDLE]
|
|
||||||
|
|
||||||
OpenSCManagerW = ctypes.windll.Advapi32.OpenSCManagerW
|
|
||||||
OpenSCManagerW.restype = SC_HANDLE
|
|
||||||
OpenSCManagerW.argtypes = [LPWSTR, LPWSTR, DWORD]
|
|
||||||
|
|
||||||
QueryServiceStatus = ctypes.windll.Advapi32.QueryServiceStatus
|
|
||||||
QueryServiceStatus.restype = BOOL
|
|
||||||
QueryServiceStatus.argtypes = [SC_HANDLE, POINTER(SERVICE_STATUS)]
|
|
||||||
|
|
||||||
def get_service_status(service):
|
|
||||||
"""Returns content of QueryServiceStatus for a service"""
|
|
||||||
SERVICE_QUERY_STATUS = 0x0004
|
|
||||||
schSCManager = OpenSCManagerW(
|
|
||||||
None, # Local machine
|
|
||||||
None, # SERVICES_ACTIVE_DATABASE
|
|
||||||
SERVICE_QUERY_STATUS
|
|
||||||
)
|
|
||||||
service = OpenServiceW(
|
|
||||||
schSCManager,
|
|
||||||
service,
|
|
||||||
SERVICE_QUERY_STATUS
|
|
||||||
)
|
|
||||||
status = SERVICE_STATUS()
|
|
||||||
QueryServiceStatus(
|
|
||||||
service,
|
|
||||||
status
|
|
||||||
)
|
|
||||||
result = _struct_to_dict(status)
|
|
||||||
CloseServiceHandle(service)
|
|
||||||
CloseServiceHandle(schSCManager)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
##############################
|
|
||||||
###### Define IPHLPAPI ######
|
|
||||||
##############################
|
|
||||||
|
|
||||||
|
|
||||||
iphlpapi = ctypes.windll.iphlpapi
|
|
||||||
|
|
||||||
##############################
|
|
||||||
########### Common ###########
|
|
||||||
##############################
|
|
||||||
|
|
||||||
|
|
||||||
class in_addr(Structure):
|
|
||||||
_fields_ = [("byte", UBYTE * 4)]
|
|
||||||
|
|
||||||
|
|
||||||
class in6_addr(Structure):
|
|
||||||
_fields_ = [("byte", UBYTE * 16)]
|
|
||||||
|
|
||||||
|
|
||||||
class sockaddr_in(Structure):
|
|
||||||
_fields_ = [("sin_family", SHORT),
|
|
||||||
("sin_port", USHORT),
|
|
||||||
("sin_addr", in_addr),
|
|
||||||
("sin_zero", 8 * CHAR)]
|
|
||||||
|
|
||||||
|
|
||||||
class sockaddr_in6(Structure):
|
|
||||||
_fields_ = [("sin6_family", SHORT),
|
|
||||||
("sin6_port", USHORT),
|
|
||||||
("sin6_flowinfo", ULONG),
|
|
||||||
("sin6_addr", in6_addr),
|
|
||||||
("sin6_scope_id", ULONG)]
|
|
||||||
|
|
||||||
|
|
||||||
class SOCKADDR_INET(ctypes.Union):
|
|
||||||
_fields_ = [("Ipv4", sockaddr_in),
|
|
||||||
("Ipv6", sockaddr_in6),
|
|
||||||
("si_family", USHORT)]
|
|
||||||
|
|
||||||
##############################
|
|
||||||
######### ICMP stats #########
|
|
||||||
##############################
|
|
||||||
|
|
||||||
|
|
||||||
class MIBICMPSTATS(Structure):
|
|
||||||
_fields_ = [("dwMsgs", DWORD),
|
|
||||||
("dwErrors", DWORD),
|
|
||||||
("dwDestUnreachs", DWORD),
|
|
||||||
("dwTimeExcds", DWORD),
|
|
||||||
("dwParmProbs", DWORD),
|
|
||||||
("dwSrcQuenchs", DWORD),
|
|
||||||
("dwRedirects", DWORD),
|
|
||||||
("dwEchos", DWORD),
|
|
||||||
("dwEchoReps", DWORD),
|
|
||||||
("dwTimestamps", DWORD),
|
|
||||||
("dwTimestampReps", DWORD),
|
|
||||||
("dwAddrMasks", DWORD),
|
|
||||||
("dwAddrMaskReps", DWORD)]
|
|
||||||
|
|
||||||
|
|
||||||
class MIBICMPINFO(Structure):
|
|
||||||
_fields_ = [("icmpInStats", MIBICMPSTATS),
|
|
||||||
("icmpOutStats", MIBICMPSTATS)]
|
|
||||||
|
|
||||||
|
|
||||||
class MIB_ICMP(Structure):
|
|
||||||
_fields_ = [("stats", MIBICMPINFO)]
|
|
||||||
|
|
||||||
|
|
||||||
PMIB_ICMP = POINTER(MIB_ICMP)
|
|
||||||
|
|
||||||
# Func
|
|
||||||
|
|
||||||
_GetIcmpStatistics = WINFUNCTYPE(ULONG, PMIB_ICMP)(
|
|
||||||
('GetIcmpStatistics', iphlpapi))
|
|
||||||
|
|
||||||
|
|
||||||
def GetIcmpStatistics():
|
|
||||||
"""Return all Windows ICMP stats from iphlpapi"""
|
|
||||||
statistics = MIB_ICMP()
|
|
||||||
_GetIcmpStatistics(byref(statistics))
|
|
||||||
results = _struct_to_dict(statistics)
|
|
||||||
del(statistics)
|
|
||||||
return results
|
|
||||||
|
|
||||||
##############################
|
|
||||||
##### Adapters Addresses #####
|
|
||||||
##############################
|
|
||||||
|
|
||||||
|
|
||||||
# Our GetAdaptersAddresses implementation is inspired by
|
|
||||||
# @sphaero 's gist: https://gist.github.com/sphaero/f9da6ebb9a7a6f679157
|
|
||||||
# published under a MPL 2.0 License (GPLv2 compatible)
|
|
||||||
|
|
||||||
# from iptypes.h
|
|
||||||
MAX_ADAPTER_ADDRESS_LENGTH = 8
|
|
||||||
MAX_DHCPV6_DUID_LENGTH = 130
|
|
||||||
|
|
||||||
GAA_FLAG_INCLUDE_PREFIX = ULONG(0x0010)
|
|
||||||
# for now, just use void * for pointers to unused structures
|
|
||||||
PIP_ADAPTER_WINS_SERVER_ADDRESS_LH = VOID
|
|
||||||
PIP_ADAPTER_GATEWAY_ADDRESS_LH = VOID
|
|
||||||
PIP_ADAPTER_DNS_SUFFIX = VOID
|
|
||||||
|
|
||||||
IF_OPER_STATUS = UINT
|
|
||||||
IF_LUID = UINT64
|
|
||||||
|
|
||||||
NET_IF_COMPARTMENT_ID = UINT32
|
|
||||||
GUID = BYTE * 16
|
|
||||||
NET_IF_NETWORK_GUID = GUID
|
|
||||||
NET_IF_CONNECTION_TYPE = UINT # enum
|
|
||||||
TUNNEL_TYPE = UINT # enum
|
|
||||||
|
|
||||||
|
|
||||||
class SOCKET_ADDRESS(ctypes.Structure):
|
|
||||||
_fields_ = [('address', POINTER(SOCKADDR_INET)),
|
|
||||||
('length', INT)]
|
|
||||||
|
|
||||||
|
|
||||||
class _IP_ADAPTER_ADDRESSES_METRIC(Structure):
|
|
||||||
_fields_ = [('length', ULONG),
|
|
||||||
('interface_index', DWORD)]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADAPTER_UNICAST_ADDRESS(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
PIP_ADAPTER_UNICAST_ADDRESS = POINTER(IP_ADAPTER_UNICAST_ADDRESS)
|
|
||||||
if WINDOWS_XP:
|
|
||||||
IP_ADAPTER_UNICAST_ADDRESS._fields_ = [
|
|
||||||
("length", ULONG),
|
|
||||||
("flags", DWORD),
|
|
||||||
("next", PIP_ADAPTER_UNICAST_ADDRESS),
|
|
||||||
("address", SOCKET_ADDRESS),
|
|
||||||
("prefix_origin", INT),
|
|
||||||
("suffix_origin", INT),
|
|
||||||
("dad_state", INT),
|
|
||||||
("valid_lifetime", ULONG),
|
|
||||||
("preferred_lifetime", ULONG),
|
|
||||||
("lease_lifetime", ULONG),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
IP_ADAPTER_UNICAST_ADDRESS._fields_ = [
|
|
||||||
("length", ULONG),
|
|
||||||
("flags", DWORD),
|
|
||||||
("next", PIP_ADAPTER_UNICAST_ADDRESS),
|
|
||||||
("address", SOCKET_ADDRESS),
|
|
||||||
("prefix_origin", INT),
|
|
||||||
("suffix_origin", INT),
|
|
||||||
("dad_state", INT),
|
|
||||||
("valid_lifetime", ULONG),
|
|
||||||
("preferred_lifetime", ULONG),
|
|
||||||
("lease_lifetime", ULONG),
|
|
||||||
("on_link_prefix_length", UBYTE)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADAPTER_ANYCAST_ADDRESS(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
PIP_ADAPTER_ANYCAST_ADDRESS = POINTER(IP_ADAPTER_ANYCAST_ADDRESS)
|
|
||||||
IP_ADAPTER_ANYCAST_ADDRESS._fields_ = [
|
|
||||||
("length", ULONG),
|
|
||||||
("flags", DWORD),
|
|
||||||
("next", PIP_ADAPTER_ANYCAST_ADDRESS),
|
|
||||||
("address", SOCKET_ADDRESS),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADAPTER_MULTICAST_ADDRESS(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
PIP_ADAPTER_MULTICAST_ADDRESS = POINTER(IP_ADAPTER_MULTICAST_ADDRESS)
|
|
||||||
IP_ADAPTER_MULTICAST_ADDRESS._fields_ = [
|
|
||||||
("length", ULONG),
|
|
||||||
("flags", DWORD),
|
|
||||||
("next", PIP_ADAPTER_MULTICAST_ADDRESS),
|
|
||||||
("address", SOCKET_ADDRESS),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADAPTER_DNS_SERVER_ADDRESS(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
PIP_ADAPTER_DNS_SERVER_ADDRESS = POINTER(IP_ADAPTER_DNS_SERVER_ADDRESS)
|
|
||||||
IP_ADAPTER_DNS_SERVER_ADDRESS._fields_ = [
|
|
||||||
("length", ULONG),
|
|
||||||
("flags", DWORD),
|
|
||||||
("next", PIP_ADAPTER_DNS_SERVER_ADDRESS),
|
|
||||||
("address", SOCKET_ADDRESS),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADAPTER_PREFIX(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
PIP_ADAPTER_PREFIX = ctypes.POINTER(IP_ADAPTER_PREFIX)
|
|
||||||
IP_ADAPTER_PREFIX._fields_ = [
|
|
||||||
("alignment", ULONGLONG),
|
|
||||||
("next", PIP_ADAPTER_PREFIX),
|
|
||||||
("address", SOCKET_ADDRESS),
|
|
||||||
("prefix_length", ULONG)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADAPTER_ADDRESSES(Structure):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
LP_IP_ADAPTER_ADDRESSES = POINTER(IP_ADAPTER_ADDRESSES)
|
|
||||||
|
|
||||||
if WINDOWS_XP:
|
|
||||||
IP_ADAPTER_ADDRESSES._fields_ = [
|
|
||||||
('length', ULONG),
|
|
||||||
('interface_index', DWORD),
|
|
||||||
('next', LP_IP_ADAPTER_ADDRESSES),
|
|
||||||
('adapter_name', ctypes.c_char_p),
|
|
||||||
('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS),
|
|
||||||
('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS),
|
|
||||||
('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS),
|
|
||||||
('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS),
|
|
||||||
('dns_suffix', ctypes.c_wchar_p),
|
|
||||||
('description', ctypes.c_wchar_p),
|
|
||||||
('friendly_name', ctypes.c_wchar_p),
|
|
||||||
('physical_address', BYTE * MAX_ADAPTER_ADDRESS_LENGTH),
|
|
||||||
('physical_address_length', ULONG),
|
|
||||||
('flags', ULONG),
|
|
||||||
('mtu', ULONG),
|
|
||||||
('interface_type', DWORD),
|
|
||||||
('oper_status', IF_OPER_STATUS),
|
|
||||||
('ipv6_interface_index', DWORD),
|
|
||||||
('zone_indices', ULONG * 16),
|
|
||||||
('first_prefix', PIP_ADAPTER_PREFIX),
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
IP_ADAPTER_ADDRESSES._fields_ = [
|
|
||||||
('length', ULONG),
|
|
||||||
('interface_index', DWORD),
|
|
||||||
('next', LP_IP_ADAPTER_ADDRESSES),
|
|
||||||
('adapter_name', ctypes.c_char_p),
|
|
||||||
('first_unicast_address', PIP_ADAPTER_UNICAST_ADDRESS),
|
|
||||||
('first_anycast_address', PIP_ADAPTER_ANYCAST_ADDRESS),
|
|
||||||
('first_multicast_address', PIP_ADAPTER_MULTICAST_ADDRESS),
|
|
||||||
('first_dns_server_address', PIP_ADAPTER_DNS_SERVER_ADDRESS),
|
|
||||||
('dns_suffix', ctypes.c_wchar_p),
|
|
||||||
('description', ctypes.c_wchar_p),
|
|
||||||
('friendly_name', ctypes.c_wchar_p),
|
|
||||||
('physical_address', BYTE * MAX_ADAPTER_ADDRESS_LENGTH),
|
|
||||||
('physical_address_length', ULONG),
|
|
||||||
('flags', ULONG),
|
|
||||||
('mtu', ULONG),
|
|
||||||
('interface_type', DWORD),
|
|
||||||
('oper_status', IF_OPER_STATUS),
|
|
||||||
('ipv6_interface_index', DWORD),
|
|
||||||
('zone_indices', ULONG * 16),
|
|
||||||
('first_prefix', PIP_ADAPTER_PREFIX),
|
|
||||||
('transmit_link_speed', ULONGLONG),
|
|
||||||
('receive_link_speed', ULONGLONG),
|
|
||||||
('first_wins_server_address', PIP_ADAPTER_WINS_SERVER_ADDRESS_LH),
|
|
||||||
('first_gateway_address', PIP_ADAPTER_GATEWAY_ADDRESS_LH),
|
|
||||||
('ipv4_metric', ULONG),
|
|
||||||
('ipv6_metric', ULONG),
|
|
||||||
('luid', IF_LUID),
|
|
||||||
('dhcpv4_server', SOCKET_ADDRESS),
|
|
||||||
('compartment_id', NET_IF_COMPARTMENT_ID),
|
|
||||||
('network_guid', NET_IF_NETWORK_GUID),
|
|
||||||
('connection_type', NET_IF_CONNECTION_TYPE),
|
|
||||||
('tunnel_type', TUNNEL_TYPE),
|
|
||||||
('dhcpv6_server', SOCKET_ADDRESS),
|
|
||||||
('dhcpv6_client_duid', BYTE * MAX_DHCPV6_DUID_LENGTH),
|
|
||||||
('dhcpv6_client_duid_length', ULONG),
|
|
||||||
('dhcpv6_iaid', ULONG),
|
|
||||||
('first_dns_suffix', PIP_ADAPTER_DNS_SUFFIX)]
|
|
||||||
|
|
||||||
# Func
|
|
||||||
|
|
||||||
_GetAdaptersAddresses = WINFUNCTYPE(ULONG, ULONG, ULONG,
|
|
||||||
POINTER(VOID),
|
|
||||||
LP_IP_ADAPTER_ADDRESSES,
|
|
||||||
POINTER(ULONG))(
|
|
||||||
('GetAdaptersAddresses', iphlpapi))
|
|
||||||
|
|
||||||
|
|
||||||
def GetAdaptersAddresses(AF=AF_UNSPEC):
|
|
||||||
"""Return all Windows Adapters addresses from iphlpapi"""
|
|
||||||
# We get the size first
|
|
||||||
size = ULONG()
|
|
||||||
flags = GAA_FLAG_INCLUDE_PREFIX
|
|
||||||
res = _GetAdaptersAddresses(AF, flags,
|
|
||||||
None, None,
|
|
||||||
byref(size))
|
|
||||||
if res != 0x6f: # BUFFER OVERFLOW -> populate size
|
|
||||||
raise RuntimeError("Error getting structure length (%d)" % res)
|
|
||||||
# Now let's build our buffer
|
|
||||||
pointer_type = POINTER(IP_ADAPTER_ADDRESSES)
|
|
||||||
buffer = create_string_buffer(size.value)
|
|
||||||
AdapterAddresses = ctypes.cast(buffer, pointer_type)
|
|
||||||
# And call GetAdaptersAddresses
|
|
||||||
res = _GetAdaptersAddresses(AF, flags,
|
|
||||||
None, AdapterAddresses,
|
|
||||||
byref(size))
|
|
||||||
if res != NO_ERROR:
|
|
||||||
raise RuntimeError("Error retrieving table (%d)" % res)
|
|
||||||
results = _resolve_list(AdapterAddresses)
|
|
||||||
del(AdapterAddresses)
|
|
||||||
return results
|
|
||||||
|
|
||||||
##############################
|
|
||||||
####### Routing tables #######
|
|
||||||
##############################
|
|
||||||
|
|
||||||
### V1 ###
|
|
||||||
|
|
||||||
|
|
||||||
class MIB_IPFORWARDROW(Structure):
|
|
||||||
_fields_ = [('ForwardDest', DWORD),
|
|
||||||
('ForwardMask', DWORD),
|
|
||||||
('ForwardPolicy', DWORD),
|
|
||||||
('ForwardNextHop', DWORD),
|
|
||||||
('ForwardIfIndex', DWORD),
|
|
||||||
('ForwardType', DWORD),
|
|
||||||
('ForwardProto', DWORD),
|
|
||||||
('ForwardAge', DWORD),
|
|
||||||
('ForwardNextHopAS', DWORD),
|
|
||||||
('ForwardMetric1', DWORD),
|
|
||||||
('ForwardMetric2', DWORD),
|
|
||||||
('ForwardMetric3', DWORD),
|
|
||||||
('ForwardMetric4', DWORD),
|
|
||||||
('ForwardMetric5', DWORD)]
|
|
||||||
|
|
||||||
|
|
||||||
class MIB_IPFORWARDTABLE(Structure):
|
|
||||||
_fields_ = [('NumEntries', DWORD),
|
|
||||||
('Table', MIB_IPFORWARDROW * ANY_SIZE)]
|
|
||||||
|
|
||||||
|
|
||||||
PMIB_IPFORWARDTABLE = POINTER(MIB_IPFORWARDTABLE)
|
|
||||||
|
|
||||||
# Func
|
|
||||||
|
|
||||||
_GetIpForwardTable = WINFUNCTYPE(DWORD,
|
|
||||||
PMIB_IPFORWARDTABLE, POINTER(ULONG), BOOL)(
|
|
||||||
('GetIpForwardTable', iphlpapi))
|
|
||||||
|
|
||||||
|
|
||||||
def GetIpForwardTable():
|
|
||||||
"""Return all Windows routes (IPv4 only) from iphlpapi"""
|
|
||||||
# We get the size first
|
|
||||||
size = ULONG()
|
|
||||||
res = _GetIpForwardTable(None, byref(size), False)
|
|
||||||
if res != 0x7a: # ERROR_INSUFFICIENT_BUFFER -> populate size
|
|
||||||
raise RuntimeError("Error getting structure length (%d)" % res)
|
|
||||||
# Now let's build our buffer
|
|
||||||
pointer_type = PMIB_IPFORWARDTABLE
|
|
||||||
buffer = create_string_buffer(size.value)
|
|
||||||
pIpForwardTable = ctypes.cast(buffer, pointer_type)
|
|
||||||
# And call GetAdaptersAddresses
|
|
||||||
res = _GetIpForwardTable(pIpForwardTable, byref(size), True)
|
|
||||||
if res != NO_ERROR:
|
|
||||||
raise RuntimeError("Error retrieving table (%d)" % res)
|
|
||||||
results = []
|
|
||||||
for i in range(pIpForwardTable.contents.NumEntries):
|
|
||||||
results.append(_struct_to_dict(pIpForwardTable.contents.Table[i]))
|
|
||||||
del(pIpForwardTable)
|
|
||||||
return results
|
|
||||||
|
|
||||||
### V2 ###
|
|
||||||
|
|
||||||
|
|
||||||
NET_IFINDEX = ULONG
|
|
||||||
NL_ROUTE_PROTOCOL = INT
|
|
||||||
NL_ROUTE_ORIGIN = INT
|
|
||||||
|
|
||||||
|
|
||||||
class NET_LUID(Structure):
|
|
||||||
_fields_ = [("Value", ULONGLONG)]
|
|
||||||
|
|
||||||
|
|
||||||
class IP_ADDRESS_PREFIX(Structure):
|
|
||||||
_fields_ = [("Prefix", SOCKADDR_INET),
|
|
||||||
("PrefixLength", UINT8)]
|
|
||||||
|
|
||||||
|
|
||||||
class MIB_IPFORWARD_ROW2(Structure):
|
|
||||||
_fields_ = [("InterfaceLuid", NET_LUID),
|
|
||||||
("InterfaceIndex", NET_IFINDEX),
|
|
||||||
("DestinationPrefix", IP_ADDRESS_PREFIX),
|
|
||||||
("NextHop", SOCKADDR_INET),
|
|
||||||
("SitePrefixLength", UCHAR),
|
|
||||||
("ValidLifetime", ULONG),
|
|
||||||
("PreferredLifetime", ULONG),
|
|
||||||
("Metric", ULONG),
|
|
||||||
("Protocol", NL_ROUTE_PROTOCOL),
|
|
||||||
("Loopback", BOOLEAN),
|
|
||||||
("AutoconfigureAddress", BOOLEAN),
|
|
||||||
("Publish", BOOLEAN),
|
|
||||||
("Immortal", BOOLEAN),
|
|
||||||
("Age", ULONG),
|
|
||||||
("Origin", NL_ROUTE_ORIGIN)]
|
|
||||||
|
|
||||||
|
|
||||||
class MIB_IPFORWARD_TABLE2(Structure):
|
|
||||||
_fields_ = [("NumEntries", ULONG),
|
|
||||||
("Table", MIB_IPFORWARD_ROW2 * ANY_SIZE)]
|
|
||||||
|
|
||||||
|
|
||||||
PMIB_IPFORWARD_TABLE2 = POINTER(MIB_IPFORWARD_TABLE2)
|
|
||||||
|
|
||||||
# Func
|
|
||||||
|
|
||||||
if not WINDOWS_XP:
|
|
||||||
# GetIpForwardTable2 does not exist under Windows XP
|
|
||||||
_GetIpForwardTable2 = WINFUNCTYPE(
|
|
||||||
ULONG, USHORT,
|
|
||||||
POINTER(PMIB_IPFORWARD_TABLE2))(
|
|
||||||
('GetIpForwardTable2', iphlpapi)
|
|
||||||
)
|
|
||||||
_FreeMibTable = WINFUNCTYPE(None, PMIB_IPFORWARD_TABLE2)(
|
|
||||||
('FreeMibTable', iphlpapi)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def GetIpForwardTable2(AF=AF_UNSPEC):
|
|
||||||
"""Return all Windows routes (IPv4/IPv6) from iphlpapi"""
|
|
||||||
if WINDOWS_XP:
|
|
||||||
raise OSError("Not available on Windows XP !")
|
|
||||||
table = PMIB_IPFORWARD_TABLE2()
|
|
||||||
res = _GetIpForwardTable2(AF, byref(table))
|
|
||||||
if res != NO_ERROR:
|
|
||||||
raise RuntimeError("Error retrieving table (%d)" % res)
|
|
||||||
results = []
|
|
||||||
for i in range(table.contents.NumEntries):
|
|
||||||
results.append(_struct_to_dict(table.contents.Table[i]))
|
|
||||||
_FreeMibTable(table)
|
|
||||||
return results
|
|
|
@ -1,143 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Resolve Autonomous Systems (AS).
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
import socket
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.compat import plain_str
|
|
||||||
|
|
||||||
|
|
||||||
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 line in txt.splitlines():
|
|
||||||
if not asn and line.startswith(b"origin:"):
|
|
||||||
asn = plain_str(line[7:].strip())
|
|
||||||
if line.startswith(b"descr:"):
|
|
||||||
if desc:
|
|
||||||
desc += b"\n"
|
|
||||||
desc += line[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() for ip in ips) +
|
|
||||||
b"\r\nend\r\n"
|
|
||||||
)
|
|
||||||
r = b""
|
|
||||||
while True:
|
|
||||||
line = s.recv(8192)
|
|
||||||
if line == b"":
|
|
||||||
break
|
|
||||||
r += line
|
|
||||||
s.close()
|
|
||||||
|
|
||||||
return self.parse(r)
|
|
||||||
|
|
||||||
def parse(self, data):
|
|
||||||
"""Parse bulk cymru data"""
|
|
||||||
|
|
||||||
ASNlist = []
|
|
||||||
for line in data.splitlines()[1:]:
|
|
||||||
line = plain_str(line)
|
|
||||||
if "|" not in line:
|
|
||||||
continue
|
|
||||||
asn, ip, desc = [elt.strip() for elt in line.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())
|
|
||||||
resolvers_list = resolvers_list[1:]
|
|
||||||
|
|
||||||
def __init__(self, *reslist):
|
|
||||||
AS_resolver.__init__(self)
|
|
||||||
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:
|
|
||||||
continue
|
|
||||||
todo = [ip for ip in todo if ip not in [r[0] for r in res]]
|
|
||||||
ret += res
|
|
||||||
if not todo:
|
|
||||||
break
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
conf.AS_resolver = AS_resolver_multi()
|
|
|
@ -1,8 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package holding ASN.1 related modules.
|
|
||||||
"""
|
|
|
@ -1,517 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# 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, 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" # noqa: E501
|
|
||||||
|
|
||||||
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): # noqa: E501
|
|
||||||
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 is 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) # noqa: E501
|
|
||||||
|
|
||||||
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:
|
|
||||||
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__() # noqa: E501
|
|
||||||
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 six.itervalues(cls.__dict__):
|
|
||||||
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 Exception:
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
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__), # noqa: E501
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
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 stored in val_readable
|
|
||||||
"""
|
|
||||||
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):
|
|
||||||
if name == "val_readable":
|
|
||||||
if isinstance(value, (str, bytes)):
|
|
||||||
val = "".join(binrepr(orb(x)).zfill(8) for x in value)
|
|
||||||
else:
|
|
||||||
warning("Invalid val: should be bytes")
|
|
||||||
val = "<invalid val_readable>"
|
|
||||||
object.__setattr__(self, "val", val)
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
object.__setattr__(self, "unused_bits", 0)
|
|
||||||
elif name == "val":
|
|
||||||
value = plain_str(value)
|
|
||||||
if isinstance(value, str):
|
|
||||||
if any(c for c in value if c not in ["0", "1"]):
|
|
||||||
warning("Invalid operation: 'val' is not a valid bit string.") # noqa: E501
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
if len(value) % 8 == 0:
|
|
||||||
unused_bits = 0
|
|
||||||
else:
|
|
||||||
unused_bits = 8 - (len(value) % 8)
|
|
||||||
padded_value = 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) # noqa: E501
|
|
||||||
else:
|
|
||||||
warning("Invalid val: should be str")
|
|
||||||
val_readable = b"<invalid val>"
|
|
||||||
unused_bits = 0
|
|
||||||
object.__setattr__(self, "val_readable", val_readable)
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
object.__setattr__(self, "unused_bits", unused_bits)
|
|
||||||
elif name == "unused_bits":
|
|
||||||
warning("Invalid operation: unused_bits rewriting "
|
|
||||||
"is not supported.")
|
|
||||||
else:
|
|
||||||
object.__setattr__(self, name, value)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
s = self.val_readable
|
|
||||||
if len(s) > 16:
|
|
||||||
s = s[:10] + b"..." + s[-10:]
|
|
||||||
v = self.val
|
|
||||||
if len(v) > 20:
|
|
||||||
v = v[:10] + "..." + v[-10:]
|
|
||||||
return "<%s[%s]=%s (%d unused bit%s)>" % (
|
|
||||||
self.__dict__.get("name", self.__class__.__name__),
|
|
||||||
v,
|
|
||||||
s,
|
|
||||||
self.unused_bits,
|
|
||||||
"s" if self.unused_bits > 1 else ""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
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 = plain_str(val)
|
|
||||||
val = conf.mib._oid(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) # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
ASN1_STRING.__init__(self, val)
|
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
|
||||||
if isinstance(value, bytes):
|
|
||||||
value = plain_str(value)
|
|
||||||
if name == "val":
|
|
||||||
pretty_time = None
|
|
||||||
if isinstance(self, ASN1_GENERALIZED_TIME):
|
|
||||||
_len = 15
|
|
||||||
self._format = "%Y%m%d%H%M%S"
|
|
||||||
else:
|
|
||||||
_len = 13
|
|
||||||
self._format = "%y%m%d%H%M%S"
|
|
||||||
_nam = self.tag._asn1_obj.__name__[4:].lower()
|
|
||||||
if (isinstance(value, str) and
|
|
||||||
len(value) == _len and value[-1] == "Z"):
|
|
||||||
dt = datetime.strptime(value[:-1], self._format)
|
|
||||||
pretty_time = dt.strftime("%b %d %H:%M:%S %Y GMT")
|
|
||||||
else:
|
|
||||||
pretty_time = "%s [invalid %s]" % (value, _nam)
|
|
||||||
ASN1_STRING.__setattr__(self, "pretty_time", pretty_time)
|
|
||||||
ASN1_STRING.__setattr__(self, name, value)
|
|
||||||
elif name == "pretty_time":
|
|
||||||
print("Invalid operation: pretty_time rewriting is not supported.")
|
|
||||||
else:
|
|
||||||
ASN1_STRING.__setattr__(self, name, value)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "%s %s" % (self.pretty_time, ASN1_STRING.__repr__(self))
|
|
||||||
|
|
||||||
|
|
||||||
class ASN1_GENERALIZED_TIME(ASN1_UTC_TIME):
|
|
||||||
tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
|
@ -1,565 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# 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 chb, orb, bytes_encode
|
|
||||||
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
|
|
||||||
from scapy.modules import 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(ll, size=0):
|
|
||||||
if ll <= 127 and size == 0:
|
|
||||||
return chb(ll)
|
|
||||||
s = b""
|
|
||||||
while ll or size > 0:
|
|
||||||
s = chb(ll & 0xff) + s
|
|
||||||
ll >>= 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):
|
|
||||||
tmp_len = orb(s[0])
|
|
||||||
if not tmp_len & 0x80:
|
|
||||||
return tmp_len, s[1:]
|
|
||||||
tmp_len &= 0x7f
|
|
||||||
if len(s) <= tmp_len:
|
|
||||||
raise BER_Decoding_Error(
|
|
||||||
"BER_len_dec: Got %i bytes while expecting %i" %
|
|
||||||
(len(s) - 1, tmp_len),
|
|
||||||
remaining=s
|
|
||||||
)
|
|
||||||
ll = 0
|
|
||||||
for c in s[1:tmp_len + 1]:
|
|
||||||
ll <<= 8
|
|
||||||
ll |= orb(c)
|
|
||||||
return ll, s[tmp_len + 1:]
|
|
||||||
|
|
||||||
|
|
||||||
def BER_num_enc(ll, size=1):
|
|
||||||
x = []
|
|
||||||
while ll or size > 0:
|
|
||||||
x.insert(0, ll & 0x7f)
|
|
||||||
if len(x) > 1:
|
|
||||||
x[0] |= 0x80
|
|
||||||
ll >>= 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 Exception:
|
|
||||||
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 not isinstance(tag, int) or 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, remainder = 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)
|
|
||||||
if codec == BERcodec_Object:
|
|
||||||
# Value type defined as Unknown
|
|
||||||
l, s = BER_num_dec(remainder)
|
|
||||||
return ASN1_BADTAG(s[:l]), s[l:]
|
|
||||||
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 + (bytes,)):
|
|
||||||
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 = bytes_encode(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 = bytes_encode(s)
|
|
||||||
# Be sure we are encoding bytes
|
|
||||||
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)
|
|
||||||
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 = bytes_encode(oid)
|
|
||||||
if oid:
|
|
||||||
lst = [int(x) for x in oid.strip(b".").split(b".")]
|
|
||||||
else:
|
|
||||||
lst = list()
|
|
||||||
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, ll):
|
|
||||||
if not isinstance(ll, bytes):
|
|
||||||
ll = b"".join(x.enc(cls.codec) for x in ll)
|
|
||||||
return chb(hash(cls.tag)) + BER_len_enc(len(ll)) + ll
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def do_dec(cls, s, context=None, safe=False):
|
|
||||||
if context is None:
|
|
||||||
context = cls.tag.context
|
|
||||||
ll, st = cls.check_type_get_len(s) # we may have len(s) < ll
|
|
||||||
s, t = st[:ll], st[ll:]
|
|
||||||
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) < ll:
|
|
||||||
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
|
|
|
@ -1,623 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# 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 plain_str
|
|
||||||
|
|
||||||
#################
|
|
||||||
# MIB parsing #
|
|
||||||
#################
|
|
||||||
|
|
||||||
_mib_re_integer = re.compile(r"^[0-9]+$")
|
|
||||||
_mib_re_both = re.compile(r"^([a-zA-Z_][a-zA-Z0-9_-]*)\(([0-9]+)\)$")
|
|
||||||
_mib_re_oiddecl = re.compile(r"$\s*([a-zA-Z0-9_-]+)\s+OBJECT([^:\{\}]|\{[^:]+\})+::=\s*\{([^\}]+)\}", re.M) # noqa: E501
|
|
||||||
_mib_re_strings = re.compile(r'"[^"]*"')
|
|
||||||
_mib_re_comments = re.compile(r'--.*(\r|\n)')
|
|
||||||
|
|
||||||
|
|
||||||
class MIBDict(DADict):
|
|
||||||
def fixname(self, val):
|
|
||||||
# We overwrite DADict fixname method as we want to keep - in names
|
|
||||||
return val
|
|
||||||
|
|
||||||
def _findroot(self, x):
|
|
||||||
"""Internal MIBDict function used to find a partial OID"""
|
|
||||||
if x.startswith("."):
|
|
||||||
x = x[1:]
|
|
||||||
if not x.endswith("."):
|
|
||||||
x += "."
|
|
||||||
max = 0
|
|
||||||
root = "."
|
|
||||||
root_key = ""
|
|
||||||
for k in six.iterkeys(self):
|
|
||||||
if x.startswith(k + "."):
|
|
||||||
if max < len(k):
|
|
||||||
max = len(k)
|
|
||||||
root = self[k]
|
|
||||||
root_key = k
|
|
||||||
return root, root_key, x[max:-1]
|
|
||||||
|
|
||||||
def _oidname(self, x):
|
|
||||||
"""Deduce the OID name from its OID ID"""
|
|
||||||
root, _, remainder = self._findroot(x)
|
|
||||||
return root + remainder
|
|
||||||
|
|
||||||
def _oid(self, x):
|
|
||||||
"""Parse the OID id/OID generator, and return real OID"""
|
|
||||||
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 six.itervalues(self.__dict__):
|
|
||||||
return x
|
|
||||||
xl[p] = next(k for k, v in six.iteritems(self.__dict__) if v == xl[p])
|
|
||||||
return ".".join(xl[p:])
|
|
||||||
|
|
||||||
def _make_graph(self, other_keys=None, **kargs):
|
|
||||||
if other_keys is None:
|
|
||||||
other_keys = []
|
|
||||||
nodes = [(self[key], key) for key in self.iterkeys()]
|
|
||||||
oids = set(self.iterkeys())
|
|
||||||
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, parent_key, remainder = self._findroot(o[:-1])
|
|
||||||
remainder = remainder[1:] + o[-1]
|
|
||||||
if parent != ".":
|
|
||||||
parent = parent_key
|
|
||||||
s += '\t"%s" -> "%s" [label="%s"];\n' % (parent, o, remainder)
|
|
||||||
s += "}\n"
|
|
||||||
do_graph(s, **kargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _mib_register(ident, value, the_mib, unresolved):
|
|
||||||
"""Internal function used to register an OID and its name in a MIBDict"""
|
|
||||||
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):
|
|
||||||
"""Load the conf.mib dict from a list of filenames"""
|
|
||||||
the_mib = {'iso': ['1']}
|
|
||||||
unresolved = {}
|
|
||||||
for k in six.iterkeys(conf.mib):
|
|
||||||
_mib_register(conf.mib[k], k.split("."), the_mib, unresolved)
|
|
||||||
|
|
||||||
if isinstance(filenames, (str, bytes)):
|
|
||||||
filenames = [filenames]
|
|
||||||
for fnames in filenames:
|
|
||||||
for fname in glob(fnames):
|
|
||||||
with open(fname) as f:
|
|
||||||
text = f.read()
|
|
||||||
cleantext = " ".join(_mib_re_strings.split(" ".join(_mib_re_comments.split(text)))) # noqa: E501
|
|
||||||
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 oid, key in six.iteritems(the_mib):
|
|
||||||
newmib[".".join(key)] = oid
|
|
||||||
for oid, key in six.iteritems(unresolved):
|
|
||||||
newmib[".".join(key)] = oid
|
|
||||||
|
|
||||||
conf.mib = newmib
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
|
||||||
# OID references #
|
|
||||||
####################
|
|
||||||
|
|
||||||
# pkcs1 #
|
|
||||||
|
|
||||||
pkcs1_oids = {
|
|
||||||
"1.2.840.113549.1.1.1": "rsaEncryption",
|
|
||||||
"1.2.840.113549.1.1.2": "md2WithRSAEncryption",
|
|
||||||
"1.2.840.113549.1.1.3": "md4WithRSAEncryption",
|
|
||||||
"1.2.840.113549.1.1.4": "md5WithRSAEncryption",
|
|
||||||
"1.2.840.113549.1.1.5": "sha1-with-rsa-signature",
|
|
||||||
"1.2.840.113549.1.1.6": "rsaOAEPEncryptionSET",
|
|
||||||
"1.2.840.113549.1.1.7": "id-RSAES-OAEP",
|
|
||||||
"1.2.840.113549.1.1.8": "id-mgf1",
|
|
||||||
"1.2.840.113549.1.1.9": "id-pSpecified",
|
|
||||||
"1.2.840.113549.1.1.10": "rsassa-pss",
|
|
||||||
"1.2.840.113549.1.1.11": "sha256WithRSAEncryption",
|
|
||||||
"1.2.840.113549.1.1.12": "sha384WithRSAEncryption",
|
|
||||||
"1.2.840.113549.1.1.13": "sha512WithRSAEncryption",
|
|
||||||
"1.2.840.113549.1.1.14": "sha224WithRSAEncryption"
|
|
||||||
}
|
|
||||||
|
|
||||||
# secsig oiw #
|
|
||||||
|
|
||||||
secsig_oids = {
|
|
||||||
"1.3.14.3.2.26": "sha1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# pkcs9 #
|
|
||||||
|
|
||||||
pkcs9_oids = {
|
|
||||||
"1.2.840.113549.1.9.0": "modules",
|
|
||||||
"1.2.840.113549.1.9.1": "emailAddress",
|
|
||||||
"1.2.840.113549.1.9.2": "unstructuredName",
|
|
||||||
"1.2.840.113549.1.9.3": "contentType",
|
|
||||||
"1.2.840.113549.1.9.4": "messageDigest",
|
|
||||||
"1.2.840.113549.1.9.5": "signing-time",
|
|
||||||
"1.2.840.113549.1.9.6": "countersignature",
|
|
||||||
"1.2.840.113549.1.9.7": "challengePassword",
|
|
||||||
"1.2.840.113549.1.9.8": "unstructuredAddress",
|
|
||||||
"1.2.840.113549.1.9.9": "extendedCertificateAttributes",
|
|
||||||
"1.2.840.113549.1.9.13": "signingDescription",
|
|
||||||
"1.2.840.113549.1.9.14": "extensionRequest",
|
|
||||||
"1.2.840.113549.1.9.15": "smimeCapabilities",
|
|
||||||
"1.2.840.113549.1.9.16": "smime",
|
|
||||||
"1.2.840.113549.1.9.17": "pgpKeyID",
|
|
||||||
"1.2.840.113549.1.9.20": "friendlyName",
|
|
||||||
"1.2.840.113549.1.9.21": "localKeyID",
|
|
||||||
"1.2.840.113549.1.9.22": "certTypes",
|
|
||||||
"1.2.840.113549.1.9.23": "crlTypes",
|
|
||||||
"1.2.840.113549.1.9.24": "pkcs-9-oc",
|
|
||||||
"1.2.840.113549.1.9.25": "pkcs-9-at",
|
|
||||||
"1.2.840.113549.1.9.26": "pkcs-9-sx",
|
|
||||||
"1.2.840.113549.1.9.27": "pkcs-9-mr",
|
|
||||||
"1.2.840.113549.1.9.52": "id-aa-CMSAlgorithmProtection"
|
|
||||||
}
|
|
||||||
|
|
||||||
# x509 #
|
|
||||||
|
|
||||||
attributeType_oids = {
|
|
||||||
"2.5.4.0": "objectClass",
|
|
||||||
"2.5.4.1": "aliasedEntryName",
|
|
||||||
"2.5.4.2": "knowledgeInformation",
|
|
||||||
"2.5.4.3": "commonName",
|
|
||||||
"2.5.4.4": "surname",
|
|
||||||
"2.5.4.5": "serialNumber",
|
|
||||||
"2.5.4.6": "countryName",
|
|
||||||
"2.5.4.7": "localityName",
|
|
||||||
"2.5.4.8": "stateOrProvinceName",
|
|
||||||
"2.5.4.9": "streetAddress",
|
|
||||||
"2.5.4.10": "organizationName",
|
|
||||||
"2.5.4.11": "organizationUnitName",
|
|
||||||
"2.5.4.12": "title",
|
|
||||||
"2.5.4.13": "description",
|
|
||||||
"2.5.4.14": "searchGuide",
|
|
||||||
"2.5.4.15": "businessCategory",
|
|
||||||
"2.5.4.16": "postalAddress",
|
|
||||||
"2.5.4.17": "postalCode",
|
|
||||||
"2.5.4.18": "postOfficeBox",
|
|
||||||
"2.5.4.19": "physicalDeliveryOfficeName",
|
|
||||||
"2.5.4.20": "telephoneNumber",
|
|
||||||
"2.5.4.21": "telexNumber",
|
|
||||||
"2.5.4.22": "teletexTerminalIdentifier",
|
|
||||||
"2.5.4.23": "facsimileTelephoneNumber",
|
|
||||||
"2.5.4.24": "x121Address",
|
|
||||||
"2.5.4.25": "internationalISDNNumber",
|
|
||||||
"2.5.4.26": "registeredAddress",
|
|
||||||
"2.5.4.27": "destinationIndicator",
|
|
||||||
"2.5.4.28": "preferredDeliveryMethod",
|
|
||||||
"2.5.4.29": "presentationAddress",
|
|
||||||
"2.5.4.30": "supportedApplicationContext",
|
|
||||||
"2.5.4.31": "member",
|
|
||||||
"2.5.4.32": "owner",
|
|
||||||
"2.5.4.33": "roleOccupant",
|
|
||||||
"2.5.4.34": "seeAlso",
|
|
||||||
"2.5.4.35": "userPassword",
|
|
||||||
"2.5.4.36": "userCertificate",
|
|
||||||
"2.5.4.37": "cACertificate",
|
|
||||||
"2.5.4.38": "authorityRevocationList",
|
|
||||||
"2.5.4.39": "certificateRevocationList",
|
|
||||||
"2.5.4.40": "crossCertificatePair",
|
|
||||||
"2.5.4.41": "name",
|
|
||||||
"2.5.4.42": "givenName",
|
|
||||||
"2.5.4.43": "initials",
|
|
||||||
"2.5.4.44": "generationQualifier",
|
|
||||||
"2.5.4.45": "uniqueIdentifier",
|
|
||||||
"2.5.4.46": "dnQualifier",
|
|
||||||
"2.5.4.47": "enhancedSearchGuide",
|
|
||||||
"2.5.4.48": "protocolInformation",
|
|
||||||
"2.5.4.49": "distinguishedName",
|
|
||||||
"2.5.4.50": "uniqueMember",
|
|
||||||
"2.5.4.51": "houseIdentifier",
|
|
||||||
"2.5.4.52": "supportedAlgorithms",
|
|
||||||
"2.5.4.53": "deltaRevocationList",
|
|
||||||
"2.5.4.54": "dmdName",
|
|
||||||
"2.5.4.55": "clearance",
|
|
||||||
"2.5.4.56": "defaultDirQop",
|
|
||||||
"2.5.4.57": "attributeIntegrityInfo",
|
|
||||||
"2.5.4.58": "attributeCertificate",
|
|
||||||
"2.5.4.59": "attributeCertificateRevocationList",
|
|
||||||
"2.5.4.60": "confKeyInfo",
|
|
||||||
"2.5.4.61": "aACertificate",
|
|
||||||
"2.5.4.62": "attributeDescriptorCertificate",
|
|
||||||
"2.5.4.63": "attributeAuthorityRevocationList",
|
|
||||||
"2.5.4.64": "family-information",
|
|
||||||
"2.5.4.65": "pseudonym",
|
|
||||||
"2.5.4.66": "communicationsService",
|
|
||||||
"2.5.4.67": "communicationsNetwork",
|
|
||||||
"2.5.4.68": "certificationPracticeStmt",
|
|
||||||
"2.5.4.69": "certificatePolicy",
|
|
||||||
"2.5.4.70": "pkiPath",
|
|
||||||
"2.5.4.71": "privPolicy",
|
|
||||||
"2.5.4.72": "role",
|
|
||||||
"2.5.4.73": "delegationPath",
|
|
||||||
"2.5.4.74": "protPrivPolicy",
|
|
||||||
"2.5.4.75": "xMLPrivilegeInfo",
|
|
||||||
"2.5.4.76": "xmlPrivPolicy",
|
|
||||||
"2.5.4.77": "uuidpair",
|
|
||||||
"2.5.4.78": "tagOid",
|
|
||||||
"2.5.4.79": "uiiFormat",
|
|
||||||
"2.5.4.80": "uiiInUrh",
|
|
||||||
"2.5.4.81": "contentUrl",
|
|
||||||
"2.5.4.82": "permission",
|
|
||||||
"2.5.4.83": "uri",
|
|
||||||
"2.5.4.84": "pwdAttribute",
|
|
||||||
"2.5.4.85": "userPwd",
|
|
||||||
"2.5.4.86": "urn",
|
|
||||||
"2.5.4.87": "url",
|
|
||||||
"2.5.4.88": "utmCoordinates",
|
|
||||||
"2.5.4.89": "urnC",
|
|
||||||
"2.5.4.90": "uii",
|
|
||||||
"2.5.4.91": "epc",
|
|
||||||
"2.5.4.92": "tagAfi",
|
|
||||||
"2.5.4.93": "epcFormat",
|
|
||||||
"2.5.4.94": "epcInUrn",
|
|
||||||
"2.5.4.95": "ldapUrl",
|
|
||||||
"2.5.4.96": "ldapUrl",
|
|
||||||
"2.5.4.97": "organizationIdentifier"
|
|
||||||
}
|
|
||||||
|
|
||||||
certificateExtension_oids = {
|
|
||||||
"2.5.29.1": "authorityKeyIdentifier",
|
|
||||||
"2.5.29.2": "keyAttributes",
|
|
||||||
"2.5.29.3": "certificatePolicies",
|
|
||||||
"2.5.29.4": "keyUsageRestriction",
|
|
||||||
"2.5.29.5": "policyMapping",
|
|
||||||
"2.5.29.6": "subtreesConstraint",
|
|
||||||
"2.5.29.7": "subjectAltName",
|
|
||||||
"2.5.29.8": "issuerAltName",
|
|
||||||
"2.5.29.9": "subjectDirectoryAttributes",
|
|
||||||
"2.5.29.10": "basicConstraints",
|
|
||||||
"2.5.29.14": "subjectKeyIdentifier",
|
|
||||||
"2.5.29.15": "keyUsage",
|
|
||||||
"2.5.29.16": "privateKeyUsagePeriod",
|
|
||||||
"2.5.29.17": "subjectAltName",
|
|
||||||
"2.5.29.18": "issuerAltName",
|
|
||||||
"2.5.29.19": "basicConstraints",
|
|
||||||
"2.5.29.20": "cRLNumber",
|
|
||||||
"2.5.29.21": "reasonCode",
|
|
||||||
"2.5.29.22": "expirationDate",
|
|
||||||
"2.5.29.23": "instructionCode",
|
|
||||||
"2.5.29.24": "invalidityDate",
|
|
||||||
"2.5.29.25": "cRLDistributionPoints",
|
|
||||||
"2.5.29.26": "issuingDistributionPoint",
|
|
||||||
"2.5.29.27": "deltaCRLIndicator",
|
|
||||||
"2.5.29.28": "issuingDistributionPoint",
|
|
||||||
"2.5.29.29": "certificateIssuer",
|
|
||||||
"2.5.29.30": "nameConstraints",
|
|
||||||
"2.5.29.31": "cRLDistributionPoints",
|
|
||||||
"2.5.29.32": "certificatePolicies",
|
|
||||||
"2.5.29.33": "policyMappings",
|
|
||||||
"2.5.29.34": "policyConstraints",
|
|
||||||
"2.5.29.35": "authorityKeyIdentifier",
|
|
||||||
"2.5.29.36": "policyConstraints",
|
|
||||||
"2.5.29.37": "extKeyUsage",
|
|
||||||
"2.5.29.38": "authorityAttributeIdentifier",
|
|
||||||
"2.5.29.39": "roleSpecCertIdentifier",
|
|
||||||
"2.5.29.40": "cRLStreamIdentifier",
|
|
||||||
"2.5.29.41": "basicAttConstraints",
|
|
||||||
"2.5.29.42": "delegatedNameConstraints",
|
|
||||||
"2.5.29.43": "timeSpecification",
|
|
||||||
"2.5.29.44": "cRLScope",
|
|
||||||
"2.5.29.45": "statusReferrals",
|
|
||||||
"2.5.29.46": "freshestCRL",
|
|
||||||
"2.5.29.47": "orderedList",
|
|
||||||
"2.5.29.48": "attributeDescriptor",
|
|
||||||
"2.5.29.49": "userNotice",
|
|
||||||
"2.5.29.50": "sOAIdentifier",
|
|
||||||
"2.5.29.51": "baseUpdateTime",
|
|
||||||
"2.5.29.52": "acceptableCertPolicies",
|
|
||||||
"2.5.29.53": "deltaInfo",
|
|
||||||
"2.5.29.54": "inhibitAnyPolicy",
|
|
||||||
"2.5.29.55": "targetInformation",
|
|
||||||
"2.5.29.56": "noRevAvail",
|
|
||||||
"2.5.29.57": "acceptablePrivilegePolicies",
|
|
||||||
"2.5.29.58": "id-ce-toBeRevoked",
|
|
||||||
"2.5.29.59": "id-ce-RevokedGroups",
|
|
||||||
"2.5.29.60": "id-ce-expiredCertsOnCRL",
|
|
||||||
"2.5.29.61": "indirectIssuer",
|
|
||||||
"2.5.29.62": "id-ce-noAssertion",
|
|
||||||
"2.5.29.63": "id-ce-aAissuingDistributionPoint",
|
|
||||||
"2.5.29.64": "id-ce-issuedOnBehaIFOF",
|
|
||||||
"2.5.29.65": "id-ce-singleUse",
|
|
||||||
"2.5.29.66": "id-ce-groupAC",
|
|
||||||
"2.5.29.67": "id-ce-allowedAttAss",
|
|
||||||
"2.5.29.68": "id-ce-attributeMappings",
|
|
||||||
"2.5.29.69": "id-ce-holderNameConstraints"
|
|
||||||
}
|
|
||||||
|
|
||||||
certExt_oids = {
|
|
||||||
"2.16.840.1.113730.1.1": "cert-type",
|
|
||||||
"2.16.840.1.113730.1.2": "base-url",
|
|
||||||
"2.16.840.1.113730.1.3": "revocation-url",
|
|
||||||
"2.16.840.1.113730.1.4": "ca-revocation-url",
|
|
||||||
"2.16.840.1.113730.1.5": "ca-crl-url",
|
|
||||||
"2.16.840.1.113730.1.6": "ca-cert-url",
|
|
||||||
"2.16.840.1.113730.1.7": "renewal-url",
|
|
||||||
"2.16.840.1.113730.1.8": "ca-policy-url",
|
|
||||||
"2.16.840.1.113730.1.9": "homepage-url",
|
|
||||||
"2.16.840.1.113730.1.10": "entity-logo",
|
|
||||||
"2.16.840.1.113730.1.11": "user-picture",
|
|
||||||
"2.16.840.1.113730.1.12": "ssl-server-name",
|
|
||||||
"2.16.840.1.113730.1.13": "comment",
|
|
||||||
"2.16.840.1.113730.1.14": "lost-password-url",
|
|
||||||
"2.16.840.1.113730.1.15": "cert-renewal-time",
|
|
||||||
"2.16.840.1.113730.1.16": "aia",
|
|
||||||
"2.16.840.1.113730.1.17": "cert-scope-of-use",
|
|
||||||
}
|
|
||||||
|
|
||||||
certPkixPe_oids = {
|
|
||||||
"1.3.6.1.5.5.7.1.1": "authorityInfoAccess",
|
|
||||||
"1.3.6.1.5.5.7.1.2": "biometricInfo",
|
|
||||||
"1.3.6.1.5.5.7.1.3": "qcStatements",
|
|
||||||
"1.3.6.1.5.5.7.1.4": "auditIdentity",
|
|
||||||
"1.3.6.1.5.5.7.1.6": "aaControls",
|
|
||||||
"1.3.6.1.5.5.7.1.10": "proxying",
|
|
||||||
"1.3.6.1.5.5.7.1.11": "subjectInfoAccess"
|
|
||||||
}
|
|
||||||
|
|
||||||
certPkixQt_oids = {
|
|
||||||
"1.3.6.1.5.5.7.2.1": "cps",
|
|
||||||
"1.3.6.1.5.5.7.2.2": "unotice"
|
|
||||||
}
|
|
||||||
|
|
||||||
certPkixKp_oids = {
|
|
||||||
"1.3.6.1.5.5.7.3.1": "serverAuth",
|
|
||||||
"1.3.6.1.5.5.7.3.2": "clientAuth",
|
|
||||||
"1.3.6.1.5.5.7.3.3": "codeSigning",
|
|
||||||
"1.3.6.1.5.5.7.3.4": "emailProtection",
|
|
||||||
"1.3.6.1.5.5.7.3.5": "ipsecEndSystem",
|
|
||||||
"1.3.6.1.5.5.7.3.6": "ipsecTunnel",
|
|
||||||
"1.3.6.1.5.5.7.3.7": "ipsecUser",
|
|
||||||
"1.3.6.1.5.5.7.3.8": "timeStamping",
|
|
||||||
"1.3.6.1.5.5.7.3.9": "ocspSigning",
|
|
||||||
"1.3.6.1.5.5.7.3.10": "dvcs",
|
|
||||||
"1.3.6.1.5.5.7.3.21": "secureShellClient",
|
|
||||||
"1.3.6.1.5.5.7.3.22": "secureShellServer"
|
|
||||||
}
|
|
||||||
|
|
||||||
certPkixAd_oids = {
|
|
||||||
"1.3.6.1.5.5.7.48.1": "ocsp",
|
|
||||||
"1.3.6.1.5.5.7.48.2": "caIssuers",
|
|
||||||
"1.3.6.1.5.5.7.48.3": "timestamping",
|
|
||||||
"1.3.6.1.5.5.7.48.4": "id-ad-dvcs",
|
|
||||||
"1.3.6.1.5.5.7.48.5": "id-ad-caRepository",
|
|
||||||
"1.3.6.1.5.5.7.48.6": "id-pkix-ocsp-archive-cutoff",
|
|
||||||
"1.3.6.1.5.5.7.48.7": "id-pkix-ocsp-service-locator",
|
|
||||||
"1.3.6.1.5.5.7.48.12": "id-ad-cmc",
|
|
||||||
"1.3.6.1.5.5.7.48.1.1": "basic-response"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ansi-x962 #
|
|
||||||
|
|
||||||
x962KeyType_oids = {
|
|
||||||
"1.2.840.10045.1.1": "prime-field",
|
|
||||||
"1.2.840.10045.1.2": "characteristic-two-field",
|
|
||||||
"1.2.840.10045.2.1": "ecPublicKey",
|
|
||||||
}
|
|
||||||
|
|
||||||
x962Signature_oids = {
|
|
||||||
"1.2.840.10045.4.1": "ecdsa-with-SHA1",
|
|
||||||
"1.2.840.10045.4.2": "ecdsa-with-Recommended",
|
|
||||||
"1.2.840.10045.4.3.1": "ecdsa-with-SHA224",
|
|
||||||
"1.2.840.10045.4.3.2": "ecdsa-with-SHA256",
|
|
||||||
"1.2.840.10045.4.3.3": "ecdsa-with-SHA384",
|
|
||||||
"1.2.840.10045.4.3.4": "ecdsa-with-SHA512"
|
|
||||||
}
|
|
||||||
|
|
||||||
# elliptic curves #
|
|
||||||
|
|
||||||
ansiX962Curve_oids = {
|
|
||||||
"1.2.840.10045.3.1.1": "prime192v1",
|
|
||||||
"1.2.840.10045.3.1.2": "prime192v2",
|
|
||||||
"1.2.840.10045.3.1.3": "prime192v3",
|
|
||||||
"1.2.840.10045.3.1.4": "prime239v1",
|
|
||||||
"1.2.840.10045.3.1.5": "prime239v2",
|
|
||||||
"1.2.840.10045.3.1.6": "prime239v3",
|
|
||||||
"1.2.840.10045.3.1.7": "prime256v1"
|
|
||||||
}
|
|
||||||
|
|
||||||
certicomCurve_oids = {
|
|
||||||
"1.3.132.0.1": "ansit163k1",
|
|
||||||
"1.3.132.0.2": "ansit163r1",
|
|
||||||
"1.3.132.0.3": "ansit239k1",
|
|
||||||
"1.3.132.0.4": "sect113r1",
|
|
||||||
"1.3.132.0.5": "sect113r2",
|
|
||||||
"1.3.132.0.6": "secp112r1",
|
|
||||||
"1.3.132.0.7": "secp112r2",
|
|
||||||
"1.3.132.0.8": "ansip160r1",
|
|
||||||
"1.3.132.0.9": "ansip160k1",
|
|
||||||
"1.3.132.0.10": "ansip256k1",
|
|
||||||
"1.3.132.0.15": "ansit163r2",
|
|
||||||
"1.3.132.0.16": "ansit283k1",
|
|
||||||
"1.3.132.0.17": "ansit283r1",
|
|
||||||
"1.3.132.0.22": "sect131r1",
|
|
||||||
"1.3.132.0.24": "ansit193r1",
|
|
||||||
"1.3.132.0.25": "ansit193r2",
|
|
||||||
"1.3.132.0.26": "ansit233k1",
|
|
||||||
"1.3.132.0.27": "ansit233r1",
|
|
||||||
"1.3.132.0.28": "secp128r1",
|
|
||||||
"1.3.132.0.29": "secp128r2",
|
|
||||||
"1.3.132.0.30": "ansip160r2",
|
|
||||||
"1.3.132.0.31": "ansip192k1",
|
|
||||||
"1.3.132.0.32": "ansip224k1",
|
|
||||||
"1.3.132.0.33": "ansip224r1",
|
|
||||||
"1.3.132.0.34": "ansip384r1",
|
|
||||||
"1.3.132.0.35": "ansip521r1",
|
|
||||||
"1.3.132.0.36": "ansit409k1",
|
|
||||||
"1.3.132.0.37": "ansit409r1",
|
|
||||||
"1.3.132.0.38": "ansit571k1",
|
|
||||||
"1.3.132.0.39": "ansit571r1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# policies #
|
|
||||||
|
|
||||||
certPolicy_oids = {
|
|
||||||
"anyPolicy": "2.5.29.32.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
# from Chromium source code (ev_root_ca_metadata.cc)
|
|
||||||
evPolicy_oids = {
|
|
||||||
'1.2.392.200091.100.721.1': 'EV Security Communication RootCA1',
|
|
||||||
'1.2.616.1.113527.2.5.1.1': 'EV Certum Trusted Network CA',
|
|
||||||
'1.3.159.1.17.1': 'EV Actualis Authentication Root CA',
|
|
||||||
'1.3.6.1.4.1.13177.10.1.3.10': 'EV Autoridad de Certificacion Firmaprofesional CIF A62634068', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.14370.1.6': 'EV GeoTrust Primary Certification Authority',
|
|
||||||
'1.3.6.1.4.1.14777.6.1.1': 'EV Izenpe.com roots Business',
|
|
||||||
'1.3.6.1.4.1.14777.6.1.2': 'EV Izenpe.com roots Government',
|
|
||||||
'1.3.6.1.4.1.17326.10.14.2.1.2': 'EV AC Camerfirma S.A. Chambers of Commerce Root - 2008', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.17326.10.14.2.2.2': 'EV AC Camerfirma S.A. Chambers of Commerce Root - 2008', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.17326.10.8.12.1.2': 'EV AC Camerfirma S.A. Global Chambersign Root - 2008', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.17326.10.8.12.2.2': 'EV AC Camerfirma S.A. Global Chambersign Root - 2008', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.22234.2.5.2.3.1': 'EV CertPlus Class 2 Primary CA (KEYNECTIS)', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.23223.1.1.1': 'EV StartCom Certification Authority',
|
|
||||||
'1.3.6.1.4.1.29836.1.10': 'EV China Internet Network Information Center EV Certificates Root', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.311.60.2.1.1': 'jurisdictionOfIncorporationLocalityName',
|
|
||||||
'1.3.6.1.4.1.311.60.2.1.2': 'jurisdictionOfIncorporationStateOrProvinceName', # noqa: E501
|
|
||||||
'1.3.6.1.4.1.311.60.2.1.3': 'jurisdictionOfIncorporationCountryName',
|
|
||||||
'1.3.6.1.4.1.34697.2.1': 'EV AffirmTrust Commercial',
|
|
||||||
'1.3.6.1.4.1.34697.2.2': 'EV AffirmTrust Networking',
|
|
||||||
'1.3.6.1.4.1.34697.2.3': 'EV AffirmTrust Premium',
|
|
||||||
'1.3.6.1.4.1.34697.2.4': 'EV AffirmTrust Premium ECC',
|
|
||||||
'1.3.6.1.4.1.36305.2': 'EV Certificate Authority of WoSign',
|
|
||||||
'1.3.6.1.4.1.40869.1.1.22.3': 'EV TWCA Roots',
|
|
||||||
'1.3.6.1.4.1.4146.1.1': 'EV GlobalSign Root CAs',
|
|
||||||
'1.3.6.1.4.1.4788.2.202.1': 'EV D-TRUST Root Class 3 CA 2 EV 2009',
|
|
||||||
'1.3.6.1.4.1.6334.1.100.1': 'EV Cybertrust Global Root',
|
|
||||||
'1.3.6.1.4.1.6449.1.2.1.5.1': 'EV USERTrust Certification Authorities',
|
|
||||||
'1.3.6.1.4.1.781.1.2.1.8.1': 'EV Network Solutions Certificate Authority',
|
|
||||||
'1.3.6.1.4.1.782.1.2.1.8.1': 'EV AddTrust External CA Root',
|
|
||||||
'1.3.6.1.4.1.7879.13.24.1': 'EV T-Telessec GlobalRoot Class 3',
|
|
||||||
'1.3.6.1.4.1.8024.0.2.100.1.2': 'EV QuoVadis Roots',
|
|
||||||
'2.16.528.1.1003.1.2.7': 'EV Staat der Nederlanden EV Root CA',
|
|
||||||
'2.16.578.1.26.1.3.3': 'EV Buypass Class 3',
|
|
||||||
'2.16.756.1.83.21.0': 'EV Swisscom Root EV CA 2',
|
|
||||||
'2.16.756.1.89.1.2.1.1': 'EV SwissSign Gold CA - G2',
|
|
||||||
'2.16.792.3.0.4.1.1.4': 'EV E-Tugra Certification Authority',
|
|
||||||
'2.16.840.1.113733.1.7.23.6': 'EV VeriSign Certification Authorities',
|
|
||||||
'2.16.840.1.113733.1.7.48.1': 'EV thawte CAs',
|
|
||||||
'2.16.840.1.114028.10.1.2': 'EV Entrust Certification Authority',
|
|
||||||
'2.16.840.1.114171.500.9': 'EV Wells Fargo WellsSecure Public Root Certification Authority', # noqa: E501
|
|
||||||
'2.16.840.1.114404.1.1.2.4.1': 'EV XRamp Global Certification Authority',
|
|
||||||
'2.16.840.1.114412.2.1': 'EV DigiCert High Assurance EV Root CA',
|
|
||||||
'2.16.840.1.114413.1.7.23.3': 'EV ValiCert Class 2 Policy Validation Authority', # noqa: E501
|
|
||||||
'2.16.840.1.114414.1.7.23.3': 'EV Starfield Certificate Authority',
|
|
||||||
'2.16.840.1.114414.1.7.24.3': 'EV Starfield Service Certificate Authority' # noqa: E501
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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"
|
|
||||||
}
|
|
|
@ -1,665 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# 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 ASN1_Class_UNIVERSAL, ASN1_NULL, ASN1_Error, \
|
|
||||||
ASN1_Object, ASN1_INTEGER
|
|
||||||
from scapy.asn1.ber import BER_tagging_dec, BER_Decoding_Error, BER_id_dec, \
|
|
||||||
BER_tagging_enc
|
|
||||||
from scapy.volatile import RandInt, RandChoice, RandNum, RandString, RandOID, \
|
|
||||||
GeneralizedTime
|
|
||||||
from scapy.compat import orb, raw
|
|
||||||
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)) # noqa: E501
|
|
||||||
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 __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) # noqa: E501
|
|
||||||
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. # noqa: E501
|
|
||||||
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"]:
|
|
||||||
setattr(self, kwarg, kwargs.get(kwarg))
|
|
||||||
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):
|
|
||||||
return all(f.is_empty(pkt) for f in self.seq)
|
|
||||||
|
|
||||||
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:
|
|
||||||
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) # noqa: E501
|
|
||||||
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: # noqa: E501
|
|
||||||
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"]:
|
|
||||||
setattr(self, kwarg, kwargs.get(kwarg))
|
|
||||||
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) # noqa: E501
|
|
||||||
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 # noqa: E501
|
|
||||||
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) # noqa: E501
|
|
||||||
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, # noqa: E501
|
|
||||||
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):
|
|
||||||
s = b"" if x is None else 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
|
|
||||||
return [self.mapping[i] for i, positional in enumerate(fbytes)
|
|
||||||
if positional == '1' and i < len(self.mapping)]
|
|
||||||
|
|
||||||
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)
|
|
|
@ -1,35 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
ASN.1 Packet
|
|
||||||
|
|
||||||
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)
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,203 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Run commands when the Scapy interpreter starts.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import code
|
|
||||||
import sys
|
|
||||||
import importlib
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.themes import NoTheme, DefaultTheme, HTMLTheme2, LatexTheme2
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
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=None):
|
|
||||||
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)
|
|
||||||
if verb is not 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)))
|
|
||||||
|
|
||||||
line = cmds.pop()
|
|
||||||
print(line)
|
|
||||||
cmd += "\n" + line
|
|
||||||
if interp.runsource(cmd):
|
|
||||||
continue
|
|
||||||
if interp.error:
|
|
||||||
return 0
|
|
||||||
cmd = ""
|
|
||||||
if len(cmds) <= 1:
|
|
||||||
break
|
|
||||||
except SystemExit:
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
conf.verb = sv
|
|
||||||
return _ # noqa: F821
|
|
||||||
|
|
||||||
|
|
||||||
class StringWriter(object):
|
|
||||||
"""Util to mock sys.stdout and sys.stderr, and
|
|
||||||
store their output in a 's' var."""
|
|
||||||
def __init__(self, debug=None):
|
|
||||||
self.s = ""
|
|
||||||
self.debug = debug
|
|
||||||
|
|
||||||
def write(self, x):
|
|
||||||
if self.debug:
|
|
||||||
self.debug.write(x)
|
|
||||||
self.s += x
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
if self.debug:
|
|
||||||
self.debug.flush()
|
|
||||||
|
|
||||||
|
|
||||||
def autorun_get_interactive_session(cmds, **kargs):
|
|
||||||
"""Create an interactive session and execute the
|
|
||||||
commands passed as "cmds" and return all output
|
|
||||||
|
|
||||||
:param cmds: a list of commands to run
|
|
||||||
:returns: (output, returned) contains both sys.stdout and sys.stderr logs
|
|
||||||
"""
|
|
||||||
sstdout, sstderr = sys.stdout, sys.stderr
|
|
||||||
sw = StringWriter()
|
|
||||||
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_interactive_live_session(cmds, **kargs):
|
|
||||||
"""Create an interactive session and execute the
|
|
||||||
commands passed as "cmds" and return all output
|
|
||||||
|
|
||||||
:param cmds: a list of commands to run
|
|
||||||
:returns: (output, returned) contains both sys.stdout and sys.stderr logs
|
|
||||||
"""
|
|
||||||
sstdout, sstderr = sys.stdout, sys.stderr
|
|
||||||
sw = StringWriter(debug=sstdout)
|
|
||||||
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_live_interactive_session(cmds, **kargs):
|
|
||||||
ct = conf.color_theme
|
|
||||||
try:
|
|
||||||
conf.color_theme = DefaultTheme()
|
|
||||||
s, res = autorun_get_interactive_live_session(cmds, **kargs)
|
|
||||||
finally:
|
|
||||||
conf.color_theme = ct
|
|
||||||
return s, res
|
|
||||||
|
|
||||||
|
|
||||||
def autorun_get_ansi_interactive_session(cmds, **kargs):
|
|
||||||
ct = conf.color_theme
|
|
||||||
try:
|
|
||||||
conf.color_theme = DefaultTheme()
|
|
||||||
s, res = autorun_get_interactive_session(cmds, **kargs)
|
|
||||||
finally:
|
|
||||||
conf.color_theme = ct
|
|
||||||
return s, res
|
|
||||||
|
|
||||||
|
|
||||||
def autorun_get_html_interactive_session(cmds, **kargs):
|
|
||||||
ct = conf.color_theme
|
|
||||||
to_html = lambda s: s.replace("<", "<").replace(">", ">").replace("#[#", "<").replace("#]#", ">") # noqa: E501
|
|
||||||
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("@`@", "\\") # noqa: E501
|
|
||||||
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
|
|
|
@ -1,360 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Generators and packet meta classes.
|
|
||||||
"""
|
|
||||||
|
|
||||||
################
|
|
||||||
# Generators #
|
|
||||||
################
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
|
|
||||||
from functools import reduce
|
|
||||||
import operator
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import random
|
|
||||||
import socket
|
|
||||||
import subprocess
|
|
||||||
import types
|
|
||||||
|
|
||||||
from scapy.consts import WINDOWS
|
|
||||||
from scapy.modules.six.moves import range
|
|
||||||
|
|
||||||
|
|
||||||
class Gen(object):
|
|
||||||
__slots__ = []
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter([])
|
|
||||||
|
|
||||||
def __iterlen__(self):
|
|
||||||
return sum(1 for _ in iter(self))
|
|
||||||
|
|
||||||
|
|
||||||
def _get_values(value):
|
|
||||||
"""Generate a range object from (start, stop[, step]) tuples, or
|
|
||||||
return value.
|
|
||||||
|
|
||||||
"""
|
|
||||||
if (isinstance(value, tuple) and (2 <= len(value) <= 3) and
|
|
||||||
all(hasattr(i, "__int__") for i in value)):
|
|
||||||
# We use values[1] + 1 as stop value for (x)range to maintain
|
|
||||||
# the behavior of using tuples as field `values`
|
|
||||||
return range(*((int(value[0]), int(value[1]) + 1) +
|
|
||||||
tuple(int(v) for v in value[2:])))
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class SetGen(Gen):
|
|
||||||
def __init__(self, values, _iterpacket=1):
|
|
||||||
self._iterpacket = _iterpacket
|
|
||||||
if isinstance(values, (list, BasePacketList)):
|
|
||||||
self.values = [_get_values(val) for val in values]
|
|
||||||
else:
|
|
||||||
self.values = [_get_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])?$") # noqa: E501
|
|
||||||
|
|
||||||
@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) # noqa: E501
|
|
||||||
else:
|
|
||||||
a = (int(a) & (0xff << netmask), (int(a) | (0xff >> (8 - netmask))) + 1) # noqa: E501
|
|
||||||
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])] # noqa: E501
|
|
||||||
return ret_list, netmask
|
|
||||||
|
|
||||||
def __init__(self, net):
|
|
||||||
self.repr = net
|
|
||||||
self.parsed, self.netmask = self._parse_net(net)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return next(self.__iter__(), 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 __iterlen__(self):
|
|
||||||
return reduce(operator.mul, ((y - x) for (x, y) in self.parsed), 1)
|
|
||||||
|
|
||||||
def choice(self):
|
|
||||||
return ".".join(str(random.randint(v[0], v[1] - 1)) for v in self.parsed) # noqa: E501
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "Net(%r)" % self.repr
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if not other:
|
|
||||||
return False
|
|
||||||
if hasattr(other, "parsed"):
|
|
||||||
p2 = other.parsed
|
|
||||||
else:
|
|
||||||
p2, nm2 = self._parse_net(other)
|
|
||||||
return self.parsed == p2
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
# Python 2.7 compat
|
|
||||||
return not self == other
|
|
||||||
|
|
||||||
__hash__ = None
|
|
||||||
|
|
||||||
def __contains__(self, other):
|
|
||||||
if hasattr(other, "parsed"):
|
|
||||||
p2 = other.parsed
|
|
||||||
else:
|
|
||||||
p2, nm2 = self._parse_net(other)
|
|
||||||
return all(a1 <= a2 and b1 >= b2 for (a1, b1), (a2, b2) in zip(self.parsed, p2)) # noqa: E501
|
|
||||||
|
|
||||||
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):
|
|
||||||
return
|
|
||||||
if ii[i] < self.cmpt[i][1]:
|
|
||||||
ii[i] += 1
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
ii[i] = self.cmpt[i][0]
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
def __iterlen__(self):
|
|
||||||
return reduce(operator.mul, (max(y - x, 0) + 1 for (x, y) in self.cmpt), 1) # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
######################################
|
|
||||||
# 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 # noqa: E501
|
|
||||||
current_fld = dct["fields_desc"]
|
|
||||||
resolved_fld = []
|
|
||||||
for f in current_fld:
|
|
||||||
if isinstance(f, Packet_metaclass): # reference to another fields_desc # noqa: E501
|
|
||||||
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
|
|
||||||
|
|
||||||
dct.setdefault("__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__
|
|
||||||
)
|
|
||||||
|
|
||||||
newcls.aliastypes = [newcls] + getattr(newcls, "aliastypes", [])
|
|
||||||
|
|
||||||
if hasattr(newcls, "register_variant"):
|
|
||||||
newcls.register_variant()
|
|
||||||
for f in newcls.fields_desc:
|
|
||||||
if hasattr(f, "register_owner"):
|
|
||||||
f.register_owner(newcls)
|
|
||||||
if newcls.__name__[0] != "_":
|
|
||||||
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 Exception:
|
|
||||||
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):
|
|
||||||
dct.setdefault("__slots__", [])
|
|
||||||
newcls = super(Field_metaclass, cls).__new__(cls, name, bases, dct)
|
|
||||||
return newcls
|
|
||||||
|
|
||||||
|
|
||||||
class BasePacket(Gen):
|
|
||||||
__slots__ = []
|
|
||||||
|
|
||||||
|
|
||||||
#############################
|
|
||||||
# Packet list base class #
|
|
||||||
#############################
|
|
||||||
|
|
||||||
class BasePacketList(object):
|
|
||||||
__slots__ = []
|
|
||||||
|
|
||||||
|
|
||||||
class _CanvasDumpExtended(object):
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.utils import get_temp_file, ContextManagerSubprocess
|
|
||||||
canvas = self.canvas_dump(**kargs)
|
|
||||||
if filename is None:
|
|
||||||
fname = get_temp_file(autoext=kargs.get("suffix", ".eps"))
|
|
||||||
canvas.writeEPSfile(fname)
|
|
||||||
if WINDOWS and conf.prog.psreader is None:
|
|
||||||
os.startfile(fname)
|
|
||||||
else:
|
|
||||||
with ContextManagerSubprocess(conf.prog.psreader):
|
|
||||||
subprocess.Popen([conf.prog.psreader, fname])
|
|
||||||
else:
|
|
||||||
canvas.writeEPSfile(filename)
|
|
||||||
print()
|
|
||||||
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.utils import get_temp_file, ContextManagerSubprocess
|
|
||||||
canvas = self.canvas_dump(**kargs)
|
|
||||||
if filename is None:
|
|
||||||
fname = get_temp_file(autoext=kargs.get("suffix", ".pdf"))
|
|
||||||
canvas.writePDFfile(fname)
|
|
||||||
if WINDOWS and conf.prog.pdfreader is None:
|
|
||||||
os.startfile(fname)
|
|
||||||
else:
|
|
||||||
with ContextManagerSubprocess(conf.prog.pdfreader):
|
|
||||||
subprocess.Popen([conf.prog.pdfreader, fname])
|
|
||||||
else:
|
|
||||||
canvas.writePDFfile(filename)
|
|
||||||
print()
|
|
||||||
|
|
||||||
def svgdump(self, filename=None, **kargs):
|
|
||||||
"""
|
|
||||||
svgdump(filename=None, layer_shift=0, rebuild=1)
|
|
||||||
|
|
||||||
Creates an SVG file describing a packet. If filename is not provided a
|
|
||||||
temporary file is created and gs is called.
|
|
||||||
|
|
||||||
:param filename: the file's filename
|
|
||||||
"""
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.utils import get_temp_file, ContextManagerSubprocess
|
|
||||||
canvas = self.canvas_dump(**kargs)
|
|
||||||
if filename is None:
|
|
||||||
fname = get_temp_file(autoext=kargs.get("suffix", ".svg"))
|
|
||||||
canvas.writeSVGfile(fname)
|
|
||||||
if WINDOWS and conf.prog.svgreader is None:
|
|
||||||
os.startfile(fname)
|
|
||||||
else:
|
|
||||||
with ContextManagerSubprocess(conf.prog.svgreader):
|
|
||||||
subprocess.Popen([conf.prog.svgreader, fname])
|
|
||||||
else:
|
|
||||||
canvas.writeSVGfile(filename)
|
|
||||||
print()
|
|
|
@ -1,169 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# 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 gzip
|
|
||||||
import struct
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import scapy.modules.six as six
|
|
||||||
|
|
||||||
|
|
||||||
###########
|
|
||||||
# Python3 #
|
|
||||||
###########
|
|
||||||
|
|
||||||
|
|
||||||
def lambda_tuple_converter(func):
|
|
||||||
"""
|
|
||||||
Converts a Python 2 function as
|
|
||||||
lambda (x,y): x + y
|
|
||||||
In the Python 3 format:
|
|
||||||
lambda x,y : x + y
|
|
||||||
"""
|
|
||||||
if func is not None and func.__code__.co_argcount == 1:
|
|
||||||
return lambda *args: func(args[0] if len(args) == 1 else args)
|
|
||||||
else:
|
|
||||||
return func
|
|
||||||
|
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
bytes_encode = plain_str = str
|
|
||||||
chb = lambda x: x if isinstance(x, str) else chr(x)
|
|
||||||
orb = ord
|
|
||||||
|
|
||||||
|
|
||||||
def raw(x):
|
|
||||||
"""Builds a packet and returns its bytes representation.
|
|
||||||
This function is and always be cross-version compatible"""
|
|
||||||
if hasattr(x, "__bytes__"):
|
|
||||||
return x.__bytes__()
|
|
||||||
return bytes(x)
|
|
||||||
else:
|
|
||||||
def raw(x):
|
|
||||||
"""Builds a packet and returns its bytes representation.
|
|
||||||
This function is and always be cross-version compatible"""
|
|
||||||
return x.__bytes__()
|
|
||||||
|
|
||||||
|
|
||||||
def bytes_encode(x):
|
|
||||||
"""Ensure that the given object is bytes.
|
|
||||||
If the parameter is a packet, raw() should be preferred.
|
|
||||||
"""
|
|
||||||
if isinstance(x, str):
|
|
||||||
return x.encode()
|
|
||||||
return bytes(x)
|
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info[0:2] <= (3, 4):
|
|
||||||
def plain_str(x):
|
|
||||||
"""Convert basic byte objects to str"""
|
|
||||||
if isinstance(x, bytes):
|
|
||||||
return x.decode(errors="ignore")
|
|
||||||
return str(x)
|
|
||||||
else:
|
|
||||||
# Python 3.5+
|
|
||||||
def plain_str(x):
|
|
||||||
"""Convert basic byte objects to str"""
|
|
||||||
if isinstance(x, bytes):
|
|
||||||
return x.decode(errors="backslashreplace")
|
|
||||||
return str(x)
|
|
||||||
|
|
||||||
|
|
||||||
def chb(x):
|
|
||||||
"""Same than chr() but encode as bytes."""
|
|
||||||
return struct.pack("!B", x)
|
|
||||||
|
|
||||||
|
|
||||||
def orb(x):
|
|
||||||
"""Return ord(x) when not already an int."""
|
|
||||||
if isinstance(x, int):
|
|
||||||
return x
|
|
||||||
return ord(x)
|
|
||||||
|
|
||||||
|
|
||||||
def bytes_hex(x):
|
|
||||||
"""Hexify a str or a bytes object"""
|
|
||||||
return binascii.b2a_hex(bytes_encode(x))
|
|
||||||
|
|
||||||
|
|
||||||
def hex_bytes(x):
|
|
||||||
"""De-hexify a str or a byte object"""
|
|
||||||
return binascii.a2b_hex(bytes_encode(x))
|
|
||||||
|
|
||||||
|
|
||||||
def base64_bytes(x):
|
|
||||||
"""Turn base64 into bytes"""
|
|
||||||
if six.PY2:
|
|
||||||
return base64.decodestring(x)
|
|
||||||
return base64.decodebytes(bytes_encode(x))
|
|
||||||
|
|
||||||
|
|
||||||
def bytes_base64(x):
|
|
||||||
"""Turn bytes into base64"""
|
|
||||||
if six.PY2:
|
|
||||||
return base64.encodestring(x).replace('\n', '')
|
|
||||||
return base64.encodebytes(bytes_encode(x)).replace(b'\n', b'')
|
|
||||||
|
|
||||||
|
|
||||||
if six.PY2:
|
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
|
|
||||||
def gzip_decompress(x):
|
|
||||||
"""Decompress using gzip"""
|
|
||||||
with gzip.GzipFile(fileobj=StringIO(x), mode='rb') as fdesc:
|
|
||||||
return fdesc.read()
|
|
||||||
|
|
||||||
|
|
||||||
def gzip_compress(x):
|
|
||||||
"""Compress using gzip"""
|
|
||||||
buf = StringIO()
|
|
||||||
with gzip.GzipFile(fileobj=buf, mode='wb') as fdesc:
|
|
||||||
fdesc.write(x)
|
|
||||||
return buf.getvalue()
|
|
||||||
else:
|
|
||||||
gzip_decompress = gzip.decompress
|
|
||||||
gzip_compress = gzip.compress
|
|
||||||
|
|
||||||
# Typing compatibility
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Only required if using mypy-lang for static typing
|
|
||||||
from typing import Optional, List, Union, Callable, Any, AnyStr, Tuple, \
|
|
||||||
Sized, Dict, Pattern, cast
|
|
||||||
except ImportError:
|
|
||||||
# Let's make some fake ones.
|
|
||||||
|
|
||||||
def cast(_type, obj):
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
class _FakeType(object):
|
|
||||||
# make the objects subscriptable indefinetly
|
|
||||||
def __getitem__(self, item):
|
|
||||||
return _FakeType()
|
|
||||||
|
|
||||||
|
|
||||||
Optional = _FakeType()
|
|
||||||
Union = _FakeType()
|
|
||||||
Callable = _FakeType()
|
|
||||||
List = _FakeType()
|
|
||||||
Dict = _FakeType()
|
|
||||||
Any = _FakeType()
|
|
||||||
AnyStr = _FakeType()
|
|
||||||
Tuple = _FakeType()
|
|
||||||
Pattern = _FakeType()
|
|
||||||
|
|
||||||
|
|
||||||
class Sized(object):
|
|
||||||
pass
|
|
|
@ -1,707 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Implementation of the configuration object.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
from __future__ import print_function
|
|
||||||
import functools
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import time
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
import atexit
|
|
||||||
|
|
||||||
from scapy import VERSION, base_classes
|
|
||||||
from scapy.consts import DARWIN, WINDOWS, LINUX, BSD, SOLARIS
|
|
||||||
from scapy.error import log_scapy, warning, ScapyInvalidPlatformException
|
|
||||||
from scapy.modules import six
|
|
||||||
from scapy.themes import NoTheme, apply_ipython_style
|
|
||||||
|
|
||||||
############
|
|
||||||
# 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=None, default=None,
|
|
||||||
hook=None, 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)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def set_from_hook(obj, name, val):
|
|
||||||
int_name = "_intercepted_%s" % name
|
|
||||||
setattr(obj, int_name, val)
|
|
||||||
|
|
||||||
def __set__(self, obj, val):
|
|
||||||
setattr(obj, self.intname, val)
|
|
||||||
self.hook(self.name, val, *self.args, **self.kargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _readonly(name):
|
|
||||||
default = Conf.__dict__[name].default
|
|
||||||
Interceptor.set_from_hook(conf, name, default)
|
|
||||||
raise ValueError("Read-only value !")
|
|
||||||
|
|
||||||
|
|
||||||
ReadOnlyAttribute = functools.partial(
|
|
||||||
Interceptor,
|
|
||||||
hook=(lambda name, *args, **kwargs: _readonly(name))
|
|
||||||
)
|
|
||||||
ReadOnlyAttribute.__doc__ = "Read-only class attribute"
|
|
||||||
|
|
||||||
|
|
||||||
class ProgPath(ConfClass):
|
|
||||||
universal_open = "open" if DARWIN else "xdg-open"
|
|
||||||
pdfreader = universal_open
|
|
||||||
psreader = universal_open
|
|
||||||
svgreader = universal_open
|
|
||||||
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)) # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
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):
|
|
||||||
return self[item] if item in self else 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 __init__(self):
|
|
||||||
list.__init__(self)
|
|
||||||
self.ldict = {}
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "\n".join("%-20s: %s" % (l.__name__, l.name) for l in self)
|
|
||||||
|
|
||||||
def register(self, layer):
|
|
||||||
self.append(layer)
|
|
||||||
if layer.__module__ not in self.ldict:
|
|
||||||
self.ldict[layer.__module__] = []
|
|
||||||
self.ldict[layer.__module__].append(layer)
|
|
||||||
|
|
||||||
def layers(self):
|
|
||||||
result = []
|
|
||||||
# This import may feel useless, but it is required for the eval below
|
|
||||||
import scapy # noqa: F401
|
|
||||||
for lay in self.ldict:
|
|
||||||
doc = eval(lay).__doc__
|
|
||||||
result.append((lay, doc.strip().split("\n")[0] if doc else lay))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
class CommandsList(list):
|
|
||||||
def __repr__(self):
|
|
||||||
s = []
|
|
||||||
for l in sorted(self, key=lambda x: x.__name__):
|
|
||||||
doc = l.__doc__.split("\n")[0] if l.__doc__ else "--"
|
|
||||||
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():
|
|
||||||
"""Displays Scapy's default commands"""
|
|
||||||
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 six.iteritems(other):
|
|
||||||
# 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) # noqa: E501
|
|
||||||
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
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] # noqa: E501
|
|
||||||
|
|
||||||
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] # noqa: E501
|
|
||||||
|
|
||||||
def values(self):
|
|
||||||
if self.timeout is None:
|
|
||||||
return list(six.itervalues(self))
|
|
||||||
t0 = time.time()
|
|
||||||
return [v for (k, v) in six.iteritems(self.__dict__) if t0 - self._timetable[k] < self.timeout] # noqa: E501
|
|
||||||
|
|
||||||
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) # noqa: E501
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
def _version_checker(module, minver):
|
|
||||||
"""Checks that module has a higher version that minver.
|
|
||||||
|
|
||||||
params:
|
|
||||||
- module: a module to test
|
|
||||||
- minver: a tuple of versions
|
|
||||||
"""
|
|
||||||
# We could use LooseVersion, but distutils imports imp which is deprecated
|
|
||||||
version_regexp = r'[a-z]?((?:\d|\.)+\d+)(?:\.dev[0-9]+)?'
|
|
||||||
version_tags = re.match(version_regexp, module.__version__)
|
|
||||||
if not version_tags:
|
|
||||||
return False
|
|
||||||
version_tags = version_tags.group(1).split(".")
|
|
||||||
version_tags = tuple(int(x) for x in version_tags)
|
|
||||||
return version_tags >= minver
|
|
||||||
|
|
||||||
|
|
||||||
def isCryptographyValid():
|
|
||||||
"""
|
|
||||||
Check if the cryptography module >= 2.0.0 is present. This is the minimum
|
|
||||||
version for most usages in Scapy.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
import cryptography
|
|
||||||
except ImportError:
|
|
||||||
return False
|
|
||||||
return _version_checker(cryptography, (2, 0, 0))
|
|
||||||
|
|
||||||
|
|
||||||
def isCryptographyAdvanced():
|
|
||||||
"""
|
|
||||||
Check if the cryptography module is present, and if it supports X25519,
|
|
||||||
ChaCha20Poly1305 and such.
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
- cryptography >= 2.0 is required
|
|
||||||
- OpenSSL >= 1.1.0 is required
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey # noqa: E501
|
|
||||||
X25519PrivateKey.generate()
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def isPyPy():
|
|
||||||
"""Returns either scapy is running under PyPy or not"""
|
|
||||||
try:
|
|
||||||
import __pypy__ # noqa: F401
|
|
||||||
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 Exception:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
apply_ipython_style(get_ipython())
|
|
||||||
except NameError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _set_conf_sockets():
|
|
||||||
"""Populate the conf.L2Socket and conf.L3Socket
|
|
||||||
according to the various use_* parameters
|
|
||||||
"""
|
|
||||||
from scapy.main import _load
|
|
||||||
if conf.use_bpf and not BSD:
|
|
||||||
Interceptor.set_from_hook(conf, "use_bpf", False)
|
|
||||||
raise ScapyInvalidPlatformException("BSD-like (OSX, *BSD...) only !")
|
|
||||||
if not conf.use_pcap and SOLARIS:
|
|
||||||
Interceptor.set_from_hook(conf, "use_pcap", True)
|
|
||||||
raise ScapyInvalidPlatformException(
|
|
||||||
"Scapy only supports libpcap on Solaris !"
|
|
||||||
)
|
|
||||||
# we are already in an Interceptor hook, use Interceptor.set_from_hook
|
|
||||||
if conf.use_pcap:
|
|
||||||
try:
|
|
||||||
from scapy.arch.pcapdnet import L2pcapListenSocket, L2pcapSocket, \
|
|
||||||
L3pcapSocket
|
|
||||||
except (OSError, ImportError):
|
|
||||||
warning("No libpcap provider available ! pcap won't be used")
|
|
||||||
Interceptor.set_from_hook(conf, "use_pcap", False)
|
|
||||||
else:
|
|
||||||
conf.L3socket = L3pcapSocket
|
|
||||||
conf.L3socket6 = functools.partial(L3pcapSocket, filter="ip6")
|
|
||||||
conf.L2socket = L2pcapSocket
|
|
||||||
conf.L2listen = L2pcapListenSocket
|
|
||||||
# Update globals
|
|
||||||
_load("scapy.arch.pcapdnet")
|
|
||||||
return
|
|
||||||
if conf.use_bpf:
|
|
||||||
from scapy.arch.bpf.supersocket import L2bpfListenSocket, \
|
|
||||||
L2bpfSocket, L3bpfSocket
|
|
||||||
conf.L3socket = L3bpfSocket
|
|
||||||
conf.L3socket6 = functools.partial(L3bpfSocket, filter="ip6")
|
|
||||||
conf.L2socket = L2bpfSocket
|
|
||||||
conf.L2listen = L2bpfListenSocket
|
|
||||||
# Update globals
|
|
||||||
_load("scapy.arch.bpf")
|
|
||||||
return
|
|
||||||
if LINUX:
|
|
||||||
from scapy.arch.linux import L3PacketSocket, L2Socket, L2ListenSocket
|
|
||||||
conf.L3socket = L3PacketSocket
|
|
||||||
conf.L3socket6 = functools.partial(L3PacketSocket, filter="ip6")
|
|
||||||
conf.L2socket = L2Socket
|
|
||||||
conf.L2listen = L2ListenSocket
|
|
||||||
# Update globals
|
|
||||||
_load("scapy.arch.linux")
|
|
||||||
return
|
|
||||||
if WINDOWS:
|
|
||||||
from scapy.arch.windows import _NotAvailableSocket
|
|
||||||
from scapy.arch.windows.native import L3WinSocket, L3WinSocket6
|
|
||||||
conf.L3socket = L3WinSocket
|
|
||||||
conf.L3socket6 = L3WinSocket6
|
|
||||||
conf.L2socket = _NotAvailableSocket
|
|
||||||
conf.L2listen = _NotAvailableSocket
|
|
||||||
# No need to update globals on Windows
|
|
||||||
return
|
|
||||||
from scapy.supersocket import L3RawSocket
|
|
||||||
from scapy.layers.inet6 import L3RawSocket6
|
|
||||||
conf.L3socket = L3RawSocket
|
|
||||||
conf.L3socket6 = L3RawSocket6
|
|
||||||
|
|
||||||
|
|
||||||
def _socket_changer(attr, val):
|
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise TypeError("This argument should be a boolean")
|
|
||||||
dependencies = { # Things that will be turned off
|
|
||||||
"use_pcap": ["use_bpf"],
|
|
||||||
"use_bpf": ["use_pcap"],
|
|
||||||
}
|
|
||||||
restore = {k: getattr(conf, k) for k in dependencies}
|
|
||||||
del restore[attr] # This is handled directly by _set_conf_sockets
|
|
||||||
if val: # Only if True
|
|
||||||
for param in dependencies[attr]:
|
|
||||||
Interceptor.set_from_hook(conf, param, False)
|
|
||||||
try:
|
|
||||||
_set_conf_sockets()
|
|
||||||
except (ScapyInvalidPlatformException, ImportError) as e:
|
|
||||||
for key, value in restore.items():
|
|
||||||
Interceptor.set_from_hook(conf, key, value)
|
|
||||||
if isinstance(e, ScapyInvalidPlatformException):
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def _loglevel_changer(attr, val):
|
|
||||||
"""Handle a change of conf.logLevel"""
|
|
||||||
log_scapy.setLevel(val)
|
|
||||||
|
|
||||||
|
|
||||||
class Conf(ConfClass):
|
|
||||||
"""
|
|
||||||
This object contains the configuration of Scapy.
|
|
||||||
"""
|
|
||||||
version = ReadOnlyAttribute("version", VERSION)
|
|
||||||
session = "" #: filename where the session will be saved
|
|
||||||
interactive = False
|
|
||||||
#: can be "ipython", "python" or "auto". Default: Auto
|
|
||||||
interactive_shell = ""
|
|
||||||
#: if 1, prevents any unwanted packet to go out (ARP, DNS, ...)
|
|
||||||
stealth = "not implemented"
|
|
||||||
#: selects the default output interface for srp() and sendp().
|
|
||||||
iface = None
|
|
||||||
layers = LayersList()
|
|
||||||
commands = CommandsList()
|
|
||||||
ASN1_default_codec = None #: Codec used by default for ASN1 objects
|
|
||||||
AS_resolver = None #: choose the AS resolver class to use
|
|
||||||
dot15d4_protocol = None # Used in dot15d4.py
|
|
||||||
logLevel = Interceptor("logLevel", log_scapy.level, _loglevel_changer)
|
|
||||||
#: 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
|
|
||||||
checkIPID = False
|
|
||||||
#: if 1, checks IP src in IP and ICMP IP citation match
|
|
||||||
#: (bug in some NAT stacks)
|
|
||||||
checkIPsrc = True
|
|
||||||
checkIPaddr = True
|
|
||||||
#: if True, checks that IP-in-IP layers match. If False, do
|
|
||||||
#: not check IP layers that encapsulates another IP layer
|
|
||||||
checkIPinIP = True
|
|
||||||
#: if 1, also check that TCP seq and ack match the
|
|
||||||
#: ones in ICMP citation
|
|
||||||
check_TCPerror_seqack = False
|
|
||||||
verb = 2 #: level of verbosity, from 0 (almost mute) to 3 (verbose)
|
|
||||||
prompt = Interceptor("prompt", ">>> ", _prompt_changer)
|
|
||||||
#: default mode for listening socket (to get answers if you
|
|
||||||
#: spoof on a lan)
|
|
||||||
promisc = True
|
|
||||||
sniff_promisc = 1 #: default mode for sniff()
|
|
||||||
raw_layer = None
|
|
||||||
raw_summary = False
|
|
||||||
default_l2 = None
|
|
||||||
l2types = Num2Layer()
|
|
||||||
l3types = Num2Layer()
|
|
||||||
L3socket = None
|
|
||||||
L3socket6 = None
|
|
||||||
L2socket = None
|
|
||||||
L2listen = None
|
|
||||||
BTsocket = None
|
|
||||||
USBsocket = None
|
|
||||||
min_pkt_size = 60
|
|
||||||
mib = None #: holds MIB direct access dictionary
|
|
||||||
bufsize = 2**16
|
|
||||||
#: history file
|
|
||||||
histfile = os.getenv('SCAPY_HISTFILE',
|
|
||||||
os.path.join(os.path.expanduser("~"),
|
|
||||||
".scapy_history"))
|
|
||||||
#: includes padding in disassembled packets
|
|
||||||
padding = 1
|
|
||||||
#: BPF filter for packets to ignore
|
|
||||||
except_filter = ""
|
|
||||||
#: bpf filter added to every sniffing socket to exclude traffic
|
|
||||||
#: from analysis
|
|
||||||
filter = ""
|
|
||||||
#: when 1, store received packet that are not matched into `debug.recv`
|
|
||||||
debug_match = False
|
|
||||||
#: When 1, print some TLS session secrets when they are computed.
|
|
||||||
debug_tls = False
|
|
||||||
wepkey = ""
|
|
||||||
cache_iflist = {}
|
|
||||||
#: holds the Scapy IPv4 routing table and provides methods to
|
|
||||||
#: manipulate it
|
|
||||||
route = None # Filed by route.py
|
|
||||||
#: holds the Scapy IPv6 routing table and provides methods to
|
|
||||||
#: manipulate it
|
|
||||||
route6 = None # Filed by route6.py
|
|
||||||
auto_fragment = True
|
|
||||||
#: raise exception when a packet dissector raises an exception
|
|
||||||
debug_dissector = False
|
|
||||||
color_theme = Interceptor("color_theme", NoTheme(), _prompt_changer)
|
|
||||||
#: how much time between warnings from the same place
|
|
||||||
warning_threshold = 5
|
|
||||||
prog = ProgPath()
|
|
||||||
#: holds list of fields for which resolution should be done
|
|
||||||
resolve = Resolve()
|
|
||||||
#: holds list of enum fields for which conversion to string
|
|
||||||
#: should NOT be done
|
|
||||||
noenum = Resolve()
|
|
||||||
emph = Emphasize()
|
|
||||||
#: read only attribute to show if PyPy is in use
|
|
||||||
use_pypy = ReadOnlyAttribute("use_pypy", isPyPy())
|
|
||||||
#: use libpcap integration or not. Changing this value will update
|
|
||||||
#: the conf.L[2/3] sockets
|
|
||||||
use_pcap = Interceptor(
|
|
||||||
"use_pcap",
|
|
||||||
os.getenv("SCAPY_USE_PCAPDNET", "").lower().startswith("y"),
|
|
||||||
_socket_changer
|
|
||||||
)
|
|
||||||
use_bpf = Interceptor("use_bpf", False, _socket_changer)
|
|
||||||
use_npcap = False
|
|
||||||
ipv6_enabled = socket.has_ipv6
|
|
||||||
#: path or list of paths where extensions are to be looked for
|
|
||||||
extensions_paths = "."
|
|
||||||
stats_classic_protocols = []
|
|
||||||
stats_dot11_protocols = []
|
|
||||||
temp_files = []
|
|
||||||
netcache = NetCache()
|
|
||||||
geoip_city = None
|
|
||||||
# can, tls, http are not loaded by default
|
|
||||||
load_layers = ['bluetooth', 'bluetooth4LE', 'dhcp', 'dhcp6', 'dns',
|
|
||||||
'dot11', 'dot15d4', 'eap', 'gprs', 'hsrp', 'inet',
|
|
||||||
'inet6', 'ipsec', 'ir', 'isakmp', 'l2', 'l2tp',
|
|
||||||
'llmnr', 'lltd', 'mgcp', 'mobileip', 'netbios',
|
|
||||||
'netflow', 'ntp', 'ppi', 'ppp', 'pptp', 'radius', 'rip',
|
|
||||||
'rtp', 'sctp', 'sixlowpan', 'skinny', 'smb', 'smb2', 'snmp',
|
|
||||||
'tftp', 'vrrp', 'vxlan', 'x509', 'zigbee']
|
|
||||||
#: a dict which can be used by contrib layers to store local
|
|
||||||
#: configuration
|
|
||||||
contribs = dict()
|
|
||||||
crypto_valid = isCryptographyValid()
|
|
||||||
crypto_valid_advanced = isCryptographyAdvanced()
|
|
||||||
fancy_prompt = True
|
|
||||||
auto_crop_tables = True
|
|
||||||
#: how often to check for new packets.
|
|
||||||
#: Defaults to 0.05s.
|
|
||||||
recv_poll_rate = 0.05
|
|
||||||
#: When True, raise exception if no dst MAC found otherwise broadcast.
|
|
||||||
#: Default is False.
|
|
||||||
raise_no_dst_mac = False
|
|
||||||
loopback_name = "lo" if LINUX else "lo0"
|
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
# Those are loaded on runtime to avoid import loops
|
|
||||||
if attr == "manufdb":
|
|
||||||
from scapy.data import MANUFDB
|
|
||||||
return MANUFDB
|
|
||||||
if attr == "ethertypes":
|
|
||||||
from scapy.data import ETHER_TYPES
|
|
||||||
return ETHER_TYPES
|
|
||||||
if attr == "protocols":
|
|
||||||
from scapy.data import IP_PROTOS
|
|
||||||
return IP_PROTOS
|
|
||||||
if attr == "services_udp":
|
|
||||||
from scapy.data import UDP_SERVICES
|
|
||||||
return UDP_SERVICES
|
|
||||||
if attr == "services_tcp":
|
|
||||||
from scapy.data import TCP_SERVICES
|
|
||||||
return TCP_SERVICES
|
|
||||||
if attr == "iface6":
|
|
||||||
warning("conf.iface6 is deprecated in favor of conf.iface")
|
|
||||||
attr = "iface"
|
|
||||||
return object.__getattribute__(self, attr)
|
|
||||||
|
|
||||||
|
|
||||||
if not Conf.ipv6_enabled:
|
|
||||||
log_scapy.warning("IPv6 support disabled in Python. Cannot load Scapy IPv6 layers.") # noqa: E501
|
|
||||||
for m in ["inet6", "dhcp6"]:
|
|
||||||
if m in Conf.load_layers:
|
|
||||||
Conf.load_layers.remove(m)
|
|
||||||
|
|
||||||
conf = Conf()
|
|
||||||
|
|
||||||
|
|
||||||
def crypto_validator(func):
|
|
||||||
"""
|
|
||||||
This a decorator to be used for any method relying on the cryptography library. # noqa: E501
|
|
||||||
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.") # noqa: E501
|
|
||||||
return func(*args, **kwargs)
|
|
||||||
return func_in
|
|
||||||
|
|
||||||
|
|
||||||
def scapy_delete_temp_files():
|
|
||||||
# type: () -> None
|
|
||||||
for f in conf.temp_files:
|
|
||||||
try:
|
|
||||||
os.unlink(f)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
del conf.temp_files[:]
|
|
||||||
|
|
||||||
|
|
||||||
atexit.register(scapy_delete_temp_files)
|
|
|
@ -1,20 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
from sys import platform, maxsize
|
|
||||||
import platform as platform_lib
|
|
||||||
|
|
||||||
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")
|
|
||||||
WINDOWS_XP = platform_lib.release() == "XP"
|
|
||||||
BSD = DARWIN or FREEBSD or OPENBSD or NETBSD
|
|
||||||
# See https://docs.python.org/3/library/platform.html#cross-platform
|
|
||||||
IS_64BITS = maxsize > 2**32
|
|
||||||
# LOOPBACK_NAME moved to conf.loopback_name
|
|
|
@ -1,8 +0,0 @@
|
||||||
# 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
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib modules that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,82 +0,0 @@
|
||||||
# -*- mode: python3; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
||||||
# altbeacon.py - protocol handlers for AltBeacon
|
|
||||||
#
|
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Michael Farrell <micolous+git@gmail.com>
|
|
||||||
# This program is published under a GPLv2 (or later) license
|
|
||||||
#
|
|
||||||
# scapy.contrib.description = AltBeacon BLE proximity beacon
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
"""
|
|
||||||
scapy.contrib.altbeacon - AltBeacon Bluetooth LE proximity beacons.
|
|
||||||
|
|
||||||
The AltBeacon specification can be found at: https://github.com/AltBeacon/spec
|
|
||||||
"""
|
|
||||||
|
|
||||||
from scapy.fields import ByteField, ShortField, SignedByteField, \
|
|
||||||
StrFixedLenField
|
|
||||||
from scapy.layers.bluetooth import EIR_Hdr, EIR_Manufacturer_Specific_Data, \
|
|
||||||
UUIDField, LowEnergyBeaconHelper
|
|
||||||
from scapy.packet import Packet
|
|
||||||
|
|
||||||
|
|
||||||
# When building beacon frames, one should use their own manufacturer ID.
|
|
||||||
#
|
|
||||||
# However, most software (including the AltBeacon SDK) requires explicitly
|
|
||||||
# registering particular manufacturer IDs to listen to, and the only ID used is
|
|
||||||
# that of Radius Networks (the developer of the specification).
|
|
||||||
#
|
|
||||||
# To maximise compatibility, Scapy's implementation of
|
|
||||||
# LowEnergyBeaconHelper.build_eir (for constructing frames) uses Radius
|
|
||||||
# Networks' manufacturer ID.
|
|
||||||
#
|
|
||||||
# Scapy's implementation of AltBeacon **does not** require a specific
|
|
||||||
# manufacturer ID to detect AltBeacons - it uses
|
|
||||||
# EIR_Manufacturer_Specific_Data.register_magic_payload.
|
|
||||||
RADIUS_NETWORKS_MFG = 0x0118
|
|
||||||
|
|
||||||
|
|
||||||
class AltBeacon(Packet, LowEnergyBeaconHelper):
|
|
||||||
"""
|
|
||||||
AltBeacon broadcast frame type.
|
|
||||||
|
|
||||||
https://github.com/AltBeacon/spec
|
|
||||||
"""
|
|
||||||
name = "AltBeacon"
|
|
||||||
magic = b"\xBE\xAC"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField("header", magic, len(magic)),
|
|
||||||
|
|
||||||
# The spec says this is 20 bytes, with >=16 bytes being an
|
|
||||||
# organisational unit-specific identifier. However, the Android library
|
|
||||||
# treats this as UUID + uint16 + uint16.
|
|
||||||
UUIDField("id1", None),
|
|
||||||
|
|
||||||
# Local identifier
|
|
||||||
ShortField("id2", None),
|
|
||||||
ShortField("id3", None),
|
|
||||||
|
|
||||||
SignedByteField("tx_power", None),
|
|
||||||
ByteField("mfg_reserved", None),
|
|
||||||
]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def magic_check(cls, payload):
|
|
||||||
"""
|
|
||||||
Checks if the given payload is for us (starts with our magic string).
|
|
||||||
"""
|
|
||||||
return payload.startswith(cls.magic)
|
|
||||||
|
|
||||||
def build_eir(self):
|
|
||||||
"""Builds a list of EIR messages to wrap this frame."""
|
|
||||||
|
|
||||||
# Note: Company ID is not required by spec, but most tools only look
|
|
||||||
# for manufacturer-specific data with Radius Networks' manufacturer ID.
|
|
||||||
return LowEnergyBeaconHelper.base_eir + [
|
|
||||||
EIR_Hdr() / EIR_Manufacturer_Specific_Data(
|
|
||||||
company_id=RADIUS_NETWORKS_MFG) / self
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
EIR_Manufacturer_Specific_Data.register_magic_payload(AltBeacon)
|
|
|
@ -1,94 +0,0 @@
|
||||||
% AltBeacon unit tests
|
|
||||||
#
|
|
||||||
# Type the following command to launch start the tests:
|
|
||||||
# $ test/run_tests -P "load_contrib('altbeacon')" -t scapy/contrib/altbeacon.uts
|
|
||||||
#
|
|
||||||
# AltBeaconParser tests adapted from:
|
|
||||||
# https://github.com/AltBeacon/android-beacon-library/blob/master/lib/src/test/java/org/altbeacon/beacon/AltBeaconParserTest.java
|
|
||||||
|
|
||||||
+ AltBeacon tests
|
|
||||||
|
|
||||||
= Setup
|
|
||||||
|
|
||||||
def next_eir(p):
|
|
||||||
return EIR_Hdr(p[Padding])
|
|
||||||
|
|
||||||
= Presence check
|
|
||||||
|
|
||||||
AltBeacon
|
|
||||||
|
|
||||||
= AltBeaconParserTest.testRecognizeBeacon
|
|
||||||
|
|
||||||
d = hex_bytes('02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c509')
|
|
||||||
p = EIR_Hdr(d)
|
|
||||||
|
|
||||||
# First is a flags header
|
|
||||||
assert EIR_Flags in p
|
|
||||||
|
|
||||||
# Then the AltBeacon
|
|
||||||
p = next_eir(p)
|
|
||||||
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
|
|
||||||
assert p[AltBeacon].mfg_reserved == 9
|
|
||||||
|
|
||||||
|
|
||||||
= AltBeaconParserTest.testDetectsDaveMHardwareBeacon
|
|
||||||
|
|
||||||
d = hex_bytes('02011a1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600050003be020e09526164426561636f6e20555342020a03000000000000000000000000')
|
|
||||||
p = EIR_Hdr(d)
|
|
||||||
|
|
||||||
# First is Flags
|
|
||||||
assert EIR_Flags in p
|
|
||||||
|
|
||||||
# Then the AltBeacon
|
|
||||||
p = next_eir(p)
|
|
||||||
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
|
|
||||||
assert AltBeacon in p
|
|
||||||
|
|
||||||
# Then CompleteLocalName
|
|
||||||
p = next_eir(p)
|
|
||||||
assert p[EIR_CompleteLocalName].local_name == b'RadBeacon USB'
|
|
||||||
|
|
||||||
# Then TX_Power_Level
|
|
||||||
p = next_eir(p)
|
|
||||||
assert p[EIR_TX_Power_Level].level == 3
|
|
||||||
|
|
||||||
= AltBeaconParserTest.testParseWrongFormatReturnsNothing
|
|
||||||
|
|
||||||
d = hex_bytes('02011a1aff1801ffff2f234454cf6d4a0fadf2f4911ba9ffa600010002c509')
|
|
||||||
p = EIR_Hdr(d)
|
|
||||||
|
|
||||||
# First is Flags
|
|
||||||
assert EIR_Flags in p
|
|
||||||
|
|
||||||
# Then the EIR_Manufacturer_Specific_Data
|
|
||||||
p = next_eir(p)
|
|
||||||
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
|
|
||||||
assert AltBeacon not in p
|
|
||||||
|
|
||||||
= AltBeaconParserTest.testParsesBeaconMissingDataField
|
|
||||||
|
|
||||||
d = hex_bytes('02011a1aff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c50000')
|
|
||||||
p = EIR_Hdr(d)
|
|
||||||
|
|
||||||
# First is Flags
|
|
||||||
assert EIR_Flags in p
|
|
||||||
|
|
||||||
# Then the EIR_Manufacturer_Specific_Data
|
|
||||||
p = next_eir(p)
|
|
||||||
assert p[EIR_Manufacturer_Specific_Data].company_id == RADIUS_NETWORKS_MFG
|
|
||||||
assert p[AltBeacon].id1 == uuid.UUID('2f234454-cf6d-4a0f-adf2-f4911ba9ffa6')
|
|
||||||
assert p[AltBeacon].id2 == 1
|
|
||||||
assert p[AltBeacon].id3 == 2
|
|
||||||
assert p[AltBeacon].tx_power == -59
|
|
||||||
|
|
||||||
= Build EIR
|
|
||||||
|
|
||||||
p = AltBeacon(
|
|
||||||
id1=uuid.UUID('2f234454-cf6d-4a0f-adf2-f4911ba9ffa6'),
|
|
||||||
id2=1,
|
|
||||||
id3=2,
|
|
||||||
tx_power=-59,
|
|
||||||
)
|
|
||||||
|
|
||||||
d = raw(p.build_eir()[-1])
|
|
||||||
assert d == hex_bytes('1bff1801beac2f234454cf6d4a0fadf2f4911ba9ffa600010002c500')
|
|
|
@ -1,137 +0,0 @@
|
||||||
# Copyright (C) 2018 antoine.torre <torreantoine1@gmail.com>
|
|
||||||
##
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
|
|
||||||
# scapy.contrib.description = ATA Over Internet
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.fields import FlagsField, XByteField, ByteField, XShortField, \
|
|
||||||
ShortField, StrLenField, BitField, BitEnumField, ByteEnumField, \
|
|
||||||
FieldLenField, PacketListField, FieldListField, MACField, PacketField, \
|
|
||||||
ConditionalField, XIntField
|
|
||||||
from scapy.layers.l2 import Ether
|
|
||||||
from scapy.data import ETHER_ANY
|
|
||||||
|
|
||||||
|
|
||||||
class IssueATACommand(Packet):
|
|
||||||
name = "Issue ATA Command"
|
|
||||||
fields_desc = [FlagsField("flags", 0, 8, "zezdzzaw"),
|
|
||||||
XByteField("err_feature", 0),
|
|
||||||
ByteField("sector_count", 1),
|
|
||||||
XByteField("cmd_status", 0xec),
|
|
||||||
XByteField("lba0", 0),
|
|
||||||
XByteField("lba1", 0),
|
|
||||||
XByteField("lba2", 0),
|
|
||||||
XByteField("lba3", 0),
|
|
||||||
XByteField("lba4", 0),
|
|
||||||
XByteField("lba5", 0),
|
|
||||||
XShortField("reserved", 0),
|
|
||||||
StrLenField("data", "",
|
|
||||||
length_from=lambda x: x.sector_count * 512)]
|
|
||||||
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return "", s
|
|
||||||
|
|
||||||
|
|
||||||
class QueryConfigInformation(Packet):
|
|
||||||
name = "Query Config Information"
|
|
||||||
fields_desc = [ShortField("buffer_count", 0),
|
|
||||||
ShortField("firmware", 0),
|
|
||||||
ByteField("sector_count", 0),
|
|
||||||
BitField("aoe", 0, 4),
|
|
||||||
BitEnumField("ccmd", 0, 4, {0: "Read config string",
|
|
||||||
1: "Test config string",
|
|
||||||
2: "Test config string prefix",
|
|
||||||
3: "Set config string",
|
|
||||||
4: "Force set config string"}),
|
|
||||||
FieldLenField("config_length", None, length_of="config"),
|
|
||||||
StrLenField("config", None,
|
|
||||||
length_from=lambda x: x.config_length)]
|
|
||||||
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return "", s
|
|
||||||
|
|
||||||
|
|
||||||
class Directive(Packet):
|
|
||||||
name = "Directive"
|
|
||||||
fields_desc = [ByteField("reserved", 0),
|
|
||||||
ByteEnumField("dcmd", 0,
|
|
||||||
{0: "No directive",
|
|
||||||
1: "Add mac address to mask list",
|
|
||||||
2: "Delete mac address from mask list"}),
|
|
||||||
MACField("mac_addr", ETHER_ANY)]
|
|
||||||
|
|
||||||
|
|
||||||
class MacMaskList(Packet):
|
|
||||||
name = "Mac Mask List"
|
|
||||||
fields_desc = [ByteField("reserved", 0),
|
|
||||||
ByteEnumField("mcmd", 0, {0: "Read Mac Mask List",
|
|
||||||
1: "Edit Mac Mask List"}),
|
|
||||||
ByteEnumField("merror", 0, {0: "",
|
|
||||||
1: "Unspecified error",
|
|
||||||
2: "Bad dcmd directive",
|
|
||||||
3: "Mask List Full"}),
|
|
||||||
FieldLenField("dir_count", None, count_of="directives"),
|
|
||||||
PacketListField("directives", None, Directive,
|
|
||||||
count_from=lambda pkt: pkt.dir_count)]
|
|
||||||
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return "", s
|
|
||||||
|
|
||||||
|
|
||||||
class ReserveRelease(Packet):
|
|
||||||
name = "Reserve / Release"
|
|
||||||
fields_desc = [ByteEnumField("rcmd", 0, {0: "Read Reserve List",
|
|
||||||
1: "Set Reserve List",
|
|
||||||
2: "Force Set Reserve List"}),
|
|
||||||
FieldLenField("nb_mac", None, count_of="mac_addrs"),
|
|
||||||
FieldListField("mac_addrs", None, MACField("", ETHER_ANY),
|
|
||||||
count_from=lambda pkt: pkt.nb_mac)]
|
|
||||||
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return "", s
|
|
||||||
|
|
||||||
|
|
||||||
class AOE(Packet):
|
|
||||||
name = "ATA over Ethernet"
|
|
||||||
fields_desc = [BitField("version", 1, 4),
|
|
||||||
FlagsField("flags", 0, 4, ["Response", "Error",
|
|
||||||
"r1", "r2"]),
|
|
||||||
ByteEnumField("error", 0, {1: "Unrecognized command code",
|
|
||||||
2: "Bad argument parameter",
|
|
||||||
3: "Device unavailable",
|
|
||||||
4: "Config string present",
|
|
||||||
5: "Unsupported exception",
|
|
||||||
6: "Target is reserved"}),
|
|
||||||
XShortField("major", 0xFFFF),
|
|
||||||
XByteField("minor", 0xFF),
|
|
||||||
ByteEnumField("cmd", 1, {0: "Issue ATA Command",
|
|
||||||
1: "Query Config Information",
|
|
||||||
2: "Mac Mask List",
|
|
||||||
3: "Reserve / Release"}),
|
|
||||||
XIntField("tag", 0),
|
|
||||||
ConditionalField(PacketField("i_ata_cmd", IssueATACommand(),
|
|
||||||
IssueATACommand),
|
|
||||||
lambda x: x.cmd == 0),
|
|
||||||
ConditionalField(PacketField("q_conf_info",
|
|
||||||
QueryConfigInformation(),
|
|
||||||
QueryConfigInformation),
|
|
||||||
lambda x: x.cmd == 1),
|
|
||||||
ConditionalField(PacketField("mac_m_list", MacMaskList(),
|
|
||||||
MacMaskList),
|
|
||||||
lambda x: x.cmd == 2),
|
|
||||||
ConditionalField(PacketField("res_rel", ReserveRelease(),
|
|
||||||
ReserveRelease),
|
|
||||||
lambda x: x.cmd == 3)]
|
|
||||||
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return "", s
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(Ether, AOE, type=0x88A2)
|
|
||||||
bind_layers(AOE, IssueATACommand, cmd=0)
|
|
||||||
bind_layers(AOE, QueryConfigInformation, cmd=1)
|
|
||||||
bind_layers(AOE, MacMaskList, cmd=2)
|
|
||||||
bind_layers(AOE, ReserveRelease, cmd=3)
|
|
|
@ -1,44 +0,0 @@
|
||||||
% Regression tests for aoe module
|
|
||||||
############
|
|
||||||
############
|
|
||||||
+ Basic tests
|
|
||||||
|
|
||||||
= Build - Check Ethertype
|
|
||||||
|
|
||||||
a = Ether(src="00:01:02:03:04:05")
|
|
||||||
b = AOE()
|
|
||||||
c = a / b
|
|
||||||
assert(c[Ether].type == 0x88a2)
|
|
||||||
|
|
||||||
= Build - Check default
|
|
||||||
|
|
||||||
p = AOE()
|
|
||||||
assert(hasattr(p, "q_conf_info"))
|
|
||||||
|
|
||||||
= Build - Check Issue ATA command
|
|
||||||
|
|
||||||
p = AOE()
|
|
||||||
p.cmd = 0
|
|
||||||
|
|
||||||
assert(hasattr(p, "i_ata_cmd"))
|
|
||||||
|
|
||||||
= Build - Check Query Config Information
|
|
||||||
|
|
||||||
p = AOE()
|
|
||||||
p.cmd = 1
|
|
||||||
|
|
||||||
assert(hasattr(p, "q_conf_info"))
|
|
||||||
|
|
||||||
= Build - Check Mac Mask List
|
|
||||||
|
|
||||||
p = AOE()
|
|
||||||
p.cmd = 2
|
|
||||||
|
|
||||||
assert(hasattr(p, "mac_m_list"))
|
|
||||||
|
|
||||||
= Build - Check ReserveRelease
|
|
||||||
|
|
||||||
p = AOE()
|
|
||||||
p.cmd = 3
|
|
||||||
|
|
||||||
assert(hasattr(p, "res_rel"))
|
|
|
@ -1,10 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive modules that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,11 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive bmw specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,95 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = ENET - BMW diagnostic protocol over Ethernet
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import struct
|
|
||||||
import socket
|
|
||||||
from scapy.packet import Packet, bind_layers, bind_bottom_up
|
|
||||||
from scapy.fields import IntField, ShortEnumField, XByteField
|
|
||||||
from scapy.layers.inet import TCP
|
|
||||||
from scapy.supersocket import StreamSocket
|
|
||||||
from scapy.contrib.automotive.uds import UDS
|
|
||||||
from scapy.contrib.isotp import ISOTP
|
|
||||||
from scapy.error import Scapy_Exception
|
|
||||||
from scapy.data import MTU
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
BMW specific diagnostic over IP protocol implementation ENET
|
|
||||||
"""
|
|
||||||
|
|
||||||
# #########################ENET###################################
|
|
||||||
|
|
||||||
|
|
||||||
class ENET(Packet):
|
|
||||||
name = 'ENET'
|
|
||||||
fields_desc = [
|
|
||||||
IntField('length', None),
|
|
||||||
ShortEnumField('type', 1, {0x01: "message",
|
|
||||||
0x02: "echo"}),
|
|
||||||
XByteField('src', 0),
|
|
||||||
XByteField('dst', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
def hashret(self):
|
|
||||||
hdr_hash = struct.pack("B", self.src ^ self.dst)
|
|
||||||
pay_hash = self.payload.hashret()
|
|
||||||
return hdr_hash + pay_hash
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
if other.__class__ == self.__class__:
|
|
||||||
return self.payload.answers(other.payload)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return s[:self.length - 2], s[self.length - 2:]
|
|
||||||
|
|
||||||
def post_build(self, pkt, pay):
|
|
||||||
"""
|
|
||||||
This will set the LenField 'length' to the correct value.
|
|
||||||
"""
|
|
||||||
if self.length is None:
|
|
||||||
pkt = struct.pack("!I", len(pay) + 2) + pkt[4:]
|
|
||||||
return pkt + pay
|
|
||||||
|
|
||||||
|
|
||||||
bind_bottom_up(TCP, ENET, sport=6801)
|
|
||||||
bind_bottom_up(TCP, ENET, dport=6801)
|
|
||||||
bind_layers(TCP, ENET, sport=6801, dport=6801)
|
|
||||||
bind_layers(ENET, UDS)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################ENETSocket###################################
|
|
||||||
|
|
||||||
|
|
||||||
class ENETSocket(StreamSocket):
|
|
||||||
def __init__(self, ip='127.0.0.1', port=6801):
|
|
||||||
self.ip = ip
|
|
||||||
self.port = port
|
|
||||||
s = socket.socket()
|
|
||||||
s.connect((self.ip, self.port))
|
|
||||||
StreamSocket.__init__(self, s, ENET)
|
|
||||||
|
|
||||||
|
|
||||||
class ISOTP_ENETSocket(ENETSocket):
|
|
||||||
def __init__(self, src, dst, ip='127.0.0.1', port=6801, basecls=ISOTP):
|
|
||||||
super(ISOTP_ENETSocket, self).__init__(ip, port)
|
|
||||||
self.src = src
|
|
||||||
self.dst = dst
|
|
||||||
self.basecls = ENET
|
|
||||||
self.outputcls = basecls
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
if not isinstance(x, ISOTP):
|
|
||||||
raise Scapy_Exception("Please provide a packet class based on "
|
|
||||||
"ISOTP")
|
|
||||||
super(ISOTP_ENETSocket, self).send(
|
|
||||||
ENET(src=self.src, dst=self.dst) / x)
|
|
||||||
|
|
||||||
def recv(self, x=MTU):
|
|
||||||
pkt = super(ISOTP_ENETSocket, self).recv(x)
|
|
||||||
return self.outputcls(bytes(pkt[1]))
|
|
|
@ -1,69 +0,0 @@
|
||||||
+ ENET Contrib tests
|
|
||||||
|
|
||||||
= Load Contrib Layer
|
|
||||||
|
|
||||||
load_contrib("automotive.bmw.enet")
|
|
||||||
|
|
||||||
= Basic Test 1
|
|
||||||
|
|
||||||
pkt = ENET(type=1, src=0xf4, dst=0x10)/Raw(b'\x11\x22\x33')
|
|
||||||
assert bytes(pkt) == b'\x00\x00\x00\x05\x00\x01\xf4\x10\x11"3'
|
|
||||||
|
|
||||||
= Basic Test 2
|
|
||||||
|
|
||||||
pkt = ENET(type=1, src=0xf4, dst=0x10)/Raw(b'\x11\x22\x33\x11\x11\x11\x11\x11')
|
|
||||||
assert bytes(pkt) == b'\x00\x00\x00\x0a\x00\x01\xf4\x10\x11"3\x11\x11\x11\x11\x11'
|
|
||||||
|
|
||||||
= Basic Dissect Test
|
|
||||||
|
|
||||||
pkt = ENET(b'\x00\x00\x00\x0a\x00\x01\xf4\x10\x11"3\x11\x11\x11\x11\x11')
|
|
||||||
assert pkt.length == 10
|
|
||||||
assert pkt.src == 0xf4
|
|
||||||
assert pkt.dst == 0x10
|
|
||||||
assert pkt.type == 1
|
|
||||||
assert pkt[1].service == 17
|
|
||||||
assert pkt[2].resetType == 34
|
|
||||||
|
|
||||||
= Build Test
|
|
||||||
|
|
||||||
pkt = ENET(src=0xf4, dst=0x10)/Raw(b"0" * 20)
|
|
||||||
assert bytes(pkt) == b'\x00\x00\x00\x16\x00\x01\xf4\x10' + b"0" * 20
|
|
||||||
|
|
||||||
= Dissect Test
|
|
||||||
|
|
||||||
pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 20)
|
|
||||||
assert pkt.length == 24
|
|
||||||
assert pkt.src == 0xf4
|
|
||||||
assert pkt.dst == 0x10
|
|
||||||
assert pkt.type == 1
|
|
||||||
assert pkt.securitySeed == b"0" * 20
|
|
||||||
assert len(pkt[1]) == pkt.length - 2
|
|
||||||
|
|
||||||
= Dissect Test with padding
|
|
||||||
|
|
||||||
pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 20 + b"p" * 100)
|
|
||||||
assert pkt.length == 24
|
|
||||||
assert pkt.src == 0xf4
|
|
||||||
assert pkt.dst == 0x10
|
|
||||||
assert pkt.type == 1
|
|
||||||
assert pkt.securitySeed == b"0" * 20
|
|
||||||
assert pkt.load == b'p' * 100
|
|
||||||
|
|
||||||
= Dissect Test to short packet
|
|
||||||
|
|
||||||
pkt = ENET(b'\x00\x00\x00\x18\x00\x01\xf4\x10\x67\x01' + b"0" * 19)
|
|
||||||
assert pkt.length == 24
|
|
||||||
assert pkt.src == 0xf4
|
|
||||||
assert pkt.dst == 0x10
|
|
||||||
assert pkt.type == 1
|
|
||||||
assert pkt.securitySeed == b"0" * 19
|
|
||||||
|
|
||||||
= Dissect Test very long packet
|
|
||||||
|
|
||||||
pkt = ENET(b'\x00\x0f\xff\x04\x00\x01\xf4\x10\x67\x01' + b"0" * 0xfff00)
|
|
||||||
assert pkt.length == 0xfff04
|
|
||||||
assert pkt.src == 0xf4
|
|
||||||
assert pkt.dst == 0x10
|
|
||||||
assert pkt.type == 1
|
|
||||||
assert pkt.securitySeed == b"0" * 0xfff00
|
|
||||||
|
|
|
@ -1,588 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = CAN Calibration Protocol (CCP)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers, bind_bottom_up
|
|
||||||
from scapy.fields import XIntField, FlagsField, ByteEnumField, \
|
|
||||||
ThreeBytesField, XBitField, ShortField, IntField, XShortField, \
|
|
||||||
ByteField, XByteField, StrFixedLenField, LEShortField
|
|
||||||
from scapy.layers.can import CAN
|
|
||||||
|
|
||||||
|
|
||||||
class CCP(CAN):
|
|
||||||
name = 'CAN Calibration Protocol'
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('flags', 0, 3, ['error',
|
|
||||||
'remote_transmission_request',
|
|
||||||
'extended']),
|
|
||||||
XBitField('identifier', 0, 29),
|
|
||||||
ByteField('length', 8),
|
|
||||||
ThreeBytesField('reserved', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
def extract_padding(self, p):
|
|
||||||
return p, None
|
|
||||||
|
|
||||||
|
|
||||||
class CRO(Packet):
|
|
||||||
commands = {
|
|
||||||
0x01: "CONNECT",
|
|
||||||
0x1B: "GET_CCP_VERSION",
|
|
||||||
0x17: "EXCHANGE_ID",
|
|
||||||
0x12: "GET_SEED",
|
|
||||||
0x13: "UNLOCK",
|
|
||||||
0x02: "SET_MTA",
|
|
||||||
0x03: "DNLOAD",
|
|
||||||
0x23: "DNLOAD_6",
|
|
||||||
0x04: "UPLOAD",
|
|
||||||
0x0F: "SHORT_UP",
|
|
||||||
0x11: "SELECT_CAL_PAGE",
|
|
||||||
0x14: "GET_DAQ_SIZE",
|
|
||||||
0x15: "SET_DAQ_PTR",
|
|
||||||
0x16: "WRITE_DAQ",
|
|
||||||
0x06: "START_STOP",
|
|
||||||
0x07: "DISCONNECT",
|
|
||||||
0x0C: "SET_S_STATUS",
|
|
||||||
0x0D: "GET_S_STATUS",
|
|
||||||
0x0E: "BUILD_CHKSUM",
|
|
||||||
0x10: "CLEAR_MEMORY",
|
|
||||||
0x18: "PROGRAM",
|
|
||||||
0x22: "PROGRAM_6",
|
|
||||||
0x19: "MOVE",
|
|
||||||
0x05: "TEST",
|
|
||||||
0x09: "GET_ACTIVE_CAL_PAGE",
|
|
||||||
0x08: "START_STOP_ALL",
|
|
||||||
0x20: "DIAG_SERVICE",
|
|
||||||
0x21: "ACTION_SERVICE"
|
|
||||||
}
|
|
||||||
name = 'Command Receive Object'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('cmd', 0x01, commands),
|
|
||||||
ByteField('ctr', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
def hashret(self):
|
|
||||||
return struct.pack('B', self.ctr)
|
|
||||||
|
|
||||||
|
|
||||||
# ##### CROs ######
|
|
||||||
|
|
||||||
class CONNECT(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
LEShortField('station_address', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, CONNECT, cmd=0x01)
|
|
||||||
|
|
||||||
|
|
||||||
class GET_CCP_VERSION(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('main_protocol_version', 0),
|
|
||||||
XByteField('release_version', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, GET_CCP_VERSION, cmd=0x1B)
|
|
||||||
|
|
||||||
|
|
||||||
class EXCHANGE_ID(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('ccp_master_device_id', b'\x00' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, EXCHANGE_ID, cmd=0x17)
|
|
||||||
|
|
||||||
|
|
||||||
class GET_SEED(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('resource', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, GET_SEED, cmd=0x12)
|
|
||||||
|
|
||||||
|
|
||||||
class UNLOCK(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('key', b'\x00' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, UNLOCK, cmd=0x13)
|
|
||||||
|
|
||||||
|
|
||||||
class SET_MTA(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('mta_num', 0),
|
|
||||||
XByteField('address_extension', 0),
|
|
||||||
XIntField('address', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, SET_MTA, cmd=0x02)
|
|
||||||
|
|
||||||
|
|
||||||
class DNLOAD(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('size', 0),
|
|
||||||
StrFixedLenField('data', b'\x00' * 5, length=5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, DNLOAD, cmd=0x03)
|
|
||||||
|
|
||||||
|
|
||||||
class DNLOAD_6(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'\x00' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, DNLOAD_6, cmd=0x23)
|
|
||||||
|
|
||||||
|
|
||||||
class UPLOAD(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('size', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, UPLOAD, cmd=0x04)
|
|
||||||
|
|
||||||
|
|
||||||
class SHORT_UP(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('size', 0),
|
|
||||||
XByteField('address_extension', 0),
|
|
||||||
XIntField('address', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, SHORT_UP, cmd=0x0F)
|
|
||||||
|
|
||||||
|
|
||||||
class SELECT_CAL_PAGE(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, SELECT_CAL_PAGE, cmd=0x11)
|
|
||||||
|
|
||||||
|
|
||||||
class GET_DAQ_SIZE(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DAQ_num', 0),
|
|
||||||
XByteField('ccp_reserved', 0),
|
|
||||||
XIntField('DTO_identifier', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, GET_DAQ_SIZE, cmd=0x14)
|
|
||||||
|
|
||||||
|
|
||||||
class SET_DAQ_PTR(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DAQ_num', 0),
|
|
||||||
XByteField('ODT_num', 0),
|
|
||||||
XByteField('ODT_element', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, SET_DAQ_PTR, cmd=0x15)
|
|
||||||
|
|
||||||
|
|
||||||
class WRITE_DAQ(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DAQ_size', 0),
|
|
||||||
XByteField('address_extension', 0),
|
|
||||||
XIntField('address', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, WRITE_DAQ, cmd=0x16)
|
|
||||||
|
|
||||||
|
|
||||||
class START_STOP(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('mode', 0),
|
|
||||||
XByteField('DAQ_num', 0),
|
|
||||||
XByteField('ODT_num', 0),
|
|
||||||
XByteField('event_channel', 0),
|
|
||||||
XShortField('transmission_rate', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, START_STOP, cmd=0x06)
|
|
||||||
|
|
||||||
|
|
||||||
class DISCONNECT(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('type', 0, {0: "temporary", 1: "end_of_session"}),
|
|
||||||
StrFixedLenField('ccp_reserved0', b'\xff' * 1, length=1),
|
|
||||||
LEShortField('station_address', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, DISCONNECT, cmd=0x07)
|
|
||||||
|
|
||||||
|
|
||||||
class SET_S_STATUS(Packet):
|
|
||||||
name = "Set Session Status"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField("session_status", 0, 8, ["CAL", "DAQ", "RESUME", "RES0",
|
|
||||||
"RES1", "RES2", "STORE", "RUN"]),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, SET_S_STATUS, cmd=0x0C)
|
|
||||||
|
|
||||||
|
|
||||||
class GET_S_STATUS(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, GET_S_STATUS, cmd=0x0D)
|
|
||||||
|
|
||||||
|
|
||||||
class BUILD_CHKSUM(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
IntField('size', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, BUILD_CHKSUM, cmd=0x0E)
|
|
||||||
|
|
||||||
|
|
||||||
class CLEAR_MEMORY(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
IntField('size', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, CLEAR_MEMORY, cmd=0x10)
|
|
||||||
|
|
||||||
|
|
||||||
class PROGRAM(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('size', 0),
|
|
||||||
StrFixedLenField('data', b'\x00' * 0,
|
|
||||||
length_from=lambda pkt: pkt.size),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 5,
|
|
||||||
length_from=lambda pkt: 5 - pkt.size)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, PROGRAM, cmd=0x18)
|
|
||||||
|
|
||||||
|
|
||||||
class PROGRAM_6(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'\x00' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, PROGRAM_6, cmd=0x22)
|
|
||||||
|
|
||||||
|
|
||||||
class MOVE(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
IntField('size', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 2, length=2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, MOVE, cmd=0x19)
|
|
||||||
|
|
||||||
|
|
||||||
class TEST(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
LEShortField('station_address', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, TEST, cmd=0x05)
|
|
||||||
|
|
||||||
|
|
||||||
class GET_ACTIVE_CAL_PAGE(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 6, length=6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, GET_ACTIVE_CAL_PAGE, cmd=0x09)
|
|
||||||
|
|
||||||
|
|
||||||
class START_STOP_ALL(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('type', 0, {0: "stop", 1: "start"}),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 5, length=5)
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, START_STOP_ALL, cmd=0x08)
|
|
||||||
|
|
||||||
|
|
||||||
class DIAG_SERVICE(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ShortField('diag_service', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, DIAG_SERVICE, cmd=0x20)
|
|
||||||
|
|
||||||
|
|
||||||
class ACTION_SERVICE(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ShortField('action_service', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(CRO, ACTION_SERVICE, cmd=0x21)
|
|
||||||
|
|
||||||
|
|
||||||
# ##### DTOs ######
|
|
||||||
|
|
||||||
class DEFAULT_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('load', b'\xff' * 5, length=5),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class GET_CCP_VERSION_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('main_protocol_version', 0),
|
|
||||||
XByteField('release_version', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\x00' * 3, length=3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class EXCHANGE_ID_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('slave_device_ID_length', 0),
|
|
||||||
ByteField('data_type_qualifier', 0),
|
|
||||||
ByteField('resource_availability_mask', 0),
|
|
||||||
ByteField('resource_protection_mask', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 1, length=1),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class GET_SEED_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('protection_status', 0),
|
|
||||||
StrFixedLenField('seed', b'\x00' * 4, length=4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class UNLOCK_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('privilege_status', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 4, length=4),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class DNLOAD_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('MTA0_extension', 0),
|
|
||||||
XIntField('MTA0_address', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class DNLOAD_6_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('MTA0_extension', 0),
|
|
||||||
XIntField('MTA0_address', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class UPLOAD_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'\x00' * 5, length=5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SHORT_UP_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'\x00' * 5, length=5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class GET_DAQ_SIZE_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DAQ_list_size', 0),
|
|
||||||
XByteField('first_pid', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class GET_S_STATUS_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField("session_status", 0, 8, ["CAL", "DAQ", "RESUME", "RES0",
|
|
||||||
"RES1", "RES2", "STORE", "RUN"]),
|
|
||||||
ByteField('information_qualifier', 0),
|
|
||||||
StrFixedLenField('information', b'\x00' * 3, length=3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class BUILD_CHKSUM_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('checksum_size', 0),
|
|
||||||
StrFixedLenField('checksum_data', b'\x00' * 4,
|
|
||||||
length_from=lambda pkt: pkt.checksum_size),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 0,
|
|
||||||
length_from=lambda pkt: 4 - pkt.checksum_size)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PROGRAM_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('MTA0_extension', 0),
|
|
||||||
XIntField('MTA0_address', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PROGRAM_6_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('MTA0_extension', 0),
|
|
||||||
XIntField('MTA0_address', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class GET_ACTIVE_CAL_PAGE_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('address_extension', 0),
|
|
||||||
XIntField('address', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class DIAG_SERVICE_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('data_length', 0),
|
|
||||||
ByteField('data_type', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ACTION_SERVICE_DTO(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('data_length', 0),
|
|
||||||
ByteField('data_type', 0),
|
|
||||||
StrFixedLenField('ccp_reserved', b'\xff' * 3, length=3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class DTO(Packet):
|
|
||||||
__slots__ = Packet.__slots__ + ["payload_cls"]
|
|
||||||
|
|
||||||
return_codes = {
|
|
||||||
0x00: "acknowledge / no error",
|
|
||||||
0x01: "DAQ processor overload",
|
|
||||||
0x10: "command processor busy",
|
|
||||||
0x11: "DAQ processor busy",
|
|
||||||
0x12: "internal timeout",
|
|
||||||
0x18: "key request",
|
|
||||||
0x19: "session status request",
|
|
||||||
0x20: "cold start request",
|
|
||||||
0x21: "cal. data init. request",
|
|
||||||
0x22: "DAQ list init. request",
|
|
||||||
0x23: "code update request",
|
|
||||||
0x30: "unknown command",
|
|
||||||
0x31: "command syntax",
|
|
||||||
0x32: "parameter(s) out of range",
|
|
||||||
0x33: "access denied",
|
|
||||||
0x34: "overload",
|
|
||||||
0x35: "access locked",
|
|
||||||
0x36: "resource/function not available"
|
|
||||||
}
|
|
||||||
fields_desc = [
|
|
||||||
XByteField("packet_id", 0xff),
|
|
||||||
ByteEnumField('return_code', 0x00, return_codes),
|
|
||||||
ByteField('ctr', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.payload_cls = DEFAULT_DTO
|
|
||||||
if "payload_cls" in kwargs:
|
|
||||||
self.payload_cls = kwargs["payload_cls"]
|
|
||||||
del kwargs["payload_cls"]
|
|
||||||
Packet.__init__(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def guess_payload_class(self, payload):
|
|
||||||
return self.payload_cls
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_dto_cls(cmd):
|
|
||||||
try:
|
|
||||||
return {
|
|
||||||
0x03: DNLOAD_DTO,
|
|
||||||
0x04: UPLOAD_DTO,
|
|
||||||
0x09: GET_ACTIVE_CAL_PAGE_DTO,
|
|
||||||
0x0D: GET_S_STATUS_DTO,
|
|
||||||
0x0E: BUILD_CHKSUM_DTO,
|
|
||||||
0x0F: SHORT_UP_DTO,
|
|
||||||
0x12: GET_SEED_DTO,
|
|
||||||
0x13: UNLOCK_DTO,
|
|
||||||
0x14: GET_DAQ_SIZE_DTO,
|
|
||||||
0x17: EXCHANGE_ID_DTO,
|
|
||||||
0x18: PROGRAM_DTO,
|
|
||||||
0x1B: GET_CCP_VERSION_DTO,
|
|
||||||
0x20: DIAG_SERVICE_DTO,
|
|
||||||
0x21: ACTION_SERVICE_DTO,
|
|
||||||
0x22: PROGRAM_6_DTO,
|
|
||||||
0x23: DNLOAD_6_DTO
|
|
||||||
}[cmd]
|
|
||||||
except KeyError:
|
|
||||||
return DEFAULT_DTO
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
"""In CCP, the payload of a DTO packet is dependent on the cmd field
|
|
||||||
of a corresponding CRO packet. Two packets correspond, if there
|
|
||||||
ctr field is equal. If answers detect the corresponding CRO, it will
|
|
||||||
interpret the payload of a DTO with the correct class. In CCP, there is
|
|
||||||
no other way, to determine the class of a DTO payload. Since answers is
|
|
||||||
called on sr and sr1, this modification of the original answers
|
|
||||||
implementation will give a better user experience. """
|
|
||||||
if not hasattr(other, "ctr"):
|
|
||||||
return 0
|
|
||||||
if self.ctr != other.ctr:
|
|
||||||
return 0
|
|
||||||
if not hasattr(other, "cmd"):
|
|
||||||
return 0
|
|
||||||
|
|
||||||
new_pl_cls = self.get_dto_cls(other.cmd)
|
|
||||||
if self.payload_cls != new_pl_cls and \
|
|
||||||
self.payload_cls == DEFAULT_DTO:
|
|
||||||
data = bytes(self.load)
|
|
||||||
self.remove_payload()
|
|
||||||
self.add_payload(new_pl_cls(data))
|
|
||||||
self.payload_cls = new_pl_cls
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def hashret(self):
|
|
||||||
return struct.pack('B', self.ctr)
|
|
||||||
|
|
||||||
|
|
||||||
bind_bottom_up(CCP, DTO)
|
|
|
@ -1,998 +0,0 @@
|
||||||
% Regression tests for the CCP layer
|
|
||||||
|
|
||||||
# More information at http://www.secdev.org/projects/UTscapy/
|
|
||||||
|
|
||||||
|
|
||||||
############
|
|
||||||
############
|
|
||||||
|
|
||||||
+ Basic operations
|
|
||||||
|
|
||||||
= Load module
|
|
||||||
|
|
||||||
load_contrib("automotive.ccp")
|
|
||||||
|
|
||||||
= Build CRO CONNECT
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=1)/CONNECT(station_address=0x02)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 1
|
|
||||||
assert cro.cmd == 1
|
|
||||||
assert cro.station_address == 0x02
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x01\x01\x02\x00\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO CONNECT
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x01\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.identifier == 0x700
|
|
||||||
assert dto.length == 8
|
|
||||||
assert dto.flags == 0
|
|
||||||
assert dto.ctr == 1
|
|
||||||
assert dto.load == b"\xff" * 5
|
|
||||||
|
|
||||||
= Build CRO EXCHANGE_ID
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=18)/EXCHANGE_ID(ccp_master_device_id=b'abcdef')
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 18
|
|
||||||
assert cro.cmd == 0x17
|
|
||||||
assert cro.ccp_master_device_id == b"abcdef"
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x17\x12abcdef'
|
|
||||||
|
|
||||||
= Dissect DTO EXCHANGE_ID
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x12\x04\x02\x03\x03\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 18
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x04\x02\x03\x03\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 18
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.slave_device_ID_length == 4
|
|
||||||
assert dto.data_type_qualifier == 2
|
|
||||||
assert dto.resource_availability_mask == 3
|
|
||||||
assert dto.resource_protection_mask == 3
|
|
||||||
assert dto.ccp_reserved == b"\xff"
|
|
||||||
|
|
||||||
= Build CRO GET_SEED
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x711)/CRO(ctr=19)/GET_SEED(resource=2)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x711
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 19
|
|
||||||
assert cro.cmd == 0x12
|
|
||||||
assert cro.resource == 2
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 5
|
|
||||||
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x12\x13\x02\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO GET_SEED
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x13\x01\x14\x15\x16\x17')
|
|
||||||
|
|
||||||
assert dto.ctr == 19
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x01\x14\x15\x16\x17'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 19
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.protection_status == 0x1
|
|
||||||
assert dto.seed == b'\x14\x15\x16\x17'
|
|
||||||
|
|
||||||
= Build CRO UNLOCK
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x711)/CRO(ctr=20)/UNLOCK(key=b"123456")
|
|
||||||
|
|
||||||
assert cro.identifier == 0x711
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 20
|
|
||||||
assert cro.cmd == 0x13
|
|
||||||
assert cro.key == b"123456"
|
|
||||||
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x13\x14123456'
|
|
||||||
|
|
||||||
= Dissect DTO UNLOCK
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x14\x02\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 20
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x02\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 20
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.privilege_status == 0x2
|
|
||||||
assert dto.ccp_reserved == b"\xff" * 4
|
|
||||||
|
|
||||||
= Build CRO SET_MTA
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x711)/CRO(ctr=21)/SET_MTA(mta_num=0, address_extension=0x02, address=0x34002000)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x711
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 21
|
|
||||||
assert cro.cmd == 0x02
|
|
||||||
assert cro.mta_num == 0
|
|
||||||
assert cro.address_extension == 2
|
|
||||||
assert cro.address == 0x34002000
|
|
||||||
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x11\x08\x00\x00\x00\x02\x15\x00\x02\x34\x00\x20\x00'
|
|
||||||
|
|
||||||
= Dissect DTO SET_MTA
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x11\x08\x00\x00\x00\xff\x00\x15\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 21
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 21
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b"\xff" * 5
|
|
||||||
|
|
||||||
= Build CRO DNLOAD
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=17)/DNLOAD(size=0x05, data=b'abcde')
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 17
|
|
||||||
assert cro.cmd == 3
|
|
||||||
assert cro.size == 0x05
|
|
||||||
assert cro.data == b'abcde'
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x03\x11\x05abcde'
|
|
||||||
|
|
||||||
= Dissect DTO DNLOAD
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x11\x02\x34\x00\x20\x05')
|
|
||||||
|
|
||||||
assert dto.ctr == 17
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x024\x00 \x05'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 17
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.MTA0_extension == 2
|
|
||||||
assert dto.MTA0_address == 0x34002005
|
|
||||||
|
|
||||||
= Build CRO DNLOAD_6
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x40)/DNLOAD_6(data=b'abcdef')
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x40
|
|
||||||
assert cro.cmd == 0x23
|
|
||||||
assert cro.data == b'abcdef'
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x23\x40abcdef'
|
|
||||||
|
|
||||||
= Dissect DTO DNLOAD_6
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x40\x02\x34\x00\x20\x06')
|
|
||||||
|
|
||||||
assert dto.ctr == 64
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x024\x00 \x06'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 64
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.MTA0_extension == 2
|
|
||||||
assert dto.MTA0_address == 0x34002006
|
|
||||||
|
|
||||||
= Build CRO UPLOAD
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x41)/UPLOAD(size=4)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x41
|
|
||||||
assert cro.cmd == 0x04
|
|
||||||
assert cro.size == 4
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 5
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x04\x41\x04\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO UPLOAD
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x41\x10\x11\x12\x13\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 65
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x10\x11\x12\x13\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 65
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.data == b"\x10\x11\x12\x13\xff"
|
|
||||||
|
|
||||||
= Build CRO SHORT_UP
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x42)/SHORT_UP(size=4, address_extension=0, address=0x12345678)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x42
|
|
||||||
assert cro.cmd == 0x0f
|
|
||||||
assert cro.size == 4
|
|
||||||
assert cro.address == 0x12345678
|
|
||||||
assert cro.address_extension == 0
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0f\x42\x04\x00\x12\x34\x56\x78'
|
|
||||||
|
|
||||||
= Dissect DTO SHORT_UP
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x42\x10\x11\x12\x13\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 66
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x10\x11\x12\x13\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 66
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.data == b"\x10\x11\x12\x13\xff"
|
|
||||||
|
|
||||||
= Build CRO SELECT_CAL_PAGE
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x43)/SELECT_CAL_PAGE()
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x43
|
|
||||||
assert cro.cmd == 0x11
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 6
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x11\x43\xff\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO SELECT_CAL_PAGE
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x43\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 67
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 67
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b"\xff\xff\xff\xff\xff"
|
|
||||||
|
|
||||||
= Build CRO GET_DAQ_SIZE
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x44)/GET_DAQ_SIZE(DAQ_num=0x03, DTO_identifier=0x1020304)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x44
|
|
||||||
assert cro.cmd == 0x14
|
|
||||||
assert cro.DAQ_num == 0x03
|
|
||||||
assert cro.ccp_reserved == 00
|
|
||||||
assert cro.DTO_identifier == 0x01020304
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x14\x44\x03\x00\x01\x02\x03\x04'
|
|
||||||
|
|
||||||
= Dissect DTO GET_DAQ_SIZE
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x44\x10\x08\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 68
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x10\x08\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 68
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.DAQ_list_size == 16
|
|
||||||
assert dto.first_pid == 8
|
|
||||||
assert dto.ccp_reserved == b"\xff\xff\xff"
|
|
||||||
|
|
||||||
= Build CRO SET_DAQ_PTR
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x45)/SET_DAQ_PTR(DAQ_num=3, ODT_num=5, ODT_element=2)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x45
|
|
||||||
assert cro.cmd == 0x15
|
|
||||||
assert cro.DAQ_num == 0x03
|
|
||||||
assert cro.ODT_num == 5
|
|
||||||
assert cro.ODT_element == 2
|
|
||||||
assert cro.ccp_reserved == b"\xff\xff\xff"
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x15\x45\x03\x05\x02\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO SET_DAQ_PTR
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x45\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 69
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 69
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO WRITE_DAQ
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x46)/WRITE_DAQ(DAQ_size=2, address_extension=1, address=0x2004200)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x46
|
|
||||||
assert cro.cmd == 0x16
|
|
||||||
assert cro.DAQ_size == 0x02
|
|
||||||
assert cro.address_extension == 1
|
|
||||||
assert cro.address == 0x2004200
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x16\x46\x02\x01\x02\x00\x42\x00'
|
|
||||||
|
|
||||||
= Dissect DTO WRITE_DAQ
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x46\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 70
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 70
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO START_STOP
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x47)/START_STOP(mode=1, DAQ_num=3, ODT_num=7, event_channel=2, transmission_rate=1)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x47
|
|
||||||
assert cro.cmd == 0x06
|
|
||||||
assert cro.mode == 0x01
|
|
||||||
assert cro.DAQ_num == 3
|
|
||||||
assert cro.event_channel == 2
|
|
||||||
assert cro.transmission_rate == 1
|
|
||||||
assert cro.ODT_num == 7
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x06\x47\x01\x03\x07\x02\x00\x01'
|
|
||||||
|
|
||||||
= Dissect DTO START_STOP
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x47\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 71
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 71
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO DISCONNECT
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x48)/DISCONNECT(type="temporary", station_address=0x208)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x48
|
|
||||||
assert cro.cmd == 0x07
|
|
||||||
assert cro.type == 0x00
|
|
||||||
assert cro.station_address == 0x208
|
|
||||||
assert cro.ccp_reserved0 == b"\xff"
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 2
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x07\x48\x00\xff\x08\x02\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO DISCONNECT
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x48\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 72
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 72
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO SET_S_STATUS
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x49)/SET_S_STATUS(session_status="RUN+CAL")
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x49
|
|
||||||
assert cro.cmd == 0x0c
|
|
||||||
assert cro.session_status == 0x81
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 5
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0c\x49\x81\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO SET_S_STATUS
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x49\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 73
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 73
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO GET_S_STATUS
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x4a)/GET_S_STATUS()
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x4a
|
|
||||||
assert cro.cmd == 0x0D
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 6
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0d\x4a\xff\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO GET_S_STATUS
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x4a\x81\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 74
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x81\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 74
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.session_status == 0x81
|
|
||||||
assert dto.information_qualifier == 0xff
|
|
||||||
assert dto.information == b"\xff" * 3
|
|
||||||
|
|
||||||
= Build CRO BUILD_CHKSUM
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x50)/BUILD_CHKSUM(size=0x8000)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x50
|
|
||||||
assert cro.cmd == 0x0e
|
|
||||||
assert cro.size == 0x8000
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 2
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x0e\x50\x00\x00\x80\x00\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO BUILD_CHKSUM
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x50\x02\x12\x34\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 80
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x02\x12\x34\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 80
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.checksum_size == 2
|
|
||||||
assert dto.checksum_data == b'\x12\x34'
|
|
||||||
assert dto.ccp_reserved == b'\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO BUILD_CHKSUM2
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x50\x04\x12\x34\x56\x78')
|
|
||||||
|
|
||||||
assert dto.ctr == 80
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x04\x12\x34\x56\x78'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 80
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.checksum_size == 4
|
|
||||||
assert dto.checksum_data == b'\x12\x34\x56\x78'
|
|
||||||
assert dto.ccp_reserved == b''
|
|
||||||
|
|
||||||
= Build CRO CLEAR_MEMORY
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x51)/CLEAR_MEMORY(size=0x8000)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x51
|
|
||||||
assert cro.cmd == 0x10
|
|
||||||
assert cro.size == 0x8000
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 2
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x10\x51\x00\x00\x80\x00\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO CLEAR_MEMORY
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x51\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 81
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 81
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO PROGRAM
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x52)/PROGRAM(size=0x3, data=b"\x10\x11\x12")
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x52
|
|
||||||
assert cro.cmd == 0x18
|
|
||||||
assert cro.size == 0x3
|
|
||||||
assert cro.data == b"\x10\x11\x12"
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 5
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x18\x52\x03\x10\x11\x12\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO PROGRAM
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x52\x02\x34\x00\x20\x03')
|
|
||||||
|
|
||||||
assert dto.ctr == 82
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x02\x34\x00\x20\x03'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 82
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.MTA0_extension == 2
|
|
||||||
assert dto.MTA0_address == 0x34002003
|
|
||||||
|
|
||||||
= Build CRO PROGRAM_6
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12")
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x53
|
|
||||||
assert cro.cmd == 0x22
|
|
||||||
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x22\x53\x10\x11\x12\x10\x11\x12'
|
|
||||||
|
|
||||||
= Dissect DTO PROGRAM_6
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x53\x02\x34\x00\x20\x06')
|
|
||||||
|
|
||||||
assert dto.ctr == 83
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x02\x34\x00\x20\x06'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 83
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.MTA0_extension == 2
|
|
||||||
assert dto.MTA0_address == 0x34002006
|
|
||||||
|
|
||||||
= Build CRO MOVE
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x54)/MOVE(size=0x8000)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x54
|
|
||||||
assert cro.cmd == 0x19
|
|
||||||
assert cro.size == 0x8000
|
|
||||||
assert cro.ccp_reserved == b'\xff\xff'
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x19\x54\x00\x00\x80\x00\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO MOVE
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x54\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 84
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 84
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
|
|
||||||
= Build CRO DIAG_SERVICE
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x55)/DIAG_SERVICE(diag_service=0x8000)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x55
|
|
||||||
assert cro.cmd == 0x20
|
|
||||||
assert cro.diag_service == 0x8000
|
|
||||||
assert cro.ccp_reserved == b'\xff\xff\xff\xff'
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x20\x55\x80\x00\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO DIAG_SERVICE
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x55\x20\x00\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 85
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x20\x00\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 85
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.data_length == 0x20
|
|
||||||
assert dto.data_type == 0x00
|
|
||||||
assert dto.ccp_reserved == b"\xff\xff\xff"
|
|
||||||
|
|
||||||
= Build CRO ACTION_SERVICE
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x56)/ACTION_SERVICE(action_service=0x8000)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x56
|
|
||||||
assert cro.cmd == 0x21
|
|
||||||
assert cro.action_service == 0x8000
|
|
||||||
assert cro.ccp_reserved == b'\xff\xff\xff\xff'
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x21\x56\x80\x00\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO ACTION_SERVICE
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x56\x20\x00\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 86
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x20\x00\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 86
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.data_length == 0x20
|
|
||||||
assert dto.data_type == 0x00
|
|
||||||
assert dto.ccp_reserved == b"\xff\xff\xff"
|
|
||||||
|
|
||||||
= Build CRO TEST
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x60)/TEST(station_address=0x80)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x60
|
|
||||||
assert cro.cmd == 0x05
|
|
||||||
assert cro.station_address == 0x80
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 4
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x05\x60\x80\x00\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO TEST
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x60\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 96
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 96
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO START_STOP_ALL
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x61)/START_STOP_ALL(type="start")
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x61
|
|
||||||
assert cro.cmd == 0x08
|
|
||||||
assert cro.type == 0x01
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 5
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x08\x61\x01\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO START_STOP_ALL
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x61\xff\xff\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 97
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 97
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == True
|
|
||||||
assert dto.load == b'\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Build CRO GET_ACTIVE_CAL_PAGE
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x62)/GET_ACTIVE_CAL_PAGE()
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x62
|
|
||||||
assert cro.cmd == 0x09
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 6
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x09\x62\xff\xff\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
= Dissect DTO GET_ACTIVE_CAL_PAGE
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x62\x01\x11\x44\x77\x22')
|
|
||||||
|
|
||||||
assert dto.ctr == 98
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x01\x11\x44\x77\x22'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 98
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.address_extension == 1
|
|
||||||
assert dto.address == 0x11447722
|
|
||||||
|
|
||||||
= Build CRO GET_CCP_VERSION
|
|
||||||
|
|
||||||
cro = CCP(identifier=0x700)/CRO(ctr=0x63)/GET_CCP_VERSION(main_protocol_version=2, release_version=1)
|
|
||||||
|
|
||||||
assert cro.identifier == 0x700
|
|
||||||
assert cro.length == 8
|
|
||||||
assert cro.flags == 0
|
|
||||||
assert cro.ctr == 0x63
|
|
||||||
assert cro.cmd == 0x1b
|
|
||||||
assert cro.main_protocol_version == 2
|
|
||||||
assert cro.release_version == 1
|
|
||||||
assert cro.ccp_reserved == b"\xff" * 4
|
|
||||||
assert bytes(cro) == b'\x00\x00\x07\x00\x08\x00\x00\x00\x1b\x63\x02\x01\xff\xff\xff\xff'
|
|
||||||
|
|
||||||
assert dto.hashret() != cro.hashret()
|
|
||||||
assert not dto.answers(cro)
|
|
||||||
|
|
||||||
= Dissect DTO GET_CCP_VERSION
|
|
||||||
|
|
||||||
dto = CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x63\x02\x01\xff\xff\xff')
|
|
||||||
|
|
||||||
assert dto.ctr == 99
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert dto.load == b'\x02\x01\xff\xff\xff'
|
|
||||||
# answers will interpret payload
|
|
||||||
assert dto.answers(cro)
|
|
||||||
assert dto.ctr == 99
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.main_protocol_version == 2
|
|
||||||
assert dto.release_version == 1
|
|
||||||
assert dto.ccp_reserved == b"\xff" * 3
|
|
||||||
|
|
||||||
assert dto.hashret() == cro.hashret()
|
|
||||||
|
|
||||||
+ Tests on a virtual CAN-Bus
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
= Load modules
|
|
||||||
~ needs_root linux conf
|
|
||||||
import can
|
|
||||||
from subprocess import call
|
|
||||||
import time
|
|
||||||
|
|
||||||
conf.contribs['CANSocket'] = {'use-python-can': True}
|
|
||||||
load_contrib("cansocket")
|
|
||||||
|
|
||||||
iface0 = "vcan0"
|
|
||||||
|
|
||||||
= Initialize a virtual CAN interface
|
|
||||||
~ needs_root linux conf
|
|
||||||
if 0 != call("cansend %s 000#" % iface0, shell=True):
|
|
||||||
# vcan0 is not enabled
|
|
||||||
if 0 != call("sudo modprobe vcan", shell=True):
|
|
||||||
raise Exception("modprobe vcan failed")
|
|
||||||
if 0 != call("sudo ip link add name %s type vcan" % iface0, shell=True):
|
|
||||||
print("add %s failed: Maybe it was already up?" % iface0)
|
|
||||||
if 0 != call("sudo ip link set dev %s up" % iface0, shell=True):
|
|
||||||
raise Exception("could not bring up %s" % iface0)
|
|
||||||
|
|
||||||
if 0 != call("cansend %s 000#" % iface0, shell=True):
|
|
||||||
raise Exception("cansend doesn't work")
|
|
||||||
|
|
||||||
print("CAN should work now")
|
|
||||||
|
|
||||||
= CAN Socket sr1 with dto.ansers(cro) == True
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
|
|
||||||
def ecu():
|
|
||||||
pkts = sock2.sniff(count=1, timeout=1)
|
|
||||||
if len(pkts) == 1:
|
|
||||||
cro = CRO(pkts[0].data)
|
|
||||||
assert cro.cmd == 0x22
|
|
||||||
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
|
|
||||||
sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x53\x02\x34\x00\x20\x06'))
|
|
||||||
|
|
||||||
|
|
||||||
thread = threading.Thread(target=ecu)
|
|
||||||
thread.start()
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x53)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1)
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
assert dto.ctr == 83
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 0
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.MTA0_extension == 2
|
|
||||||
assert dto.MTA0_address == 0x34002006
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
|
|
||||||
= CAN Socket sr1 with dto.ansers(cro) == False
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
|
|
||||||
def ecu():
|
|
||||||
pkts = sock2.sniff(count=1, timeout=1)
|
|
||||||
if len(pkts) == 1:
|
|
||||||
cro = CRO(pkts[0].data)
|
|
||||||
assert cro.cmd == 0x22
|
|
||||||
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
|
|
||||||
sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x00\x55\x02\x34\x00\x20\x06'))
|
|
||||||
|
|
||||||
|
|
||||||
thread = threading.Thread(target=ecu)
|
|
||||||
thread.start()
|
|
||||||
time.sleep(0.1)
|
|
||||||
gotTimeout = False
|
|
||||||
try:
|
|
||||||
dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x54)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1)
|
|
||||||
except CANSocketTimeoutElapsed as e:
|
|
||||||
gotTimeout = True
|
|
||||||
|
|
||||||
assert gotTimeout
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
= CAN Socket sr1 with error code
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000), basecls=CCP)
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
|
|
||||||
def ecu():
|
|
||||||
pkts = sock2.sniff(count=1, timeout=1)
|
|
||||||
if len(pkts) == 1:
|
|
||||||
cro = CRO(pkts[0].data)
|
|
||||||
assert cro.cmd == 0x22
|
|
||||||
assert cro.data == b"\x10\x11\x12\x10\x11\x12"
|
|
||||||
sock2.send(CCP(b'\x00\x00\x07\x00\x08\x00\x00\x00\xff\x01\x55\xff\xff\xff\xff\xff'))
|
|
||||||
|
|
||||||
|
|
||||||
thread = threading.Thread(target=ecu)
|
|
||||||
thread.start()
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
dto = sock1.sr1(CCP(identifier=0x700)/CRO(ctr=0x55)/PROGRAM_6(data=b"\x10\x11\x12\x10\x11\x12"), timeout=1)
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
assert dto.ctr == 85
|
|
||||||
assert dto.packet_id == 0xff
|
|
||||||
assert dto.return_code == 1
|
|
||||||
assert hasattr(dto, "load") == False
|
|
||||||
assert dto.MTA0_extension == 0xff
|
|
||||||
assert dto.MTA0_address == 0xffffffff
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
sock2.close()
|
|
|
@ -1,343 +0,0 @@
|
||||||
#! /usr/bin/env python
|
|
||||||
|
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = Helper class for tracking ECU states (ECU)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import time
|
|
||||||
import random
|
|
||||||
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
from scapy.packet import Raw, Packet
|
|
||||||
from scapy.plist import PacketList
|
|
||||||
from scapy.error import Scapy_Exception
|
|
||||||
from scapy.sessions import DefaultSession
|
|
||||||
from scapy.ansmachine import AnsweringMachine
|
|
||||||
|
|
||||||
__all__ = ["ECU", "ECUResponse", "ECUSession", "ECU_am"]
|
|
||||||
|
|
||||||
|
|
||||||
class ECU(object):
|
|
||||||
"""A ECU object can be used to
|
|
||||||
- track the states of an ECU.
|
|
||||||
- to log all modification to an ECU
|
|
||||||
- to extract supported responses of a real ECU
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
>>> print("This ecu logs, tracks and creates supported responses")
|
|
||||||
>>> my_virtual_ecu = ECU()
|
|
||||||
>>> my_virtual_ecu.update(PacketList([...]))
|
|
||||||
>>> my_virtual_ecu.supported_responses
|
|
||||||
>>> print("Another ecu just tracks")
|
|
||||||
>>> my_tracking_ecu = ECU(logging=False, store_supported_responses=False) # noqa: E501
|
|
||||||
>>> my_tracking_ecu.update(PacketList([...]))
|
|
||||||
>>> print("Another ecu just logs all modifications to it")
|
|
||||||
>>> my_logging_ecu = ECU(verbose=False, store_supported_responses=False) # noqa: E501
|
|
||||||
>>> my_logging_ecu.update(PacketList([...]))
|
|
||||||
>>> my_logging_ecu.log
|
|
||||||
>>> print("Another ecu just creates supported responses")
|
|
||||||
>>> my_response_ecu = ECU(verbose=False, logging=False)
|
|
||||||
>>> my_response_ecu.update(PacketList([...]))
|
|
||||||
>>> my_response_ecu.supported_responses
|
|
||||||
"""
|
|
||||||
def __init__(self, init_session=None, init_security_level=None,
|
|
||||||
init_communication_control=None, logging=True, verbose=True,
|
|
||||||
store_supported_responses=True):
|
|
||||||
"""
|
|
||||||
Initialize an ECU object
|
|
||||||
|
|
||||||
:param init_session: An initial session
|
|
||||||
:param init_security_level: An initial security level
|
|
||||||
:param init_communication_control: An initial communication control
|
|
||||||
setting
|
|
||||||
:param logging: Turn logging on or off. Default is on.
|
|
||||||
:param verbose: Turn tracking on or off. Default is on.
|
|
||||||
:param store_supported_responses: Turn creation of supported responses
|
|
||||||
on or off. Default is on.
|
|
||||||
"""
|
|
||||||
self.current_session = init_session or 1
|
|
||||||
self.current_security_level = init_security_level or 0
|
|
||||||
self.communication_control = init_communication_control or 0
|
|
||||||
self.verbose = verbose
|
|
||||||
self.logging = logging
|
|
||||||
self.store_supported_responses = store_supported_responses
|
|
||||||
self.log = defaultdict(list)
|
|
||||||
self._supported_responses = list()
|
|
||||||
self._unanswered_packets = PacketList()
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self.current_session = 1
|
|
||||||
self.current_security_level = 0
|
|
||||||
self.communication_control = 0
|
|
||||||
|
|
||||||
def update(self, p):
|
|
||||||
if isinstance(p, PacketList):
|
|
||||||
for pkt in p:
|
|
||||||
self._update(pkt)
|
|
||||||
elif not isinstance(p, Packet):
|
|
||||||
raise Scapy_Exception("Provide a Packet object for an update")
|
|
||||||
else:
|
|
||||||
self._update(p)
|
|
||||||
|
|
||||||
def _update(self, pkt):
|
|
||||||
if self.verbose:
|
|
||||||
print(repr(self), repr(pkt))
|
|
||||||
if self.store_supported_responses:
|
|
||||||
self._update_supported_responses(pkt)
|
|
||||||
if self.logging:
|
|
||||||
self._update_log(pkt)
|
|
||||||
self._update_internal_state(pkt)
|
|
||||||
|
|
||||||
def _update_log(self, pkt):
|
|
||||||
for l in pkt.layers():
|
|
||||||
if hasattr(l, "get_log"):
|
|
||||||
log_key, log_value = l.get_log(pkt)
|
|
||||||
self.log[log_key].append((pkt.time, log_value))
|
|
||||||
|
|
||||||
def _update_internal_state(self, pkt):
|
|
||||||
for l in pkt.layers():
|
|
||||||
if hasattr(l, "modifies_ecu_state"):
|
|
||||||
l.modifies_ecu_state(pkt, self)
|
|
||||||
|
|
||||||
def _update_supported_responses(self, pkt):
|
|
||||||
self._unanswered_packets += PacketList([pkt])
|
|
||||||
answered, unanswered = self._unanswered_packets.sr()
|
|
||||||
for _, resp in answered:
|
|
||||||
ecu_resp = ECUResponse(session=self.current_session,
|
|
||||||
security_level=self.current_security_level,
|
|
||||||
responses=resp)
|
|
||||||
|
|
||||||
if ecu_resp not in self._supported_responses:
|
|
||||||
if self.verbose:
|
|
||||||
print("[+] ", repr(ecu_resp))
|
|
||||||
self._supported_responses.append(ecu_resp)
|
|
||||||
else:
|
|
||||||
if self.verbose:
|
|
||||||
print("[-] ", repr(ecu_resp))
|
|
||||||
self._unanswered_packets = unanswered
|
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_responses(self):
|
|
||||||
# This sorts responses in the following order:
|
|
||||||
# 1. Positive responses first
|
|
||||||
# 2. Lower ServiceID first
|
|
||||||
# 3. Longer (more specific) responses first
|
|
||||||
self._supported_responses.sort(
|
|
||||||
key=lambda x: (x.responses[0].service == 0x7f,
|
|
||||||
x.responses[0].service,
|
|
||||||
0xffffffff - len(x.responses[0])))
|
|
||||||
return self._supported_responses
|
|
||||||
|
|
||||||
@property
|
|
||||||
def unanswered_packets(self):
|
|
||||||
return self._unanswered_packets
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "ses: %03d sec: %03d cc: %d" % (self.current_session,
|
|
||||||
self.current_security_level,
|
|
||||||
self.communication_control)
|
|
||||||
|
|
||||||
|
|
||||||
class ECUSession(DefaultSession):
|
|
||||||
"""Tracks modification to an ECU 'on-the-flow'.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
>>> sniff(session=ECUSession)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
DefaultSession.__init__(self, *args, **kwargs)
|
|
||||||
self.ecu = ECU(init_session=kwargs.pop("init_session", None),
|
|
||||||
init_security_level=kwargs.pop("init_security_level", None), # noqa: E501
|
|
||||||
init_communication_control=kwargs.pop("init_communication_control", None), # noqa: E501
|
|
||||||
logging=kwargs.pop("logging", True),
|
|
||||||
verbose=kwargs.pop("verbose", True),
|
|
||||||
store_supported_responses=kwargs.pop("store_supported_responses", True)) # noqa: E501
|
|
||||||
|
|
||||||
def on_packet_received(self, pkt):
|
|
||||||
if not pkt:
|
|
||||||
return
|
|
||||||
if isinstance(pkt, list):
|
|
||||||
for p in pkt:
|
|
||||||
ECUSession.on_packet_received(self, p)
|
|
||||||
return
|
|
||||||
self.ecu.update(pkt)
|
|
||||||
DefaultSession.on_packet_received(self, pkt)
|
|
||||||
|
|
||||||
|
|
||||||
class ECUResponse:
|
|
||||||
"""Encapsulates a response and the according ECU state.
|
|
||||||
A list of this objects can be used to configure a ECU Answering Machine.
|
|
||||||
This is useful, if you want to clone the behaviour of a real ECU on a bus.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
>>> print("Generates a ECUResponse which answers on UDS()/UDS_RDBI(identifiers=[2]) if ECU is in session 2 and has security_level 2") # noqa: E501
|
|
||||||
>>> ECUResponse(session=2, security_level=2, responses=UDS()/UDS_RDBIPR(dataIdentifier=2)/Raw(b"deadbeef1")) # noqa: E501
|
|
||||||
>>> print("Further examples")
|
|
||||||
>>> ECUResponse(session=range(3,5), security_level=[3,4], responses=UDS()/UDS_RDBIPR(dataIdentifier=3)/Raw(b"deadbeef2")) # noqa: E501
|
|
||||||
>>> ECUResponse(session=[5,6,7], security_level=range(5,7), responses=UDS()/UDS_RDBIPR(dataIdentifier=5)/Raw(b"deadbeef3")) # noqa: E501
|
|
||||||
>>> ECUResponse(session=lambda x: 8 < x <= 10, security_level=lambda x: x > 10, responses=UDS()/UDS_RDBIPR(dataIdentifier=9)/Raw(b"deadbeef4")) # noqa: E501
|
|
||||||
"""
|
|
||||||
def __init__(self, session=1, security_level=0,
|
|
||||||
responses=Raw(b"\x7f\x10"),
|
|
||||||
answers=None):
|
|
||||||
"""
|
|
||||||
Initialize an ECUResponse capsule
|
|
||||||
|
|
||||||
:param session: Defines the session in which this response is valid.
|
|
||||||
A integer, a callable or any iterable object can be
|
|
||||||
provided.
|
|
||||||
:param security_level: Defines the security_level in which this
|
|
||||||
response is valid. A integer, a callable or any
|
|
||||||
iterable object can be provided.
|
|
||||||
:param responses: A Packet or a list of Packet objects. By default the
|
|
||||||
last packet is asked if it answers a incoming packet.
|
|
||||||
This allows to send for example
|
|
||||||
`requestCorrectlyReceived-ResponsePending` packets.
|
|
||||||
:param answers: Optional argument to provide a custom answer here:
|
|
||||||
`lambda resp, req: return resp.answers(req)`
|
|
||||||
This allows the modification of a response depending
|
|
||||||
on a request. Custom SecurityAccess mechanisms can
|
|
||||||
be implemented in this way or generic NegativeResponse
|
|
||||||
messages which answers to everything can be realized
|
|
||||||
in this way.
|
|
||||||
"""
|
|
||||||
self.__session = session \
|
|
||||||
if hasattr(session, "__iter__") or callable(session) else [session]
|
|
||||||
self.__security_level = security_level \
|
|
||||||
if hasattr(security_level, "__iter__") or callable(security_level)\
|
|
||||||
else [security_level]
|
|
||||||
if isinstance(responses, PacketList):
|
|
||||||
self.responses = responses
|
|
||||||
elif isinstance(responses, Packet):
|
|
||||||
self.responses = PacketList([responses])
|
|
||||||
elif hasattr(responses, "__iter__"):
|
|
||||||
self.responses = PacketList(responses)
|
|
||||||
else:
|
|
||||||
self.responses = PacketList([responses])
|
|
||||||
|
|
||||||
self.__custom_answers = answers
|
|
||||||
|
|
||||||
def in_correct_session(self, current_session):
|
|
||||||
if callable(self.__session):
|
|
||||||
return self.__session(current_session)
|
|
||||||
else:
|
|
||||||
return current_session in self.__session
|
|
||||||
|
|
||||||
def has_security_access(self, current_security_level):
|
|
||||||
if callable(self.__security_level):
|
|
||||||
return self.__security_level(current_security_level)
|
|
||||||
else:
|
|
||||||
return current_security_level in self.__security_level
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
if self.__custom_answers is not None:
|
|
||||||
return self.__custom_answers(self.responses[-1], other)
|
|
||||||
else:
|
|
||||||
return self.responses[-1].answers(other)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "session=%s, security_level=%s, responses=%s" % \
|
|
||||||
(self.__session, self.__security_level,
|
|
||||||
[resp.summary() for resp in self.responses])
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
return \
|
|
||||||
self.__class__ == other.__class__ and \
|
|
||||||
self.__session == other.__session and \
|
|
||||||
self.__security_level == other.__security_level and \
|
|
||||||
len(self.responses) == len(other.responses) and \
|
|
||||||
all(bytes(x) == bytes(y) for x, y in zip(self.responses,
|
|
||||||
other.responses))
|
|
||||||
|
|
||||||
def __ne__(self, other):
|
|
||||||
# Python 2.7 compat
|
|
||||||
return not self == other
|
|
||||||
|
|
||||||
__hash__ = None
|
|
||||||
|
|
||||||
|
|
||||||
class ECU_am(AnsweringMachine):
|
|
||||||
"""AnsweringMachine which emulates the basic behaviour of a real world ECU.
|
|
||||||
Provide a list of ``ECUResponse`` objects to configure the behaviour of this
|
|
||||||
AnsweringMachine.
|
|
||||||
|
|
||||||
:param supported_responses: List of ``ECUResponse`` objects to define
|
|
||||||
the behaviour. The default response is
|
|
||||||
``generalReject``.
|
|
||||||
:param main_socket: Defines the object of the socket to send
|
|
||||||
and receive packets.
|
|
||||||
:param broadcast_socket: Defines the object of the broadcast socket.
|
|
||||||
Listen-only, responds with the main_socket.
|
|
||||||
`None` to disable broadcast capabilities.
|
|
||||||
:param basecls: Provide a basecls of the used protocol
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
>>> resp = ECUResponse(session=range(0,255), security_level=0, responses=UDS() / UDS_NR(negativeResponseCode=0x7f, requestServiceId=0x10)) # noqa: E501
|
|
||||||
>>> sock = ISOTPSocket(can_iface, sid=0x700, did=0x600, basecls=UDS) # noqa: E501
|
|
||||||
>>> answering_machine = ECU_am(supported_responses=[resp], main_socket=sock, basecls=UDS) # noqa: E501
|
|
||||||
>>> sim = threading.Thread(target=answering_machine, kwargs={'count': 4, 'timeout':5}) # noqa: E501
|
|
||||||
>>> sim.start()
|
|
||||||
"""
|
|
||||||
function_name = "ECU_am"
|
|
||||||
sniff_options_list = ["store", "opened_socket", "count", "filter", "prn", "stop_filter", "timeout"] # noqa: E501
|
|
||||||
|
|
||||||
def parse_options(self, supported_responses=None,
|
|
||||||
main_socket=None, broadcast_socket=None, basecls=Raw,
|
|
||||||
timeout=None):
|
|
||||||
self.main_socket = main_socket
|
|
||||||
self.sockets = [self.main_socket]
|
|
||||||
|
|
||||||
if broadcast_socket is not None:
|
|
||||||
self.sockets.append(broadcast_socket)
|
|
||||||
|
|
||||||
self.ecu_state = ECU(logging=False, verbose=False,
|
|
||||||
store_supported_responses=False)
|
|
||||||
self.basecls = basecls
|
|
||||||
self.supported_responses = supported_responses
|
|
||||||
|
|
||||||
self.sniff_options["timeout"] = timeout
|
|
||||||
self.sniff_options["opened_socket"] = self.sockets
|
|
||||||
|
|
||||||
def is_request(self, req):
|
|
||||||
return req.__class__ == self.basecls
|
|
||||||
|
|
||||||
def print_reply(self, req, reply):
|
|
||||||
print("%s ==> %s" % (req.summary(), [res.summary() for res in reply]))
|
|
||||||
|
|
||||||
def make_reply(self, req):
|
|
||||||
if self.supported_responses is not None:
|
|
||||||
for resp in self.supported_responses:
|
|
||||||
if not isinstance(resp, ECUResponse):
|
|
||||||
raise Scapy_Exception("Unsupported type for response. "
|
|
||||||
"Please use `ECUResponse` objects. ")
|
|
||||||
|
|
||||||
if not resp.in_correct_session(self.ecu_state.current_session):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not resp.has_security_access(
|
|
||||||
self.ecu_state.current_security_level):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not resp.answers(req):
|
|
||||||
continue
|
|
||||||
|
|
||||||
for r in resp.responses:
|
|
||||||
for l in r.layers():
|
|
||||||
if hasattr(l, "modifies_ecu_state"):
|
|
||||||
l.modifies_ecu_state(r, self.ecu_state)
|
|
||||||
|
|
||||||
return resp.responses
|
|
||||||
|
|
||||||
return PacketList([self.basecls(b"\x7f" + bytes(req)[0:1] + b"\x10")])
|
|
||||||
|
|
||||||
def send_reply(self, reply):
|
|
||||||
for p in reply:
|
|
||||||
if len(reply) > 1:
|
|
||||||
time.sleep(random.uniform(0.01, 0.5))
|
|
||||||
self.main_socket.send(p)
|
|
|
@ -1,11 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive gm specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,836 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# Copyright (C) Enrico Pozzobon <enrico.pozzobon@gmail.com>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = General Motors Local Area Network (GMLAN)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import struct
|
|
||||||
from scapy.fields import ObservableDict, XByteEnumField, ByteEnumField, \
|
|
||||||
ConditionalField, XByteField, StrField, XShortEnumField, XShortField, \
|
|
||||||
X3BytesField, XIntField, ShortField, PacketField, PacketListField, \
|
|
||||||
FieldListField
|
|
||||||
from scapy.packet import Packet, bind_layers, NoPayload
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.error import warning, log_loading
|
|
||||||
from scapy.contrib.isotp import ISOTP
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
GMLAN
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
if conf.contribs['GMLAN']['treat-response-pending-as-answer']:
|
|
||||||
pass
|
|
||||||
except KeyError:
|
|
||||||
log_loading.info("Specify \"conf.contribs['GMLAN'] = "
|
|
||||||
"{'treat-response-pending-as-answer': True}\" to treat "
|
|
||||||
"a negative response 'RequestCorrectlyReceived-"
|
|
||||||
"ResponsePending' as answer of a request. \n"
|
|
||||||
"The default value is False.")
|
|
||||||
conf.contribs['GMLAN'] = {'treat-response-pending-as-answer': False}
|
|
||||||
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = None
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN(ISOTP):
|
|
||||||
@staticmethod
|
|
||||||
def determine_len(x):
|
|
||||||
if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] is None:
|
|
||||||
warning("Define conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']! " # noqa: E501
|
|
||||||
"Assign either 2,3 or 4")
|
|
||||||
if conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] \
|
|
||||||
not in [2, 3, 4]:
|
|
||||||
warning("Define conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']! " # noqa: E501
|
|
||||||
"Assign either 2,3 or 4")
|
|
||||||
return conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == x
|
|
||||||
|
|
||||||
services = ObservableDict(
|
|
||||||
{0x04: 'ClearDiagnosticInformation',
|
|
||||||
0x10: 'InitiateDiagnosticOperation',
|
|
||||||
0x12: 'ReadFailureRecordData',
|
|
||||||
0x1a: 'ReadDataByIdentifier',
|
|
||||||
0x20: 'ReturnToNormalOperation',
|
|
||||||
0x22: 'ReadDataByParameterIdentifier',
|
|
||||||
0x23: 'ReadMemoryByAddress',
|
|
||||||
0x27: 'SecurityAccess',
|
|
||||||
0x28: 'DisableNormalCommunication',
|
|
||||||
0x2c: 'DynamicallyDefineMessage',
|
|
||||||
0x2d: 'DefinePIDByAddress',
|
|
||||||
0x34: 'RequestDownload',
|
|
||||||
0x36: 'TransferData',
|
|
||||||
0x3b: 'WriteDataByIdentifier',
|
|
||||||
0x3e: 'TesterPresent',
|
|
||||||
0x44: 'ClearDiagnosticInformationPositiveResponse',
|
|
||||||
0x50: 'InitiateDiagnosticOperationPositiveResponse',
|
|
||||||
0x52: 'ReadFailureRecordDataPositiveResponse',
|
|
||||||
0x5a: 'ReadDataByIdentifierPositiveResponse',
|
|
||||||
0x60: 'ReturnToNormalOperationPositiveResponse',
|
|
||||||
0x62: 'ReadDataByParameterIdentifierPositiveResponse',
|
|
||||||
0x63: 'ReadMemoryByAddressPositiveResponse',
|
|
||||||
0x67: 'SecurityAccessPositiveResponse',
|
|
||||||
0x68: 'DisableNormalCommunicationPositiveResponse',
|
|
||||||
0x6c: 'DynamicallyDefineMessagePositiveResponse',
|
|
||||||
0x6d: 'DefinePIDByAddressPositiveResponse',
|
|
||||||
0x74: 'RequestDownloadPositiveResponse',
|
|
||||||
0x76: 'TransferDataPositiveResponse',
|
|
||||||
0x7b: 'WriteDataByIdentifierPositiveResponse',
|
|
||||||
0x7e: 'TesterPresentPositiveResponse',
|
|
||||||
0x7f: 'NegativeResponse',
|
|
||||||
0xa2: 'ReportProgrammingState',
|
|
||||||
0xa5: 'ProgrammingMode',
|
|
||||||
0xa9: 'ReadDiagnosticInformation',
|
|
||||||
0xaa: 'ReadDataByPacketIdentifier',
|
|
||||||
0xae: 'DeviceControl',
|
|
||||||
0xe2: 'ReportProgrammingStatePositiveResponse',
|
|
||||||
0xe5: 'ProgrammingModePositiveResponse',
|
|
||||||
0xe9: 'ReadDiagnosticInformationPositiveResponse',
|
|
||||||
0xea: 'ReadDataByPacketIdentifierPositiveResponse',
|
|
||||||
0xee: 'DeviceControlPositiveResponse'})
|
|
||||||
name = 'General Motors Local Area Network'
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('service', 0, services)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
if other.__class__ != self.__class__:
|
|
||||||
return False
|
|
||||||
if self.service == 0x7f:
|
|
||||||
return self.payload.answers(other)
|
|
||||||
if self.service == (other.service + 0x40):
|
|
||||||
if isinstance(self.payload, NoPayload) or \
|
|
||||||
isinstance(other.payload, NoPayload):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return self.payload.answers(other.payload)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def hashret(self):
|
|
||||||
if self.service == 0x7f:
|
|
||||||
return struct.pack('B', self.requestServiceId)
|
|
||||||
return struct.pack('B', self.service & ~0x40)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def modifies_ecu_state(pkt, ecu):
|
|
||||||
if pkt.service == 0x50:
|
|
||||||
ecu.current_session = 3
|
|
||||||
elif pkt.service == 0x60:
|
|
||||||
ecu.current_session = 1
|
|
||||||
ecu.communication_control = 0
|
|
||||||
ecu.current_security_level = 0
|
|
||||||
elif pkt.service == 0x68:
|
|
||||||
ecu.communication_control = 1
|
|
||||||
elif pkt.service == 0xe5:
|
|
||||||
ecu.current_session = 2
|
|
||||||
|
|
||||||
|
|
||||||
# ########################IDO###################################
|
|
||||||
class GMLAN_IDO(Packet):
|
|
||||||
subfunctions = {
|
|
||||||
0x02: 'disableAllDTCs',
|
|
||||||
0x03: 'enableDTCsDuringDevCntrl',
|
|
||||||
0x04: 'wakeUpLinks'}
|
|
||||||
name = 'InitiateDiagnosticOperation'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, subfunctions)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_IDO.subfunction%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_IDO, service=0x10)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RFRD###################################
|
|
||||||
class GMLAN_DTC(Packet):
|
|
||||||
name = 'GMLAN DTC information'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('failureRecordNumber', 0),
|
|
||||||
XByteField('DTCHighByte', 0),
|
|
||||||
XByteField('DTCLowByte', 0),
|
|
||||||
XByteField('DTCFailureType', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
def extract_padding(self, p):
|
|
||||||
return "", p
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RFRD(Packet):
|
|
||||||
subfunctions = {
|
|
||||||
0x01: 'readFailureRecordIdentifiers',
|
|
||||||
0x02: 'readFailureRecordParameters'}
|
|
||||||
name = 'ReadFailureRecordData'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, subfunctions),
|
|
||||||
ConditionalField(PacketField("dtc", b'', GMLAN_DTC),
|
|
||||||
lambda pkt: pkt.subfunction == 0x02)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RFRD.subfunction%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RFRD, service=0x12)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RFRDPR(Packet):
|
|
||||||
name = 'ReadFailureRecordDataPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, GMLAN_RFRD.subfunctions)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_RFRD and \
|
|
||||||
other.subfunction == self.subfunction
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RFRDPR.subfunction%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RFRDPR, service=0x52)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RFRDPR_RFRI(Packet):
|
|
||||||
failureRecordDataStructureIdentifiers = {
|
|
||||||
0x00: "PID",
|
|
||||||
0x01: "DPID"
|
|
||||||
}
|
|
||||||
name = 'ReadFailureRecordDataPositiveResponse_readFailureRecordIdentifiers'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('failureRecordDataStructureIdentifier', 0,
|
|
||||||
failureRecordDataStructureIdentifiers),
|
|
||||||
PacketListField("dtcs", [], GMLAN_DTC)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN_RFRDPR, GMLAN_RFRDPR_RFRI, subfunction=0x01)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RFRDPR_RFRP(Packet):
|
|
||||||
name = 'ReadFailureRecordDataPositiveResponse_readFailureRecordParameters'
|
|
||||||
fields_desc = [
|
|
||||||
PacketField("dtc", b'', GMLAN_DTC)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN_RFRDPR, GMLAN_RFRDPR_RFRP, subfunction=0x02)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RDBI###################################
|
|
||||||
class GMLAN_RDBI(Packet):
|
|
||||||
dataIdentifiers = ObservableDict({
|
|
||||||
0x90: "$90: VehicleIdentificationNumber (VIN)",
|
|
||||||
0x92: "$92: SystemSupplierId (SYSSUPPID)",
|
|
||||||
0x97: "$97: SystemNameOrEngineType (SNOET)",
|
|
||||||
0x98: "$98: RepairShopCodeOrTesterSerialNumber (RSCOTSN)",
|
|
||||||
0x99: "$99: ProgrammingDate (PD)",
|
|
||||||
0x9a: "$9a: DiagnosticDataIdentifier (DDI)",
|
|
||||||
0x9b: "$9b: XmlConfigurationCompatibilityIdentifier (XMLCCID)",
|
|
||||||
0x9C: "$9C: XmlDataFilePartNumber (XMLDFPN)",
|
|
||||||
0x9D: "$9D: XmlDataFileAlphaCode (XMLDFAC)",
|
|
||||||
0x9F: "$9F: PreviousStoredRepairShopCodeOrTesterSerialNumbers "
|
|
||||||
"(PSRSCOTSN)",
|
|
||||||
0xA0: "$A0: manufacturers_enable_counter (MEC)",
|
|
||||||
0xA1: "$A1: ECUConfigurationOrCustomizationData (ECUCOCGD) 1",
|
|
||||||
0xA2: "$A2: ECUConfigurationOrCustomizationData (ECUCOCGD) 2",
|
|
||||||
0xA3: "$A3: ECUConfigurationOrCustomizationData (ECUCOCGD) 3",
|
|
||||||
0xA4: "$A4: ECUConfigurationOrCustomizationData (ECUCOCGD) 4",
|
|
||||||
0xA5: "$A5: ECUConfigurationOrCustomizationData (ECUCOCGD) 5",
|
|
||||||
0xA6: "$A6: ECUConfigurationOrCustomizationData (ECUCOCGD) 6",
|
|
||||||
0xA7: "$A7: ECUConfigurationOrCustomizationData (ECUCOCGD) 7",
|
|
||||||
0xA8: "$A8: ECUConfigurationOrCustomizationData (ECUCOCGD) 8",
|
|
||||||
0xB0: "$B0: ECUDiagnosticAddress (ECUADDR)",
|
|
||||||
0xB1: "$B1: ECUFunctionalSystemsAndVirtualDevices (ECUFSAVD)",
|
|
||||||
0xB2: "$B2: GM ManufacturingData (GMMD)",
|
|
||||||
0xB3: "$B3: Data Universal Numbering System Identification (DUNS)",
|
|
||||||
0xB4: "$B4: Manufacturing Traceability Characters (MTC)",
|
|
||||||
0xB5: "$B5: GM BroadcastCode (GMBC)",
|
|
||||||
0xB6: "$B6: GM Target Vehicle (GMTV)",
|
|
||||||
0xB7: "$B7: GM Software Usage Description (GMSUD)",
|
|
||||||
0xB8: "$B8: GM Bench Verification Information (GMBVI)",
|
|
||||||
0xB9: "$B9: Subnet_Config_List_HighSpeed (SCLHS)",
|
|
||||||
0xBA: "$BA: Subnet_Config_List_LowSpeed (SCLLS)",
|
|
||||||
0xBB: "$BB: Subnet_Config_List_MidSpeed (SCLMS)",
|
|
||||||
0xBC: "$BC: Subnet_Config_List_NonCan 1 (SCLNC 1)",
|
|
||||||
0xBD: "$BD: Subnet_Config_List_NonCan 2 (SCLNC 2)",
|
|
||||||
0xBE: "$BE: Subnet_Config_List_LIN (SCLLIN)",
|
|
||||||
0xBF: "$BF: Subnet_Config_List_GMLANChassisExpansionBus (SCLGCEB)",
|
|
||||||
0xC0: "$C0: BootSoftwarePartNumber (BSPN)",
|
|
||||||
0xC1: "$C1: SoftwareModuleIdentifier (SWMI) 01",
|
|
||||||
0xC2: "$C2: SoftwareModuleIdentifier (SWMI) 02",
|
|
||||||
0xC3: "$C3: SoftwareModuleIdentifier (SWMI) 03",
|
|
||||||
0xC4: "$C4: SoftwareModuleIdentifier (SWMI) 04",
|
|
||||||
0xC5: "$C5: SoftwareModuleIdentifier (SWMI) 05",
|
|
||||||
0xC6: "$C6: SoftwareModuleIdentifier (SWMI) 06",
|
|
||||||
0xC7: "$C7: SoftwareModuleIdentifier (SWMI) 07",
|
|
||||||
0xC8: "$C8: SoftwareModuleIdentifier (SWMI) 08",
|
|
||||||
0xC9: "$C9: SoftwareModuleIdentifier (SWMI) 09",
|
|
||||||
0xCA: "$CA: SoftwareModuleIdentifier (SWMI) 10",
|
|
||||||
0xCB: "$CB: EndModelPartNumber",
|
|
||||||
0xCC: "$CC: BaseModelPartNumber (BMPN)",
|
|
||||||
0xD0: "$D0: BootSoftwarePartNumberAlphaCode",
|
|
||||||
0xD1: "$D1: SoftwareModuleIdentifierAlphaCode (SWMIAC) 01",
|
|
||||||
0xD2: "$D2: SoftwareModuleIdentifierAlphaCode (SWMIAC) 02",
|
|
||||||
0xD3: "$D3: SoftwareModuleIdentifierAlphaCode (SWMIAC) 03",
|
|
||||||
0xD4: "$D4: SoftwareModuleIdentifierAlphaCode (SWMIAC) 04",
|
|
||||||
0xD5: "$D5: SoftwareModuleIdentifierAlphaCode (SWMIAC) 05",
|
|
||||||
0xD6: "$D6: SoftwareModuleIdentifierAlphaCode (SWMIAC) 06",
|
|
||||||
0xD7: "$D7: SoftwareModuleIdentifierAlphaCode (SWMIAC) 07",
|
|
||||||
0xD8: "$D8: SoftwareModuleIdentifierAlphaCode (SWMIAC) 08",
|
|
||||||
0xD9: "$D9: SoftwareModuleIdentifierAlphaCode (SWMIAC) 09",
|
|
||||||
0xDA: "$DA: SoftwareModuleIdentifierAlphaCode (SWMIAC) 10",
|
|
||||||
0xDB: "$DB: EndModelPartNumberAlphaCode",
|
|
||||||
0xDC: "$DC: BaseModelPartNumberAlphaCode",
|
|
||||||
0xDD: "$DD: SoftwareModuleIdentifierDataIdentifiers (SWMIDID)",
|
|
||||||
0xDE: "$DE: GMLANIdentificationData (GMLANID)",
|
|
||||||
0xDF: "$DF: ECUOdometerValue (ECUODO)",
|
|
||||||
0xE0: "$E0: VehicleLevelDataRecord (VLDR) 0",
|
|
||||||
0xE1: "$E1: VehicleLevelDataRecord (VLDR) 1",
|
|
||||||
0xE2: "$E2: VehicleLevelDataRecord (VLDR) 2",
|
|
||||||
0xE3: "$E3: VehicleLevelDataRecord (VLDR) 3",
|
|
||||||
0xE4: "$E4: VehicleLevelDataRecord (VLDR) 4",
|
|
||||||
0xE5: "$E5: VehicleLevelDataRecord (VLDR) 5",
|
|
||||||
0xE6: "$E6: VehicleLevelDataRecord (VLDR) 6",
|
|
||||||
0xE7: "$E7: VehicleLevelDataRecord (VLDR) 7",
|
|
||||||
0xE8: "$E8: Subnet_Config_List_GMLANPowertrainExpansionBus (SCLGPEB)",
|
|
||||||
0xE9: "$E9: Subnet_Config_List_GMLANFrontObjectExpansionBus "
|
|
||||||
"(SCLGFOEB)",
|
|
||||||
0xEA: "$EA: Subnet_Config_List_GMLANRearObjectExpansionBus (SCLGROEB)",
|
|
||||||
0xEB: "$EB: Subnet_Config_List_GMLANExpansionBus1 (SCLGEB1)",
|
|
||||||
0xEC: "$EC: Subnet_Config_List_GMLANExpansionBus2 (SCLGEB2)",
|
|
||||||
0xED: "$ED: Subnet_Config_List_GMLANExpansionBus3 (SCLGEB3)",
|
|
||||||
0xEE: "$EE: Subnet_Config_List_GMLANExpansionBus4 (SCLGEB4)",
|
|
||||||
0xEF: "$EF: Subnet_Config_List_GMLANExpansionBus5 (SCLGEB5)",
|
|
||||||
})
|
|
||||||
|
|
||||||
name = 'ReadDataByIdentifier'
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('dataIdentifier', 0, dataIdentifiers)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RDBI.dataIdentifier%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RDBI, service=0x1A)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RDBIPR(Packet):
|
|
||||||
name = 'ReadDataByIdentifierPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.sprintf("%GMLAN_RDBIPR.dataIdentifier%"),
|
|
||||||
bytes(pkt[1].payload))
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_RDBI and \
|
|
||||||
other.dataIdentifier == self.dataIdentifier
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RDBIPR, service=0x5A)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RDBI###################################
|
|
||||||
class GMLAN_RDBPI(Packet):
|
|
||||||
dataIdentifiers = ObservableDict({
|
|
||||||
0x0005: "OBD_EngineCoolantTemperature",
|
|
||||||
0x000C: "OBD_EngineRPM",
|
|
||||||
0x001f: "OBD_TimeSinceEngineStart"
|
|
||||||
})
|
|
||||||
name = 'ReadDataByParameterIdentifier'
|
|
||||||
fields_desc = [
|
|
||||||
FieldListField("identifiers", [],
|
|
||||||
XShortEnumField('parameterIdentifier', 0,
|
|
||||||
dataIdentifiers))
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RDBPI.identifiers%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RDBPI, service=0x22)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RDBPIPR(Packet):
|
|
||||||
name = 'ReadDataByParameterIdentifierPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
XShortEnumField('parameterIdentifier', 0, GMLAN_RDBPI.dataIdentifiers),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RDBPIPR.parameterIdentifier%")
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_RDBPI and \
|
|
||||||
self.parameterIdentifier in other.identifiers
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RDBPIPR, service=0x62)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RDBPKTI###################################
|
|
||||||
class GMLAN_RDBPKTI(Packet):
|
|
||||||
name = 'ReadDataByPacketIdentifier'
|
|
||||||
subfunctions = {
|
|
||||||
0x00: "stopSending",
|
|
||||||
0x01: "sendOneResponse",
|
|
||||||
0x02: "scheduleAtSlowRate",
|
|
||||||
0x03: "scheduleAtMediumRate",
|
|
||||||
0x04: "scheduleAtFastRate"
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('subfunction', 0, subfunctions),
|
|
||||||
ConditionalField(FieldListField('request_DPIDs', [],
|
|
||||||
XByteField("", 0)),
|
|
||||||
lambda pkt: pkt.subfunction > 0x0)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RDBPKTI.subfunction%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RDBPKTI, service=0xAA)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RMBA###################################
|
|
||||||
class GMLAN_RMBA(Packet):
|
|
||||||
name = 'ReadMemoryByAddress'
|
|
||||||
fields_desc = [
|
|
||||||
ConditionalField(XShortField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(2)),
|
|
||||||
ConditionalField(X3BytesField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(3)),
|
|
||||||
ConditionalField(XIntField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(4)),
|
|
||||||
XShortField('memorySize', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RMBA.memoryAddress%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RMBA, service=0x23)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RMBAPR(Packet):
|
|
||||||
name = 'ReadMemoryByAddressPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
ConditionalField(XShortField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(2)),
|
|
||||||
ConditionalField(X3BytesField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(3)),
|
|
||||||
ConditionalField(XIntField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(4)),
|
|
||||||
StrField('dataRecord', None, fmt="B")
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_RMBA and \
|
|
||||||
other.memoryAddress == self.memoryAddress
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.sprintf("%GMLAN_RMBAPR.memoryAddress%"), pkt.dataRecord)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RMBAPR, service=0x63)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################SA###################################
|
|
||||||
class GMLAN_SA(Packet):
|
|
||||||
subfunctions = {
|
|
||||||
0: 'ReservedByDocument',
|
|
||||||
1: 'SPSrequestSeed',
|
|
||||||
2: 'SPSsendKey',
|
|
||||||
3: 'DevCtrlrequestSeed',
|
|
||||||
4: 'DevCtrlsendKey',
|
|
||||||
255: 'ReservedByDocument'}
|
|
||||||
for i in range(0x05, 0x0a + 1):
|
|
||||||
subfunctions[i] = 'ReservedByDocument'
|
|
||||||
for i in range(0x0b, 0xfa + 1):
|
|
||||||
subfunctions[i] = 'Reserved for vehicle manufacturer specific needs'
|
|
||||||
for i in range(0xfb, 0xfe + 1):
|
|
||||||
subfunctions[i] = 'Reserved for ECU or ' \
|
|
||||||
'system supplier manufacturing needs'
|
|
||||||
|
|
||||||
name = 'SecurityAccess'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, subfunctions),
|
|
||||||
ConditionalField(XShortField('securityKey', B""),
|
|
||||||
lambda pkt: pkt.subfunction % 2 == 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
if pkt.subfunction % 2 == 1:
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.subfunction, None)
|
|
||||||
else:
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.subfunction, pkt.securityKey)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_SA, service=0x27)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_SAPR(Packet):
|
|
||||||
name = 'SecurityAccessPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, GMLAN_SA.subfunctions),
|
|
||||||
ConditionalField(XShortField('securitySeed', B""),
|
|
||||||
lambda pkt: pkt.subfunction % 2 == 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_SA \
|
|
||||||
and other.subfunction == self.subfunction
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
if pkt.subfunction % 2 == 0:
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.subfunction, None)
|
|
||||||
else:
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.subfunction, pkt.securitySeed)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def modifies_ecu_state(pkt, ecu):
|
|
||||||
if pkt.subfunction % 2 == 0:
|
|
||||||
ecu.current_security_level = pkt.subfunction
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_SAPR, service=0x67)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################DDM###################################
|
|
||||||
class GMLAN_DDM(Packet):
|
|
||||||
name = 'DynamicallyDefineMessage'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DPIDIdentifier', 0),
|
|
||||||
StrField('PIDData', b'\x00\x00')
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.sprintf("%GMLAN_DDM.DPIDIdentifier%"), pkt.PIDData)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_DDM, service=0x2C)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_DDMPR(Packet):
|
|
||||||
name = 'DynamicallyDefineMessagePositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DPIDIdentifier', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_DDMPR.DPIDIdentifier%")
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_DDM \
|
|
||||||
and other.DPIDIdentifier == self.DPIDIdentifier
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_DDMPR, service=0x6C)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################DPBA###################################
|
|
||||||
class GMLAN_DPBA(Packet):
|
|
||||||
name = 'DefinePIDByAddress'
|
|
||||||
fields_desc = [
|
|
||||||
XShortField('parameterIdentifier', 0),
|
|
||||||
ConditionalField(XShortField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(2)),
|
|
||||||
ConditionalField(X3BytesField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(3)),
|
|
||||||
ConditionalField(XIntField('memoryAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(4)),
|
|
||||||
XByteField('memorySize', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.parameterIdentifier, pkt.memoryAddress, pkt.memorySize)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_DPBA, service=0x2D)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_DPBAPR(Packet):
|
|
||||||
name = 'DefinePIDByAddressPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
XShortField('parameterIdentifier', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), pkt.parameterIdentifier
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_DPBA \
|
|
||||||
and other.parameterIdentifier == self.parameterIdentifier
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_DPBA, service=0x6D)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RD###################################
|
|
||||||
class GMLAN_RD(Packet):
|
|
||||||
name = 'RequestDownload'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('dataFormatIdentifier', 0),
|
|
||||||
ConditionalField(XShortField('memorySize', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(2)),
|
|
||||||
ConditionalField(X3BytesField('memorySize', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(3)),
|
|
||||||
ConditionalField(XIntField('memorySize', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(4)),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.dataFormatIdentifier, pkt.memorySize)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RD, service=0x34)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################TD###################################
|
|
||||||
class GMLAN_TD(Packet):
|
|
||||||
subfunctions = {
|
|
||||||
0x00: "download",
|
|
||||||
0x80: "downloadAndExecuteOrExecute"
|
|
||||||
}
|
|
||||||
name = 'TransferData'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, subfunctions),
|
|
||||||
ConditionalField(XShortField('startingAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(2)),
|
|
||||||
ConditionalField(X3BytesField('startingAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(3)),
|
|
||||||
ConditionalField(XIntField('startingAddress', 0),
|
|
||||||
lambda pkt: GMLAN.determine_len(4)),
|
|
||||||
StrField("dataRecord", None)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.sprintf("%GMLAN_TD.subfunction%"), pkt.startingAddress,
|
|
||||||
pkt.dataRecord)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_TD, service=0x36)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################WDBI###################################
|
|
||||||
class GMLAN_WDBI(Packet):
|
|
||||||
name = 'WriteDataByIdentifier'
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers),
|
|
||||||
StrField("dataRecord", b'\x00')
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.sprintf("%GMLAN_WDBI.dataIdentifier%"), pkt.dataRecord)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_WDBI, service=0x3B)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_WDBIPR(Packet):
|
|
||||||
name = 'WriteDataByIdentifierPositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('dataIdentifier', 0, GMLAN_RDBI.dataIdentifiers)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_WDBIPR.dataIdentifier%")
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == GMLAN_WDBI \
|
|
||||||
and other.dataIdentifier == self.dataIdentifier
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_WDBIPR, service=0x7B)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RPSPR###################################
|
|
||||||
class GMLAN_RPSPR(Packet):
|
|
||||||
programmedStates = {
|
|
||||||
0x00: "fully programmed",
|
|
||||||
0x01: "no op s/w or cal data",
|
|
||||||
0x02: "op s/w present, cal data missing",
|
|
||||||
0x03: "s/w present, default or no start cal present",
|
|
||||||
0x50: "General Memory Fault",
|
|
||||||
0x51: "RAM Memory Fault",
|
|
||||||
0x52: "NVRAM Memory Fault",
|
|
||||||
0x53: "Boot Memory Failure",
|
|
||||||
0x54: "Flash Memory Failure",
|
|
||||||
0x55: "EEPROM Memory Failure",
|
|
||||||
}
|
|
||||||
name = 'ReportProgrammedStatePositiveResponse'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('programmedState', 0, programmedStates),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RPSPR.programmedState%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RPSPR, service=0xE2)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################PM###################################
|
|
||||||
class GMLAN_PM(Packet):
|
|
||||||
subfunctions = {
|
|
||||||
0x01: "requestProgrammingMode",
|
|
||||||
0x02: "requestProgrammingMode_HighSpeed",
|
|
||||||
0x03: "enableProgrammingMode"
|
|
||||||
}
|
|
||||||
name = 'ProgrammingMode'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, subfunctions),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_PM.subfunction%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_PM, service=0xA5)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################RDI###################################
|
|
||||||
class GMLAN_RDI(Packet):
|
|
||||||
subfunctions = {
|
|
||||||
0x80: 'readStatusOfDTCByDTCNumber',
|
|
||||||
0x81: 'readStatusOfDTCByStatusMask',
|
|
||||||
0x82: 'sendOnChangeDTCCount'
|
|
||||||
}
|
|
||||||
name = 'ReadDiagnosticInformation'
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('subfunction', 0, subfunctions)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
pkt.sprintf("%GMLAN_RDI.subfunction%")
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_RDI, service=0xA9)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RDI_BN(Packet):
|
|
||||||
name = 'ReadStatusOfDTCByDTCNumber'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DTCHighByte', 0),
|
|
||||||
XByteField('DTCLowByte', 0),
|
|
||||||
XByteField('DTCFailureType', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN_RDI, GMLAN_RDI_BN, subfunction=0x80)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RDI_BM(Packet):
|
|
||||||
name = 'ReadStatusOfDTCByStatusMask'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DTCStatusMask', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN_RDI, GMLAN_RDI_BM, subfunction=0x81)
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_RDI_BC(Packet):
|
|
||||||
name = 'SendOnChangeDTCCount'
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('DTCStatusMask', 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN_RDI, GMLAN_RDI_BC, subfunction=0x82)
|
|
||||||
# TODO:This function receive single frame responses... (Implement GMLAN Socket)
|
|
||||||
|
|
||||||
|
|
||||||
# ########################NRC###################################
|
|
||||||
class GMLAN_NR(Packet):
|
|
||||||
negativeResponseCodes = {
|
|
||||||
0x11: 'ServiceNotSupported',
|
|
||||||
0x12: 'SubFunctionNotSupported',
|
|
||||||
0x22: 'ConditionsNotCorrectOrRequestSequenceError',
|
|
||||||
0x31: 'RequestOutOfRange',
|
|
||||||
0x35: 'InvalidKey',
|
|
||||||
0x36: 'ExceedNumberOfAttempts',
|
|
||||||
0x37: 'RequiredTimeDelayNotExpired',
|
|
||||||
0x78: 'RequestCorrectlyReceived-ResponsePending',
|
|
||||||
0x81: 'SchedulerFull',
|
|
||||||
0x83: 'VoltageOutOfRange',
|
|
||||||
0x85: 'GeneralProgrammingFailure',
|
|
||||||
0x89: 'DeviceTypeError',
|
|
||||||
0x99: 'ReadyForDownload-DTCStored',
|
|
||||||
0xe3: 'DeviceControlLimitsExceeded',
|
|
||||||
}
|
|
||||||
name = 'NegativeResponse'
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('requestServiceId', 0, GMLAN.services),
|
|
||||||
ByteEnumField('returnCode', 0, negativeResponseCodes),
|
|
||||||
ShortField('deviceControlLimitExceeded', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_log(pkt):
|
|
||||||
return pkt.sprintf("%GMLAN.service%"), \
|
|
||||||
(pkt.sprintf("%GMLAN_NR.requestServiceId%"),
|
|
||||||
pkt.sprintf("%GMLAN_NR.returnCode%"))
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return self.requestServiceId == other.service and \
|
|
||||||
(self.returnCode != 0x78 or
|
|
||||||
conf.contribs['GMLAN']['treat-response-pending-as-answer'])
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(GMLAN, GMLAN_NR, service=0x7f)
|
|
|
@ -1,403 +0,0 @@
|
||||||
# gmlan unit tests
|
|
||||||
#
|
|
||||||
# Type the following command to launch start the tests:
|
|
||||||
# $ sudo bash test/run_tests -t test/gmlan.uts -F
|
|
||||||
|
|
||||||
% gmlan unit tests
|
|
||||||
|
|
||||||
+ Configuration of scapy
|
|
||||||
= Load gmlan layer
|
|
||||||
~ conf command
|
|
||||||
|
|
||||||
load_contrib('automotive.gm.gmlan')
|
|
||||||
|
|
||||||
+ Basic Packet Tests()
|
|
||||||
= Set GMLAN ECU AddressingScheme
|
|
||||||
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
|
|
||||||
|
|
||||||
assert conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] == 2
|
|
||||||
|
|
||||||
= Craft Packet
|
|
||||||
x = GMLAN(b'\x52\x02\x01\x16\x71\x00\x00\x0c\xaa\xbb')
|
|
||||||
x.load == b'\x00\x0c\xaa\xbb'
|
|
||||||
x.service == 0x52
|
|
||||||
|
|
||||||
= Craft VIN Packet
|
|
||||||
x = GMLAN(b'\x5a\x90'+ raw("WOOOJBF35W1042000"))
|
|
||||||
x.load == b'WOOOJBF35W1042000'
|
|
||||||
x.dataIdentifier == 0x90
|
|
||||||
|
|
||||||
= Test Packet with ECU AddressingScheme2
|
|
||||||
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22')
|
|
||||||
x.memoryAddress == 0x1122
|
|
||||||
x.memorySize == 0x4422
|
|
||||||
|
|
||||||
= Test Packet GMLAN_RMBAPR with ECU AddressingScheme2
|
|
||||||
x = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22')
|
|
||||||
x.memoryAddress == 0x1122
|
|
||||||
x.dataRecord == b'\x44\x22'
|
|
||||||
|
|
||||||
= Craft Packet with ECU AddressingScheme2
|
|
||||||
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22')
|
|
||||||
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x1122, memorySize=0x4422)
|
|
||||||
bytes(x) == bytes(y)
|
|
||||||
|
|
||||||
= Test Packet with ECU AddressingScheme3
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
|
|
||||||
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11')
|
|
||||||
x.memoryAddress == 0x112244
|
|
||||||
x.memorySize == 0x2211
|
|
||||||
|
|
||||||
= Test Packet GMLAN_RMBAPR with ECU AddressingScheme3
|
|
||||||
x = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11')
|
|
||||||
x.memoryAddress == 0x112244
|
|
||||||
x.dataRecord == b'\x22\x11'
|
|
||||||
|
|
||||||
= Craft Packet with ECU AddressingScheme3
|
|
||||||
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11')
|
|
||||||
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x112244, memorySize=0x2211)
|
|
||||||
bytes(x) == bytes(y)
|
|
||||||
|
|
||||||
= Test Packet with ECU AddressingScheme4
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
|
|
||||||
x = GMLAN()/GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00')
|
|
||||||
x.memoryAddress == 0x11224422
|
|
||||||
x.memorySize == 0x1100
|
|
||||||
|
|
||||||
= Test Packet GMLAN_RMBAPR with ECU AddressingScheme4
|
|
||||||
x = GMLAN()/GMLAN_RMBAPR(b'\x11\x22\x44\x22\x11\x00')
|
|
||||||
x.memoryAddress == 0x11224422
|
|
||||||
x.dataRecord == b'\x11\x00'
|
|
||||||
|
|
||||||
= Craft Packet with ECU AddressingScheme4
|
|
||||||
x = GMLAN() / GMLAN_RMBA(b'\x11\x22\x44\x22\x11\x00')
|
|
||||||
y = GMLAN()/GMLAN_RMBA(memoryAddress=0x11224422, memorySize=0x1100)
|
|
||||||
bytes(x) == bytes(y)
|
|
||||||
|
|
||||||
= Craft Packet for RequestDownload2
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
|
|
||||||
x = GMLAN(b'\x34\x12\x08\x15')
|
|
||||||
x.service == 0x34
|
|
||||||
x.dataFormatIdentifier == 0x12
|
|
||||||
x.memorySize == 0x815
|
|
||||||
|
|
||||||
y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x815)
|
|
||||||
bytes(y) == bytes(x)
|
|
||||||
|
|
||||||
= Craft Packet for RequestDownload3
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
|
|
||||||
x = GMLAN(b'\x34\x12\x08\x15\x00')
|
|
||||||
x.service == 0x34
|
|
||||||
x.dataFormatIdentifier == 0x12
|
|
||||||
x.memorySize == 0x81500
|
|
||||||
|
|
||||||
y = GMLAN()/GMLAN_RD(dataFormatIdentifier=0x12, memorySize=0x81500)
|
|
||||||
bytes(y) == bytes(x)
|
|
||||||
|
|
||||||
= Craft Packet for RequestDownload4
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
|
|
||||||
x = GMLAN(b'\x34\x12\x08\x15\x00\x11')
|
|
||||||
x.service == 0x34
|
|
||||||
x.dataFormatIdentifier == 0x12
|
|
||||||
x.memorySize == 0x8150011
|
|
||||||
|
|
||||||
= Craft Packet for RFRD1
|
|
||||||
x = GMLAN(b'\x12\x01')
|
|
||||||
x.service == 0x12
|
|
||||||
x.subfunction == 1
|
|
||||||
|
|
||||||
= Craft Packet for RFRD2
|
|
||||||
x = GMLAN(b'\x12\x02\x01\x02\x03\x04')
|
|
||||||
x.service == 0x12
|
|
||||||
x.subfunction == 2
|
|
||||||
x.dtc.failureRecordNumber == 1
|
|
||||||
x.dtc.DTCHighByte == 2
|
|
||||||
x.dtc.DTCLowByte == 3
|
|
||||||
x.dtc.DTCFailureType == 4
|
|
||||||
|
|
||||||
= Craft Packet for RFRDPR_RFRI
|
|
||||||
x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04')
|
|
||||||
x.service == 0x52
|
|
||||||
x.subfunction == 1
|
|
||||||
x.failureRecordDataStructureIdentifier == 0
|
|
||||||
x.dtcs[0].failureRecordNumber == 1
|
|
||||||
x.dtcs[0].DTCHighByte == 2
|
|
||||||
x.dtcs[0].DTCLowByte == 3
|
|
||||||
x.dtcs[0].DTCFailureType == 4
|
|
||||||
|
|
||||||
= Craft Packet for RFRDPR_RFRI
|
|
||||||
x = GMLAN(b'\x52\x01\x00\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04\x01\x02\x03\x04')
|
|
||||||
x.service == 0x52
|
|
||||||
x.subfunction == 1
|
|
||||||
x.failureRecordDataStructureIdentifier == 0
|
|
||||||
x.dtcs[0].failureRecordNumber == 1
|
|
||||||
x.dtcs[0].DTCHighByte == 2
|
|
||||||
x.dtcs[0].DTCLowByte == 3
|
|
||||||
x.dtcs[0].DTCFailureType == 4
|
|
||||||
x.dtcs[1].failureRecordNumber == 1
|
|
||||||
x.dtcs[1].DTCHighByte == 2
|
|
||||||
x.dtcs[1].DTCLowByte == 3
|
|
||||||
x.dtcs[1].DTCFailureType == 4
|
|
||||||
x.dtcs[2].failureRecordNumber == 1
|
|
||||||
x.dtcs[2].DTCHighByte == 2
|
|
||||||
x.dtcs[2].DTCLowByte == 3
|
|
||||||
x.dtcs[2].DTCFailureType == 4
|
|
||||||
x.dtcs[3].failureRecordNumber == 1
|
|
||||||
x.dtcs[3].DTCHighByte == 2
|
|
||||||
x.dtcs[3].DTCLowByte == 3
|
|
||||||
x.dtcs[3].DTCFailureType == 4
|
|
||||||
|
|
||||||
= Craft Packet for RFRDPR_RFRP
|
|
||||||
x = GMLAN(b'\x52\x02\x01\x02\x03\x04deadbeef')
|
|
||||||
x.service == 0x52
|
|
||||||
x.subfunction == 2
|
|
||||||
x.dtc.failureRecordNumber == 1
|
|
||||||
x.dtc.DTCHighByte == 2
|
|
||||||
x.dtc.DTCLowByte == 3
|
|
||||||
x.dtc.DTCFailureType == 4
|
|
||||||
x.show()
|
|
||||||
x.load == b'deadbeef'
|
|
||||||
|
|
||||||
|
|
||||||
= Craft Packet for RDBI
|
|
||||||
x = GMLAN(b'\x1A\x11')
|
|
||||||
x.service == 0x1A
|
|
||||||
x.dataIdentifier == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for RDBIPR
|
|
||||||
x = GMLAN(b'\x5A\x11deadbeef')
|
|
||||||
x.service == 0x5A
|
|
||||||
x.dataIdentifier == 0x11
|
|
||||||
x.load == b'deadbeef'
|
|
||||||
|
|
||||||
= Craft Packet for RDBPI
|
|
||||||
x = GMLAN(b'\x22\x11\x11\x22\x22\x33\x33\x44\x44\x55\x55\x66\x66\x77\x77\x88\x88\x99\x99')
|
|
||||||
x.service == 0x22
|
|
||||||
x.identifiers[0] == 0x1111
|
|
||||||
x.identifiers[1] == 0x2222
|
|
||||||
x.identifiers[2] == 0x3333
|
|
||||||
x.identifiers[3] == 0x4444
|
|
||||||
x.identifiers[4] == 0x5555
|
|
||||||
x.identifiers[5] == 0x6666
|
|
||||||
x.identifiers[6] == 0x7777
|
|
||||||
x.identifiers[7] == 0x8888
|
|
||||||
x.identifiers[8] == 0x9999
|
|
||||||
|
|
||||||
= Craft Packet for RDBPIPR
|
|
||||||
x = GMLAN(b'\x62\x00\x11deadbeef')
|
|
||||||
x.service == 0x62
|
|
||||||
x.parameterIdentifier == 0x11
|
|
||||||
x.load == b'deadbeef'
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RDBPKTI1
|
|
||||||
x = GMLAN(b'\xAA\x01deadbeef')
|
|
||||||
x.service == 0xAA
|
|
||||||
x.subfunction == 0x01
|
|
||||||
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RDBPKTI3
|
|
||||||
x = GMLAN(b'\xAA\x02deadbeef')
|
|
||||||
x.service == 0xAA
|
|
||||||
x.subfunction == 0x02
|
|
||||||
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RDBPKTI4
|
|
||||||
x = GMLAN(b'\xAA\x03deadbeef')
|
|
||||||
x.service == 0xAA
|
|
||||||
x.subfunction == 0x03
|
|
||||||
x.request_DPIDs == [0x64, 0x65, 0x61, 0x64, 0x62, 0x65, 0x65, 0x66]
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RDBPKTI2
|
|
||||||
x = GMLAN(b'\xAA\x00')
|
|
||||||
x.service == 0xAA
|
|
||||||
x.subfunction == 0
|
|
||||||
|
|
||||||
= Build GMLAN_RDBPKTI1
|
|
||||||
x = GMLAN()/GMLAN_RDBPKTI(subfunction=1, request_DPIDs=[0x64, 0x65])
|
|
||||||
assert b"\xaa\x01de" == bytes(x)
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_SA1
|
|
||||||
x = GMLAN(b'\x27\x01')
|
|
||||||
x.service == 0x27
|
|
||||||
x.subfunction == 1
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_SA2
|
|
||||||
x = GMLAN(b'\x27\x02\xde\xad')
|
|
||||||
x.service == 0x27
|
|
||||||
x.subfunction == 2
|
|
||||||
x.securityKey == 0xdead
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_SAPR1
|
|
||||||
x = GMLAN(b'\x67\x02')
|
|
||||||
x.service == 0x67
|
|
||||||
x.subfunction == 2
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_SAPR2
|
|
||||||
x = GMLAN(b'\x67\x01\xde\xad')
|
|
||||||
x.service == 0x67
|
|
||||||
x.subfunction == 1
|
|
||||||
x.securitySeed == 0xdead
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_DDM
|
|
||||||
x = GMLAN(b'\x2c\x02dead')
|
|
||||||
x.service == 0x2c
|
|
||||||
x.DPIDIdentifier == 2
|
|
||||||
x.PIDData == b'dead'
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_DDMPR
|
|
||||||
x = GMLAN(b'\x6c\x02dead')
|
|
||||||
x.service == 0x6c
|
|
||||||
x.DPIDIdentifier == 2
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_DPBA1
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
|
|
||||||
x = GMLAN(b'\x2D\x02\x02\x11\x11\x33')
|
|
||||||
x.service == 0x2d
|
|
||||||
x.parameterIdentifier == 0x202
|
|
||||||
x.memoryAddress == 0x1111
|
|
||||||
x.memorySize == 0x33
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_DPBA2
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
|
|
||||||
x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x33')
|
|
||||||
x.service == 0x2d
|
|
||||||
x.parameterIdentifier == 0x202
|
|
||||||
x.memoryAddress == 0x111111
|
|
||||||
x.memorySize == 0x33
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_DPBA3
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
|
|
||||||
x = GMLAN(b'\x2D\x02\x02\x11\x11\x11\x11\x33')
|
|
||||||
x.service == 0x2d
|
|
||||||
x.parameterIdentifier == 0x202
|
|
||||||
x.memoryAddress == 0x11111111
|
|
||||||
x.memorySize == 0x33
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_DPBAPR
|
|
||||||
x = GMLAN(b'\x6D\x02\x02')
|
|
||||||
x.service == 0x6d
|
|
||||||
x.parameterIdentifier == 0x202
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RD1
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
|
|
||||||
x = GMLAN(b'\x34\x02\x11\x11')
|
|
||||||
x.service == 0x34
|
|
||||||
x.dataFormatIdentifier == 0x2
|
|
||||||
x.memorySize == 0x1111
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RD2
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
|
|
||||||
x = GMLAN(b'\x34\x02\x11\x11\x11')
|
|
||||||
x.service == 0x34
|
|
||||||
x.dataFormatIdentifier == 0x2
|
|
||||||
x.memorySize == 0x111111
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_RD3
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
|
|
||||||
x = GMLAN(b'\x34\x02\x11\x11\x11\x11')
|
|
||||||
x.service == 0x34
|
|
||||||
x.dataFormatIdentifier == 0x2
|
|
||||||
x.memorySize == 0x11111111
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_TD1
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 2
|
|
||||||
x = GMLAN(b'\x36\x02\x11\x11dead')
|
|
||||||
x.service == 0x36
|
|
||||||
x.subfunction == 0x2
|
|
||||||
x.startingAddress == 0x1111
|
|
||||||
x.dataRecord == b'dead'
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_TD2
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 3
|
|
||||||
x = GMLAN(b'\x36\x02\x11\x11\x11dead')
|
|
||||||
x.service == 0x36
|
|
||||||
x.subfunction == 0x2
|
|
||||||
x.startingAddress == 0x111111
|
|
||||||
x.dataRecord == b'dead'
|
|
||||||
|
|
||||||
= Craft Packet for GMLAN_TD3
|
|
||||||
conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme'] = 4
|
|
||||||
x = GMLAN(b'\x36\x02\x11\x11\x11\x11dead')
|
|
||||||
x.service == 0x36
|
|
||||||
x.subfunction == 0x2
|
|
||||||
x.startingAddress == 0x11111111
|
|
||||||
x.dataRecord == b'dead'
|
|
||||||
|
|
||||||
= Craft Packet for WDBI
|
|
||||||
x = GMLAN(b'\x3b\x11deadbeef')
|
|
||||||
x.service == 0x3b
|
|
||||||
x.dataIdentifier == 0x11
|
|
||||||
x.dataRecord == b'deadbeef'
|
|
||||||
|
|
||||||
= Craft Packet for WDBIPR
|
|
||||||
x = GMLAN(b'\x7b\x11')
|
|
||||||
x.service == 0x7b
|
|
||||||
x.dataIdentifier == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for RPSPR
|
|
||||||
x = GMLAN(b'\xe2\x11')
|
|
||||||
x.service == 0xe2
|
|
||||||
x.programmedState == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for PM
|
|
||||||
x = GMLAN(b'\xA5\x11')
|
|
||||||
x.service == 0xA5
|
|
||||||
x.subfunction == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for RDI
|
|
||||||
x = GMLAN(b'\xA9\x11')
|
|
||||||
x.service == 0xA9
|
|
||||||
x.subfunction == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for RDI_BN
|
|
||||||
x = GMLAN(b'\xA9\x80\x11\x22\x33')
|
|
||||||
x.service == 0xA9
|
|
||||||
x.subfunction == 0x80
|
|
||||||
x.DTCHighByte == 0x11
|
|
||||||
x.DTCLowByte == 0x22
|
|
||||||
x.DTCFailureType == 0x33
|
|
||||||
|
|
||||||
= Craft Packet for RDI_BM1
|
|
||||||
x = GMLAN(b'\xA9\x81\x11')
|
|
||||||
x.service == 0xA9
|
|
||||||
x.subfunction == 0x81
|
|
||||||
x.DTCStatusMask == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for RDI_BM2
|
|
||||||
x = GMLAN(b'\xA9\x82\x11')
|
|
||||||
x.service == 0xA9
|
|
||||||
x.subfunction == 0x82
|
|
||||||
x.DTCStatusMask == 0x11
|
|
||||||
|
|
||||||
= Craft Packet for NR
|
|
||||||
x = GMLAN(b'\x7f\x11\x00\x11\x22')
|
|
||||||
x.service == 0x7f
|
|
||||||
x.requestServiceId == 0x11
|
|
||||||
x.returnCode == 0
|
|
||||||
x.deviceControlLimitExceeded == 0x1122
|
|
||||||
|
|
||||||
= Check not answers
|
|
||||||
y = GMLAN(b'\x11deadbeef')
|
|
||||||
x = GMLAN(b'\x7f\x10\x00\x11\x22')
|
|
||||||
assert not x.answers(y)
|
|
||||||
|
|
||||||
= Check answers 1
|
|
||||||
y = GMLAN(b'\x10deadbeef')
|
|
||||||
x = GMLAN(b'\x7f\x10\x00\x11\x22')
|
|
||||||
assert x.answers(y)
|
|
||||||
|
|
||||||
= Check hashret 1
|
|
||||||
print(y.hashret())
|
|
||||||
print(x.hashret())
|
|
||||||
|
|
||||||
y.hashret() == x.hashret()
|
|
||||||
|
|
||||||
= Check answers 2
|
|
||||||
y = GMLAN()/GMLAN_SA(subfunction=1)
|
|
||||||
x = GMLAN()/GMLAN_SAPR()
|
|
||||||
assert x.answers(y)
|
|
||||||
|
|
||||||
= Check hashret 2
|
|
||||||
y.hashret() == x.hashret()
|
|
||||||
|
|
|
@ -1,339 +0,0 @@
|
||||||
#! /usr/bin/env python
|
|
||||||
|
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Markus Schroetter <project.m.schroetter@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = GMLAN Utilities
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import time
|
|
||||||
from scapy.contrib.automotive.gm.gmlan import GMLAN, GMLAN_SA, GMLAN_RD, \
|
|
||||||
GMLAN_TD, GMLAN_PM, GMLAN_RMBA
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.contrib.isotp import ISOTPSocket
|
|
||||||
from scapy.error import warning, log_loading
|
|
||||||
from scapy.utils import PeriodicSenderThread
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["GMLAN_TesterPresentSender", "GMLAN_InitDiagnostics",
|
|
||||||
"GMLAN_GetSecurityAccess", "GMLAN_RequestDownload",
|
|
||||||
"GMLAN_TransferData", "GMLAN_TransferPayload",
|
|
||||||
"GMLAN_ReadMemoryByAddress", "GMLAN_BroadcastSocket"]
|
|
||||||
|
|
||||||
log_loading.info("\"conf.contribs['GMLAN']"
|
|
||||||
"['treat-response-pending-as-answer']\" set to True). This "
|
|
||||||
"is required by the GMLAN-Utils module to operate "
|
|
||||||
"correctly.")
|
|
||||||
try:
|
|
||||||
conf.contribs['GMLAN']['treat-response-pending-as-answer'] = False
|
|
||||||
except KeyError:
|
|
||||||
conf.contribs['GMLAN'] = {'treat-response-pending-as-answer': False}
|
|
||||||
|
|
||||||
|
|
||||||
class GMLAN_TesterPresentSender(PeriodicSenderThread):
|
|
||||||
def __init__(self, sock, pkt=GMLAN(service="TesterPresent"), interval=2):
|
|
||||||
""" Thread to send TesterPresent messages packets periodically
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket where packet is sent periodically
|
|
||||||
pkt: packet to send
|
|
||||||
interval: interval between two packets
|
|
||||||
"""
|
|
||||||
PeriodicSenderThread.__init__(self, sock, pkt, interval)
|
|
||||||
|
|
||||||
|
|
||||||
def _check_response(resp, verbose):
|
|
||||||
if resp is None:
|
|
||||||
if verbose:
|
|
||||||
print("Timeout.")
|
|
||||||
return False
|
|
||||||
if verbose:
|
|
||||||
resp.show()
|
|
||||||
return resp.sprintf("%GMLAN.service%") != "NegativeResponse"
|
|
||||||
|
|
||||||
|
|
||||||
def _send_and_check_response(sock, req, timeout, verbose):
|
|
||||||
if verbose:
|
|
||||||
print("Sending %s" % repr(req))
|
|
||||||
resp = sock.sr1(req, timeout=timeout, verbose=0)
|
|
||||||
return _check_response(resp, verbose)
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_InitDiagnostics(sock, broadcastsocket=None, timeout=None,
|
|
||||||
verbose=None, retry=0):
|
|
||||||
"""Send messages to put an ECU into an diagnostic/programming state.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket to send the message on.
|
|
||||||
broadcast: socket for broadcasting. If provided some message will be
|
|
||||||
sent as broadcast. Recommended when used on a network with
|
|
||||||
several ECUs.
|
|
||||||
timeout: timeout for sending, receiving or sniffing packages.
|
|
||||||
verbose: set verbosity level
|
|
||||||
retry: number of retries in case of failure.
|
|
||||||
|
|
||||||
Returns true on success.
|
|
||||||
"""
|
|
||||||
if verbose is None:
|
|
||||||
verbose = conf.verb
|
|
||||||
retry = abs(retry)
|
|
||||||
|
|
||||||
while retry >= 0:
|
|
||||||
retry -= 1
|
|
||||||
|
|
||||||
# DisableNormalCommunication
|
|
||||||
p = GMLAN(service="DisableNormalCommunication")
|
|
||||||
if broadcastsocket is None:
|
|
||||||
if not _send_and_check_response(sock, p, timeout, verbose):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
if verbose:
|
|
||||||
print("Sending %s as broadcast" % repr(p))
|
|
||||||
broadcastsocket.send(p)
|
|
||||||
time.sleep(0.05)
|
|
||||||
|
|
||||||
# ReportProgrammedState
|
|
||||||
p = GMLAN(service="ReportProgrammingState")
|
|
||||||
if not _send_and_check_response(sock, p, timeout, verbose):
|
|
||||||
continue
|
|
||||||
# ProgrammingMode requestProgramming
|
|
||||||
p = GMLAN() / GMLAN_PM(subfunction="requestProgrammingMode")
|
|
||||||
if not _send_and_check_response(sock, p, timeout, verbose):
|
|
||||||
continue
|
|
||||||
time.sleep(0.05)
|
|
||||||
|
|
||||||
# InitiateProgramming enableProgramming
|
|
||||||
# No response expected
|
|
||||||
p = GMLAN() / GMLAN_PM(subfunction="enableProgrammingMode")
|
|
||||||
if verbose:
|
|
||||||
print("Sending %s" % repr(p))
|
|
||||||
sock.send(p)
|
|
||||||
time.sleep(0.05)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_GetSecurityAccess(sock, keyFunction, level=1, timeout=None,
|
|
||||||
verbose=None, retry=0):
|
|
||||||
"""Authenticate on ECU. Implements Seey-Key procedure.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket to send the message on.
|
|
||||||
keyFunction: function implementing the key algorithm.
|
|
||||||
level: level of access
|
|
||||||
timeout: timeout for sending, receiving or sniffing packages.
|
|
||||||
verbose: set verbosity level
|
|
||||||
retry: number of retries in case of failure.
|
|
||||||
|
|
||||||
Returns true on success.
|
|
||||||
"""
|
|
||||||
if verbose is None:
|
|
||||||
verbose = conf.verb
|
|
||||||
retry = abs(retry)
|
|
||||||
|
|
||||||
if level % 2 == 0:
|
|
||||||
warning("Parameter Error: Level must be an odd number.")
|
|
||||||
return False
|
|
||||||
|
|
||||||
while retry >= 0:
|
|
||||||
retry -= 1
|
|
||||||
|
|
||||||
request = GMLAN() / GMLAN_SA(subfunction=level)
|
|
||||||
if verbose:
|
|
||||||
print("Requesting seed..")
|
|
||||||
resp = sock.sr1(request, timeout=timeout, verbose=0)
|
|
||||||
if not _check_response(resp, verbose):
|
|
||||||
if verbose:
|
|
||||||
print("Negative Response.")
|
|
||||||
continue
|
|
||||||
|
|
||||||
seed = resp.securitySeed
|
|
||||||
if seed == 0:
|
|
||||||
if verbose:
|
|
||||||
print("ECU security already unlocked. (seed is 0x0000)")
|
|
||||||
return True
|
|
||||||
|
|
||||||
keypkt = GMLAN() / GMLAN_SA(subfunction=level + 1,
|
|
||||||
securityKey=keyFunction(seed))
|
|
||||||
if verbose:
|
|
||||||
print("Responding with key..")
|
|
||||||
resp = sock.sr1(keypkt, timeout=timeout, verbose=0)
|
|
||||||
if resp is None:
|
|
||||||
if verbose:
|
|
||||||
print("Timeout.")
|
|
||||||
continue
|
|
||||||
if verbose:
|
|
||||||
resp.show()
|
|
||||||
if resp.sprintf("%GMLAN.service%") == "SecurityAccessPositiveResponse": # noqa: E501
|
|
||||||
if verbose:
|
|
||||||
print("SecurityAccess granted.")
|
|
||||||
return True
|
|
||||||
# Invalid Key
|
|
||||||
elif resp.sprintf("%GMLAN.service%") == "NegativeResponse" and \
|
|
||||||
resp.sprintf("%GMLAN.returnCode%") == "InvalidKey":
|
|
||||||
if verbose:
|
|
||||||
print("Key invalid")
|
|
||||||
continue
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_RequestDownload(sock, length, timeout=None, verbose=None, retry=0):
|
|
||||||
"""Send RequestDownload message.
|
|
||||||
|
|
||||||
Usually used before calling TransferData.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket to send the message on.
|
|
||||||
length: value for the message's parameter 'unCompressedMemorySize'.
|
|
||||||
timeout: timeout for sending, receiving or sniffing packages.
|
|
||||||
verbose: set verbosity level.
|
|
||||||
retry: number of retries in case of failure.
|
|
||||||
|
|
||||||
Returns true on success.
|
|
||||||
"""
|
|
||||||
if verbose is None:
|
|
||||||
verbose = conf.verb
|
|
||||||
retry = abs(retry)
|
|
||||||
|
|
||||||
while retry >= 0:
|
|
||||||
# RequestDownload
|
|
||||||
pkt = GMLAN() / GMLAN_RD(memorySize=length)
|
|
||||||
resp = sock.sr1(pkt, timeout=timeout, verbose=0)
|
|
||||||
if _check_response(resp, verbose):
|
|
||||||
return True
|
|
||||||
retry -= 1
|
|
||||||
if retry >= 0 and verbose:
|
|
||||||
print("Retrying..")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_TransferData(sock, addr, payload, maxmsglen=None, timeout=None,
|
|
||||||
verbose=None, retry=0):
|
|
||||||
"""Send TransferData message.
|
|
||||||
|
|
||||||
Usually used after calling RequestDownload.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket to send the message on.
|
|
||||||
addr: destination memory address on the ECU.
|
|
||||||
payload: data to be sent.
|
|
||||||
maxmsglen: maximum length of a single iso-tp message. (default:
|
|
||||||
maximum length)
|
|
||||||
timeout: timeout for sending, receiving or sniffing packages.
|
|
||||||
verbose: set verbosity level.
|
|
||||||
retry: number of retries in case of failure.
|
|
||||||
|
|
||||||
Returns true on success.
|
|
||||||
"""
|
|
||||||
if verbose is None:
|
|
||||||
verbose = conf.verb
|
|
||||||
retry = abs(retry)
|
|
||||||
startretry = retry
|
|
||||||
|
|
||||||
scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']
|
|
||||||
if addr < 0 or addr >= 2**(8 * scheme):
|
|
||||||
warning("Error: Invalid address " + hex(addr) + " for scheme " +
|
|
||||||
str(scheme))
|
|
||||||
return False
|
|
||||||
|
|
||||||
# max size of dataRecord according to gmlan protocol
|
|
||||||
if maxmsglen is None or maxmsglen <= 0 or maxmsglen > (4093 - scheme):
|
|
||||||
maxmsglen = (4093 - scheme)
|
|
||||||
|
|
||||||
for i in range(0, len(payload), maxmsglen):
|
|
||||||
retry = startretry
|
|
||||||
while True:
|
|
||||||
if len(payload[i:]) > maxmsglen:
|
|
||||||
transdata = payload[i:i + maxmsglen]
|
|
||||||
else:
|
|
||||||
transdata = payload[i:]
|
|
||||||
pkt = GMLAN() / GMLAN_TD(startingAddress=addr + i,
|
|
||||||
dataRecord=transdata)
|
|
||||||
resp = sock.sr1(pkt, timeout=timeout, verbose=0)
|
|
||||||
if _check_response(resp, verbose):
|
|
||||||
break
|
|
||||||
retry -= 1
|
|
||||||
if retry >= 0:
|
|
||||||
if verbose:
|
|
||||||
print("Retrying..")
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_TransferPayload(sock, addr, payload, maxmsglen=None, timeout=None,
|
|
||||||
verbose=None, retry=0):
|
|
||||||
"""Send data by using GMLAN services.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket to send the data on.
|
|
||||||
addr: destination memory address on the ECU.
|
|
||||||
payload: data to be sent.
|
|
||||||
maxmsglen: maximum length of a single iso-tp message. (default:
|
|
||||||
maximum length)
|
|
||||||
timeout: timeout for sending, receiving or sniffing packages.
|
|
||||||
verbose: set verbosity level.
|
|
||||||
retry: number of retries in case of failure.
|
|
||||||
|
|
||||||
Returns true on success.
|
|
||||||
"""
|
|
||||||
if not GMLAN_RequestDownload(sock, len(payload), timeout=timeout,
|
|
||||||
verbose=verbose, retry=retry):
|
|
||||||
return False
|
|
||||||
if not GMLAN_TransferData(sock, addr, payload, maxmsglen=maxmsglen,
|
|
||||||
timeout=timeout, verbose=verbose, retry=retry):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_ReadMemoryByAddress(sock, addr, length, timeout=None,
|
|
||||||
verbose=None, retry=0):
|
|
||||||
"""Read data from ECU memory.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
sock: socket to send the data on.
|
|
||||||
addr: source memory address on the ECU.
|
|
||||||
length: bytes to read
|
|
||||||
timeout: timeout for sending, receiving or sniffing packages.
|
|
||||||
verbose: set verbosity level.
|
|
||||||
retry: number of retries in case of failure.
|
|
||||||
|
|
||||||
Returns the bytes read.
|
|
||||||
"""
|
|
||||||
if verbose is None:
|
|
||||||
verbose = conf.verb
|
|
||||||
retry = abs(retry)
|
|
||||||
|
|
||||||
scheme = conf.contribs['GMLAN']['GMLAN_ECU_AddressingScheme']
|
|
||||||
if addr < 0 or addr >= 2**(8 * scheme):
|
|
||||||
warning("Error: Invalid address " + hex(addr) + " for scheme " +
|
|
||||||
str(scheme))
|
|
||||||
return None
|
|
||||||
|
|
||||||
# max size of dataRecord according to gmlan protocol
|
|
||||||
if length <= 0 or length > (4094 - scheme):
|
|
||||||
warning("Error: Invalid length " + hex(length) + " for scheme " +
|
|
||||||
str(scheme) + ". Choose between 0x1 and " + hex(4094 - scheme))
|
|
||||||
return None
|
|
||||||
|
|
||||||
while retry >= 0:
|
|
||||||
# RequestDownload
|
|
||||||
pkt = GMLAN() / GMLAN_RMBA(memoryAddress=addr, memorySize=length)
|
|
||||||
resp = sock.sr1(pkt, timeout=timeout, verbose=0)
|
|
||||||
if _check_response(resp, verbose):
|
|
||||||
return resp.dataRecord
|
|
||||||
retry -= 1
|
|
||||||
if retry >= 0 and verbose:
|
|
||||||
print("Retrying..")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def GMLAN_BroadcastSocket(interface):
|
|
||||||
"""Returns a GMLAN broadcast socket using interface."""
|
|
||||||
return ISOTPSocket(interface, sid=0x101, did=0x0, basecls=GMLAN,
|
|
||||||
extended_addr=0xfe)
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive obd specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive obd specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,177 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import FieldLenField, FieldListField, StrFixedLenField, \
|
|
||||||
ByteField, ShortField, FlagsField, XByteField, PacketListField
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
from scapy.contrib.automotive.obd.services import OBD_S09
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs#Service_09
|
|
||||||
# for further information
|
|
||||||
# IID = Information IDentification
|
|
||||||
|
|
||||||
class OBD_S09_PR_Record(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField("iid", 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S09_PR(Packet):
|
|
||||||
name = "Infotype IDs"
|
|
||||||
fields_desc = [
|
|
||||||
PacketListField("data_records", [], OBD_S09_PR_Record)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S09 \
|
|
||||||
and all(r.iid in other.iid for r in self.data_records)
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID00(OBD_Packet):
|
|
||||||
name = "IID_00_Service9SupportedInformationTypes"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_iids', 0, 32, [
|
|
||||||
'IID20',
|
|
||||||
'IID1F',
|
|
||||||
'IID1E',
|
|
||||||
'IID1D',
|
|
||||||
'IID1C',
|
|
||||||
'IID1B',
|
|
||||||
'IID1A',
|
|
||||||
'IID19',
|
|
||||||
'IID18',
|
|
||||||
'IID17',
|
|
||||||
'IID16',
|
|
||||||
'IID15',
|
|
||||||
'IID14',
|
|
||||||
'IID13',
|
|
||||||
'IID12',
|
|
||||||
'IID11',
|
|
||||||
'IID10',
|
|
||||||
'IID0F',
|
|
||||||
'IID0E',
|
|
||||||
'IID0D',
|
|
||||||
'IID0C',
|
|
||||||
'IID0B',
|
|
||||||
'IID0A',
|
|
||||||
'IID09',
|
|
||||||
'IID08',
|
|
||||||
'IID07',
|
|
||||||
'IID06',
|
|
||||||
'IID05',
|
|
||||||
'IID04',
|
|
||||||
'IID03',
|
|
||||||
'IID02',
|
|
||||||
'IID01'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_IID_MessageCount(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField('message_count', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID01(_OBD_IID_MessageCount):
|
|
||||||
name = "IID_01_VinMessageCount"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID03(_OBD_IID_MessageCount):
|
|
||||||
name = "IID_03_CalibrationIdMessageCount"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID05(_OBD_IID_MessageCount):
|
|
||||||
name = "IID_05_CalibrationVerificationNumbersMessageCount"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID07(_OBD_IID_MessageCount):
|
|
||||||
name = "IID_07_InUsePerformanceTrackingMessageCount"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID09(_OBD_IID_MessageCount):
|
|
||||||
name = "IID_09_EcuNameMessageCount"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID02(OBD_Packet):
|
|
||||||
name = "IID_02_VehicleIdentificationNumber"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='vehicle_identification_numbers',
|
|
||||||
fmt='B'),
|
|
||||||
FieldListField('vehicle_identification_numbers', [],
|
|
||||||
StrFixedLenField('', b'', 17),
|
|
||||||
count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID04(OBD_Packet):
|
|
||||||
name = "IID_04_CalibrationId"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='calibration_identifications',
|
|
||||||
fmt='B'),
|
|
||||||
FieldListField('calibration_identifications', [],
|
|
||||||
StrFixedLenField('', b'', 16),
|
|
||||||
count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID06(OBD_Packet):
|
|
||||||
name = "IID_06_CalibrationVerificationNumbers"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None,
|
|
||||||
count_of='calibration_verification_numbers', fmt='B'),
|
|
||||||
FieldListField('calibration_verification_numbers', [],
|
|
||||||
StrFixedLenField('', b'', 4),
|
|
||||||
count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID08(OBD_Packet):
|
|
||||||
name = "IID_08_InUsePerformanceTracking"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='data', fmt='B'),
|
|
||||||
FieldListField('data', [],
|
|
||||||
ShortField('', 0),
|
|
||||||
count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID0A(OBD_Packet):
|
|
||||||
name = "IID_0A_EcuName"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='ecu_names', fmt='B'),
|
|
||||||
FieldListField('ecu_names', [],
|
|
||||||
StrFixedLenField('', b'', 20),
|
|
||||||
count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_IID0B(OBD_Packet):
|
|
||||||
name = "IID_0B_InUsePerformanceTrackingForCompressionIgnitionVehicles"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='data', fmt='B'),
|
|
||||||
FieldListField('data', [],
|
|
||||||
ShortField('', 0),
|
|
||||||
count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID00, iid=0x00)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID01, iid=0x01)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID02, iid=0x02)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID03, iid=0x03)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID04, iid=0x04)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID05, iid=0x05)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID06, iid=0x06)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID07, iid=0x07)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID08, iid=0x08)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID09, iid=0x09)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID0A, iid=0x0A)
|
|
||||||
bind_layers(OBD_S09_PR_Record, OBD_IID0B, iid=0x0B)
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive obd specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,554 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import FlagsField, ScalingField, ByteEnumField, \
|
|
||||||
MultipleTypeField, ShortField, ShortEnumField, PacketListField
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
from scapy.contrib.automotive.obd.services import OBD_S06
|
|
||||||
|
|
||||||
|
|
||||||
def _unit_and_scaling_fields(name):
|
|
||||||
return [
|
|
||||||
(ScalingField(name, 0, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1),
|
|
||||||
(ScalingField(name, 0, scaling=0.1, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x3),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x4),
|
|
||||||
(ScalingField(name, 0, scaling=0.0000305, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x5),
|
|
||||||
(ScalingField(name, 0, scaling=0.000305, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x6),
|
|
||||||
(ScalingField(name, 0, scaling=0.25, unit="rpm", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x7),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="km/h", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x8),
|
|
||||||
(ScalingField(name, 0, unit="km/h", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x9),
|
|
||||||
(ScalingField(name, 0, scaling=0.122, unit="mV", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xA),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="V", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xB),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="V", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xC),
|
|
||||||
(ScalingField(name, 0, scaling=0.00390625, unit="mA", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xD),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="A", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xE),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="A", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xF),
|
|
||||||
(ScalingField(name, 0, unit="ms", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x10),
|
|
||||||
(ScalingField(name, 0, scaling=100, unit="ms", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x11),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="s", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x12),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="mOhm", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x13),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="Ohm", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x14),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="kOhm", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x15),
|
|
||||||
(ScalingField(name, -40, scaling=0.1, unit="deg. C",
|
|
||||||
offset=-40, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x16),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="kPa", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x17),
|
|
||||||
(ScalingField(name, 0, scaling=0.0117, unit="kPa", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x18),
|
|
||||||
(ScalingField(name, 0, scaling=0.079, unit="kPa", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x19),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="kPa", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1A),
|
|
||||||
(ScalingField(name, 0, scaling=10, unit="kPa", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1B),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="deg.", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1C),
|
|
||||||
(ScalingField(name, 0, scaling=0.5, unit="deg.", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1D),
|
|
||||||
(ScalingField(name, 0, scaling=0.0000305, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1E),
|
|
||||||
(ScalingField(name, 0, scaling=0.05, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x1F),
|
|
||||||
(ScalingField(name, 0, scaling=0.0039062, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x20),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="mHz", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x21),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="Hz", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x22),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="KHz", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x23),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="counts", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x24),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="km", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x25),
|
|
||||||
(ScalingField(name, 0, scaling=0.1, unit="mV/ms", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x26),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="g/s", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x27),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="g/s", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x28),
|
|
||||||
(ScalingField(name, 0, scaling=0.25, unit="Pa/s", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x29),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="kg/h", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2A),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="switches", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2B),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="g/cyl", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2C),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="mg/stroke", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2D),
|
|
||||||
(ShortEnumField(name, 0, {0: "false", 1: "true"}),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2E),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="%", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x2F),
|
|
||||||
(ScalingField(name, 0, scaling=0.001526, unit="%", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x30),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="L", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x31),
|
|
||||||
(ScalingField(name, 0, scaling=0.0000305, unit="inch", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x32),
|
|
||||||
(ScalingField(name, 0, scaling=0.00024414, fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x33),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="min", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x34),
|
|
||||||
(ScalingField(name, 0, scaling=10, unit="ms", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x35),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="g", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x36),
|
|
||||||
(ScalingField(name, 0, scaling=0.1, unit="g", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x37),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="g", fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x38),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="%", offset=-327.68,
|
|
||||||
fmt='H'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x39),
|
|
||||||
(ScalingField(name, 0, scaling=1, fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x81),
|
|
||||||
(ScalingField(name, 0, scaling=0.1, fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x82),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x83),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x84),
|
|
||||||
(ScalingField(name, 0, scaling=0.0000305, fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x85),
|
|
||||||
(ScalingField(name, 0, scaling=0.000305, fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x86),
|
|
||||||
(ScalingField(name, 0, scaling=0.122, unit="mV", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x8A),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="V", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x8B),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="V", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x8C),
|
|
||||||
(ScalingField(name, 0, scaling=0.00390625, unit="mA", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x8D),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="A", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x8E),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="ms", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x90),
|
|
||||||
(ScalingField(name, 0, scaling=0.1, unit="deg. C", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x96),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="deg.", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x9C),
|
|
||||||
(ScalingField(name, 0, scaling=0.5, unit="deg.", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0x9D),
|
|
||||||
(ScalingField(name, 0, scaling=1, unit="g/s", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xA8),
|
|
||||||
(ScalingField(name, 0, scaling=0.25, unit="Pa/s", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xA9),
|
|
||||||
(ScalingField(name, 0, scaling=0.01, unit="%", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xAF),
|
|
||||||
(ScalingField(name, 0, scaling=0.003052, unit="%", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xB0),
|
|
||||||
(ScalingField(name, 0, scaling=2, unit="mV/s", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xB1),
|
|
||||||
(ScalingField(name, 0, scaling=0.001, unit="kPa", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xFD),
|
|
||||||
(ScalingField(name, 0, scaling=0.25, unit="Pa", fmt='h'),
|
|
||||||
lambda pkt: pkt.unit_and_scaling_id == 0xFE)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _mid_flags(basemid):
|
|
||||||
return [
|
|
||||||
'MID%02X' % (basemid + 0x20),
|
|
||||||
'MID%02X' % (basemid + 0x1F),
|
|
||||||
'MID%02X' % (basemid + 0x1E),
|
|
||||||
'MID%02X' % (basemid + 0x1D),
|
|
||||||
'MID%02X' % (basemid + 0x1C),
|
|
||||||
'MID%02X' % (basemid + 0x1B),
|
|
||||||
'MID%02X' % (basemid + 0x1A),
|
|
||||||
'MID%02X' % (basemid + 0x19),
|
|
||||||
'MID%02X' % (basemid + 0x18),
|
|
||||||
'MID%02X' % (basemid + 0x17),
|
|
||||||
'MID%02X' % (basemid + 0x16),
|
|
||||||
'MID%02X' % (basemid + 0x15),
|
|
||||||
'MID%02X' % (basemid + 0x14),
|
|
||||||
'MID%02X' % (basemid + 0x13),
|
|
||||||
'MID%02X' % (basemid + 0x12),
|
|
||||||
'MID%02X' % (basemid + 0x11),
|
|
||||||
'MID%02X' % (basemid + 0x10),
|
|
||||||
'MID%02X' % (basemid + 0x0F),
|
|
||||||
'MID%02X' % (basemid + 0x0E),
|
|
||||||
'MID%02X' % (basemid + 0x0D),
|
|
||||||
'MID%02X' % (basemid + 0x0C),
|
|
||||||
'MID%02X' % (basemid + 0x0B),
|
|
||||||
'MID%02X' % (basemid + 0x0A),
|
|
||||||
'MID%02X' % (basemid + 0x09),
|
|
||||||
'MID%02X' % (basemid + 0x08),
|
|
||||||
'MID%02X' % (basemid + 0x07),
|
|
||||||
'MID%02X' % (basemid + 0x06),
|
|
||||||
'MID%02X' % (basemid + 0x05),
|
|
||||||
'MID%02X' % (basemid + 0x04),
|
|
||||||
'MID%02X' % (basemid + 0x03),
|
|
||||||
'MID%02X' % (basemid + 0x02),
|
|
||||||
'MID%02X' % (basemid + 0x01)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MIDXX(OBD_Packet):
|
|
||||||
standardized_test_ids = {
|
|
||||||
1: "TID_01_RichToLeanSensorThresholdVoltage",
|
|
||||||
2: "TID_02_LeanToRichSensorThresholdVoltage",
|
|
||||||
3: "TID_03_LowSensorVoltageForSwitchTimeCalculation",
|
|
||||||
4: "TID_04_HighSensorVoltageForSwitchTimeCalculation",
|
|
||||||
5: "TID_05_RichToLeanSensorSwitchTime",
|
|
||||||
6: "TID_06_LeanToRichSensorSwitchTime",
|
|
||||||
7: "TID_07_MinimumSensorVoltageForTestCycle",
|
|
||||||
8: "TID_08_MaximumSensorVoltageForTestCycle",
|
|
||||||
9: "TID_09_TimeBetweenSensorTransitions",
|
|
||||||
10: "TID_0A_SensorPeriod"}
|
|
||||||
unit_and_scaling_ids = {
|
|
||||||
0x01: "Raw Value",
|
|
||||||
0x02: "Raw Value",
|
|
||||||
0x03: "Raw Value",
|
|
||||||
0x04: "Raw Value",
|
|
||||||
0x05: "Raw Value",
|
|
||||||
0x06: "Raw Value",
|
|
||||||
0x07: "rotational frequency",
|
|
||||||
0x08: "Speed",
|
|
||||||
0x09: "Speed",
|
|
||||||
0x0A: "Voltage",
|
|
||||||
0x0B: "Voltage",
|
|
||||||
0x0C: "Voltage",
|
|
||||||
0x0D: "Current",
|
|
||||||
0x0E: "Current",
|
|
||||||
0x0F: "Current",
|
|
||||||
0x10: "Time",
|
|
||||||
0x11: "Time",
|
|
||||||
0x12: "Time",
|
|
||||||
0x13: "Resistance",
|
|
||||||
0x14: "Resistance",
|
|
||||||
0x15: "Resistance",
|
|
||||||
0x16: "Temperature",
|
|
||||||
0x17: "Pressure (Gauge)",
|
|
||||||
0x18: "Pressure (Air pressure)",
|
|
||||||
0x19: "Pressure (Fuel pressure)",
|
|
||||||
0x1A: "Pressure (Gauge)",
|
|
||||||
0x1B: "Pressure (Diesel pressure)",
|
|
||||||
0x1C: "Angle",
|
|
||||||
0x1D: "Angle",
|
|
||||||
0x1E: "Equivalence ratio (lambda)",
|
|
||||||
0x1F: "Air/Fuel ratio",
|
|
||||||
0x20: "Ratio",
|
|
||||||
0x21: "Frequency",
|
|
||||||
0x22: "Frequency",
|
|
||||||
0x23: "Frequency",
|
|
||||||
0x24: "Counts",
|
|
||||||
0x25: "Distance",
|
|
||||||
0x26: "Voltage per time",
|
|
||||||
0x27: "Mass per time",
|
|
||||||
0x28: "Mass per time",
|
|
||||||
0x29: "Pressure per time",
|
|
||||||
0x2A: "Mass per time",
|
|
||||||
0x2B: "Switches",
|
|
||||||
0x2C: "Mass per cylinder",
|
|
||||||
0x2D: "Mass per stroke",
|
|
||||||
0x2E: "True/False",
|
|
||||||
0x2F: "Percent",
|
|
||||||
0x30: "Percent",
|
|
||||||
0x31: "volume",
|
|
||||||
0x32: "length",
|
|
||||||
0x33: "Equivalence ratio (lambda)",
|
|
||||||
0x34: "Time",
|
|
||||||
0x35: "Time",
|
|
||||||
0x36: "Weight",
|
|
||||||
0x37: "Weight",
|
|
||||||
0x38: "Weight",
|
|
||||||
0x39: "Percent",
|
|
||||||
0x81: "Raw Value",
|
|
||||||
0x82: "Raw Value",
|
|
||||||
0x83: "Raw Value",
|
|
||||||
0x84: "Raw Value",
|
|
||||||
0x85: "Raw Value",
|
|
||||||
0x86: "Raw Value",
|
|
||||||
0x8A: "Voltage",
|
|
||||||
0x8B: "Voltage",
|
|
||||||
0x8C: "Voltage",
|
|
||||||
0x8D: "Current",
|
|
||||||
0x8E: "Current",
|
|
||||||
0x90: "Time",
|
|
||||||
0x96: "Temperature",
|
|
||||||
0x9C: "Angle",
|
|
||||||
0x9D: "Angle",
|
|
||||||
0xA8: "Mass per time",
|
|
||||||
0xA9: "Pressure per time",
|
|
||||||
0xAF: "Percent",
|
|
||||||
0xB0: "Percent",
|
|
||||||
0xB1: "Voltage per time",
|
|
||||||
0xFD: "Pressure",
|
|
||||||
0xFE: "Pressure"
|
|
||||||
}
|
|
||||||
|
|
||||||
name = "OBD MID data record"
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField("standardized_test_id", 1, standardized_test_ids),
|
|
||||||
ByteEnumField("unit_and_scaling_id", 1, unit_and_scaling_ids),
|
|
||||||
MultipleTypeField(_unit_and_scaling_fields("test_value"),
|
|
||||||
ShortField("test_value", 0)),
|
|
||||||
MultipleTypeField(_unit_and_scaling_fields("min_limit"),
|
|
||||||
ShortField("min_limit", 0)),
|
|
||||||
MultipleTypeField(_unit_and_scaling_fields("max_limit"),
|
|
||||||
ShortField("max_limit", 0)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MID00(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_mids', 0, 32, _mid_flags(0x00)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MID20(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_mids', 0, 32, _mid_flags(0x20)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MID40(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_mids', 0, 32, _mid_flags(0x40)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MID60(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_mids', 0, 32, _mid_flags(0x60)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MID80(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_mids', 0, 32, _mid_flags(0x80)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_MIDA0(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_mids', 0, 32, _mid_flags(0xA0)),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S06_PR_Record(Packet):
|
|
||||||
on_board_monitoring_ids = {
|
|
||||||
0x00: "OBD Monitor IDs supported ($01 - $20)",
|
|
||||||
0x01: "Oxygen Sensor Monitor Bank 1 - Sensor 1",
|
|
||||||
0x02: "Oxygen Sensor Monitor Bank 1 - Sensor 2",
|
|
||||||
0x03: "Oxygen Sensor Monitor Bank 1 - Sensor 3",
|
|
||||||
0x04: "Oxygen Sensor Monitor Bank 1 - Sensor 4",
|
|
||||||
0x05: "Oxygen Sensor Monitor Bank 2 - Sensor 1",
|
|
||||||
0x06: "Oxygen Sensor Monitor Bank 2 - Sensor 2",
|
|
||||||
0x07: "Oxygen Sensor Monitor Bank 2 - Sensor 3",
|
|
||||||
0x08: "Oxygen Sensor Monitor Bank 2 - Sensor 4",
|
|
||||||
0x09: "Oxygen Sensor Monitor Bank 3 - Sensor 1",
|
|
||||||
0x0A: "Oxygen Sensor Monitor Bank 3 - Sensor 2",
|
|
||||||
0x0B: "Oxygen Sensor Monitor Bank 3 - Sensor 3",
|
|
||||||
0x0C: "Oxygen Sensor Monitor Bank 3 - Sensor 4",
|
|
||||||
0x0D: "Oxygen Sensor Monitor Bank 4 - Sensor 1",
|
|
||||||
0x0E: "Oxygen Sensor Monitor Bank 4 - Sensor 2",
|
|
||||||
0x0F: "Oxygen Sensor Monitor Bank 4 - Sensor 3",
|
|
||||||
0x10: "Oxygen Sensor Monitor Bank 4 - Sensor 4",
|
|
||||||
0x20: "OBD Monitor IDs supported ($21 - $40)",
|
|
||||||
0x21: "Catalyst Monitor Bank 1",
|
|
||||||
0x22: "Catalyst Monitor Bank 2",
|
|
||||||
0x23: "Catalyst Monitor Bank 3",
|
|
||||||
0x24: "Catalyst Monitor Bank 4",
|
|
||||||
0x32: "EGR Monitor Bank 2",
|
|
||||||
0x33: "EGR Monitor Bank 3",
|
|
||||||
0x34: "EGR Monitor Bank 4",
|
|
||||||
0x35: "VVT Monitor Bank 1",
|
|
||||||
0x36: "VVT Monitor Bank 2",
|
|
||||||
0x37: "VVT Monitor Bank 3",
|
|
||||||
0x38: "VVT Monitor Bank 4",
|
|
||||||
0x39: "EVAP Monitor (Cap Off / 0.150\")",
|
|
||||||
0x3A: "EVAP Monitor (0.090\")",
|
|
||||||
0x3B: "EVAP Monitor (0.040\")",
|
|
||||||
0x3C: "EVAP Monitor (0.020\")",
|
|
||||||
0x3D: "Purge Flow Monitor",
|
|
||||||
0x40: "OBD Monitor IDs supported ($41 - $60)",
|
|
||||||
0x41: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 1",
|
|
||||||
0x42: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 2",
|
|
||||||
0x43: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 3",
|
|
||||||
0x44: "Oxygen Sensor Heater Monitor Bank 1 - Sensor 4",
|
|
||||||
0x45: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 1",
|
|
||||||
0x46: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 2",
|
|
||||||
0x47: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 3",
|
|
||||||
0x48: "Oxygen Sensor Heater Monitor Bank 2 - Sensor 4",
|
|
||||||
0x49: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 1",
|
|
||||||
0x4A: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 2",
|
|
||||||
0x4B: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 3",
|
|
||||||
0x4C: "Oxygen Sensor Heater Monitor Bank 3 - Sensor 4",
|
|
||||||
0x4D: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 1",
|
|
||||||
0x4E: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 2",
|
|
||||||
0x4F: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 3",
|
|
||||||
0x50: "Oxygen Sensor Heater Monitor Bank 4 - Sensor 4",
|
|
||||||
0x60: "OBD Monitor IDs supported ($61 - $80)",
|
|
||||||
0x61: "Heated Catalyst Monitor Bank 1",
|
|
||||||
0x62: "Heated Catalyst Monitor Bank 2",
|
|
||||||
0x63: "Heated Catalyst Monitor Bank 3",
|
|
||||||
0x64: "Heated Catalyst Monitor Bank 4",
|
|
||||||
0x71: "Secondary Air Monitor 1",
|
|
||||||
0x72: "Secondary Air Monitor 2",
|
|
||||||
0x73: "Secondary Air Monitor 3",
|
|
||||||
0x74: "Secondary Air Monitor 4",
|
|
||||||
0x80: "OBD Monitor IDs supported ($81 - $A0)",
|
|
||||||
0x81: "Fuel System Monitor Bank 1",
|
|
||||||
0x82: "Fuel System Monitor Bank 2",
|
|
||||||
0x83: "Fuel System Monitor Bank 3",
|
|
||||||
0x84: "Fuel System Monitor Bank 4",
|
|
||||||
0x85: "Boost Pressure Control Monitor Bank 1",
|
|
||||||
0x86: "Boost Pressure Control Monitor Bank 2",
|
|
||||||
0x90: "NOx Adsorber Monitor Bank 1",
|
|
||||||
0x91: "NOx Adsorber Monitor Bank 2",
|
|
||||||
0x98: "NOx Catalyst Monitor Bank 1",
|
|
||||||
0x99: "NOx Catalyst Monitor Bank 2",
|
|
||||||
0xA0: "OBD Monitor IDs supported ($A1 - $C0)",
|
|
||||||
0xA1: "Misfire Monitor General Data",
|
|
||||||
0xA2: "Misfire Cylinder 1 Data",
|
|
||||||
0xA3: "Misfire Cylinder 2 Data",
|
|
||||||
0xA4: "Misfire Cylinder 3 Data",
|
|
||||||
0xA5: "Misfire Cylinder 4 Data",
|
|
||||||
0xA6: "Misfire Cylinder 5 Data",
|
|
||||||
0xA7: "Misfire Cylinder 6 Data",
|
|
||||||
0xA8: "Misfire Cylinder 7 Data",
|
|
||||||
0xA9: "Misfire Cylinder 8 Data",
|
|
||||||
0xAA: "Misfire Cylinder 9 Data",
|
|
||||||
0xAB: "Misfire Cylinder 10 Data",
|
|
||||||
0xAC: "Misfire Cylinder 11 Data",
|
|
||||||
0xAD: "Misfire Cylinder 12 Data",
|
|
||||||
0xB0: "PM Filter Monitor Bank 1",
|
|
||||||
0xB1: "PM Filter Monitor Bank 2"
|
|
||||||
}
|
|
||||||
name = "On-Board diagnostic monitoring ID"
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField("mid", 0, on_board_monitoring_ids),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S06_PR(Packet):
|
|
||||||
name = "On-Board monitoring IDs"
|
|
||||||
fields_desc = [
|
|
||||||
PacketListField("data_records", [], OBD_S06_PR_Record)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S06 \
|
|
||||||
and all(r.mid in other.mid for r in self.data_records)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MID00, mid=0x00)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x01)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x02)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x03)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x04)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x05)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x06)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x07)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x08)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x09)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0A)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0B)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0C)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0D)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0E)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x0F)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x10)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MID20, mid=0x20)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x21)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x22)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x23)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x24)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x32)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x33)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x34)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x35)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x36)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x37)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x38)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x39)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3A)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3B)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3C)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x3D)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MID40, mid=0x40)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x41)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x42)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x43)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x44)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x45)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x46)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x47)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x48)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x49)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4A)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4B)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4C)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4D)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4E)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x4F)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x50)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MID60, mid=0x60)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x61)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x62)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x63)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x64)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x71)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x72)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x73)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x74)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MID80, mid=0x80)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x81)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x82)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x83)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x84)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x85)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x86)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x90)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x91)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x98)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0x99)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDA0, mid=0xA0)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA1)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA2)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA3)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA4)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA5)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA6)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA7)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA8)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xA9)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAA)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAB)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAC)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xAD)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xB0)
|
|
||||||
bind_layers(OBD_S06_PR_Record, OBD_MIDXX, mid=0xB1)
|
|
|
@ -1,105 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = On Board Diagnostic Protocol (OBD-II)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from scapy.contrib.automotive.obd.iid.iids import *
|
|
||||||
from scapy.contrib.automotive.obd.mid.mids import *
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids import *
|
|
||||||
from scapy.contrib.automotive.obd.tid.tids import *
|
|
||||||
from scapy.contrib.automotive.obd.services import *
|
|
||||||
from scapy.packet import bind_layers, NoPayload
|
|
||||||
from scapy.error import log_loading
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.fields import XByteEnumField
|
|
||||||
from scapy.contrib.isotp import ISOTP
|
|
||||||
|
|
||||||
try:
|
|
||||||
if conf.contribs['OBD']['treat-response-pending-as-answer']:
|
|
||||||
pass
|
|
||||||
except KeyError:
|
|
||||||
log_loading.info("Specify \"conf.contribs['OBD'] = "
|
|
||||||
"{'treat-response-pending-as-answer': True}\" to treat "
|
|
||||||
"a negative response 'requestCorrectlyReceived-"
|
|
||||||
"ResponsePending' as answer of a request. \n"
|
|
||||||
"The default value is False.")
|
|
||||||
conf.contribs['OBD'] = {'treat-response-pending-as-answer': False}
|
|
||||||
|
|
||||||
|
|
||||||
class OBD(ISOTP):
|
|
||||||
services = {
|
|
||||||
0x01: 'CurrentPowertrainDiagnosticDataRequest',
|
|
||||||
0x02: 'PowertrainFreezeFrameDataRequest',
|
|
||||||
0x03: 'EmissionRelatedDiagnosticTroubleCodesRequest',
|
|
||||||
0x04: 'ClearResetDiagnosticTroubleCodesRequest',
|
|
||||||
0x05: 'OxygenSensorMonitoringTestResultsRequest',
|
|
||||||
0x06: 'OnBoardMonitoringTestResultsRequest',
|
|
||||||
0x07: 'PendingEmissionRelatedDiagnosticTroubleCodesRequest',
|
|
||||||
0x08: 'ControlOperationRequest',
|
|
||||||
0x09: 'VehicleInformationRequest',
|
|
||||||
0x0A: 'PermanentDiagnosticTroubleCodesRequest',
|
|
||||||
0x41: 'CurrentPowertrainDiagnosticDataResponse',
|
|
||||||
0x42: 'PowertrainFreezeFrameDataResponse',
|
|
||||||
0x43: 'EmissionRelatedDiagnosticTroubleCodesResponse',
|
|
||||||
0x44: 'ClearResetDiagnosticTroubleCodesResponse',
|
|
||||||
0x45: 'OxygenSensorMonitoringTestResultsResponse',
|
|
||||||
0x46: 'OnBoardMonitoringTestResultsResponse',
|
|
||||||
0x47: 'PendingEmissionRelatedDiagnosticTroubleCodesResponse',
|
|
||||||
0x48: 'ControlOperationResponse',
|
|
||||||
0x49: 'VehicleInformationResponse',
|
|
||||||
0x4A: 'PermanentDiagnosticTroubleCodesResponse',
|
|
||||||
0x7f: 'NegativeResponse'}
|
|
||||||
|
|
||||||
name = "On-board diagnostics"
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('service', 0, services)
|
|
||||||
]
|
|
||||||
|
|
||||||
def hashret(self):
|
|
||||||
if self.service == 0x7f:
|
|
||||||
return struct.pack('B', self.request_service_id & ~0x40)
|
|
||||||
return struct.pack('B', self.service & ~0x40)
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
if other.__class__ != self.__class__:
|
|
||||||
return False
|
|
||||||
if self.service == 0x7f:
|
|
||||||
return self.payload.answers(other)
|
|
||||||
if self.service == (other.service + 0x40):
|
|
||||||
if isinstance(self.payload, NoPayload) or \
|
|
||||||
isinstance(other.payload, NoPayload):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return self.payload.answers(other.payload)
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# Service Bindings
|
|
||||||
|
|
||||||
bind_layers(OBD, OBD_S01, service=0x01)
|
|
||||||
bind_layers(OBD, OBD_S02, service=0x02)
|
|
||||||
bind_layers(OBD, OBD_S03, service=0x03)
|
|
||||||
bind_layers(OBD, OBD_S04, service=0x04)
|
|
||||||
bind_layers(OBD, OBD_S06, service=0x06)
|
|
||||||
bind_layers(OBD, OBD_S07, service=0x07)
|
|
||||||
bind_layers(OBD, OBD_S08, service=0x08)
|
|
||||||
bind_layers(OBD, OBD_S09, service=0x09)
|
|
||||||
bind_layers(OBD, OBD_S0A, service=0x0A)
|
|
||||||
|
|
||||||
bind_layers(OBD, OBD_S01_PR, service=0x41)
|
|
||||||
bind_layers(OBD, OBD_S02_PR, service=0x42)
|
|
||||||
bind_layers(OBD, OBD_S03_PR, service=0x43)
|
|
||||||
bind_layers(OBD, OBD_S04_PR, service=0x44)
|
|
||||||
bind_layers(OBD, OBD_S06_PR, service=0x46)
|
|
||||||
bind_layers(OBD, OBD_S07_PR, service=0x47)
|
|
||||||
bind_layers(OBD, OBD_S08_PR, service=0x48)
|
|
||||||
bind_layers(OBD, OBD_S09_PR, service=0x49)
|
|
||||||
bind_layers(OBD, OBD_S0A_PR, service=0x4A)
|
|
||||||
bind_layers(OBD, OBD_NR, service=0x7F)
|
|
|
@ -1,921 +0,0 @@
|
||||||
% Regression tests for the OBD layer
|
|
||||||
|
|
||||||
# More information at http://www.secdev.org/projects/UTscapy/
|
|
||||||
|
|
||||||
|
|
||||||
############
|
|
||||||
############
|
|
||||||
|
|
||||||
+ Basic operations
|
|
||||||
|
|
||||||
= Load module
|
|
||||||
|
|
||||||
load_contrib("automotive.obd.obd")
|
|
||||||
|
|
||||||
|
|
||||||
= Check if positive response answers
|
|
||||||
|
|
||||||
req = OBD(b'\x01\x2f')
|
|
||||||
res = OBD(b'\x41\x2f\x1a')
|
|
||||||
assert res.answers(req)
|
|
||||||
|
|
||||||
|
|
||||||
= Check hashret
|
|
||||||
|
|
||||||
assert req.hashret() == res.hashret()
|
|
||||||
|
|
||||||
= Check if negative response answers
|
|
||||||
|
|
||||||
req = OBD(b'\x01\x2f')
|
|
||||||
res = OBD(b'\x7f\x01\x11')
|
|
||||||
assert res.answers(req)
|
|
||||||
|
|
||||||
|
|
||||||
= Check hashret
|
|
||||||
|
|
||||||
assert req.hashret() == res.hashret()
|
|
||||||
|
|
||||||
|
|
||||||
= Check hashret for Service 0x40
|
|
||||||
|
|
||||||
req = OBD(b'\x40')
|
|
||||||
res = OBD(b'\x7F\x40\x11')
|
|
||||||
assert req.hashret() == res.hashret()
|
|
||||||
|
|
||||||
|
|
||||||
= Check hashret for Service 0x51
|
|
||||||
|
|
||||||
req = OBD(b'\x51')
|
|
||||||
res = OBD(b'\x7F\x51\x11')
|
|
||||||
assert req.hashret() == res.hashret()
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 01 PID 00
|
|
||||||
|
|
||||||
p = OBD(b'\x01\x00')
|
|
||||||
assert p.service == 0x01
|
|
||||||
assert p.pid[0] == 0x00
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 01 PID 75
|
|
||||||
|
|
||||||
p = OBD(b'\x01\x75')
|
|
||||||
assert p.service == 0x01
|
|
||||||
assert p.pid[0] == 0x75
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 01 PID 78
|
|
||||||
|
|
||||||
|
|
||||||
p = OBD(b'\x01\x78')
|
|
||||||
assert p.service == 0x01
|
|
||||||
assert p.pid[0] == 0x78
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 01 PID 7F
|
|
||||||
|
|
||||||
p = OBD(b'\x01\x7F')
|
|
||||||
assert p.service == 0x01
|
|
||||||
assert p.pid[0] == 0x7F
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 01 PID 89
|
|
||||||
|
|
||||||
p = OBD(b'\x01\x89')
|
|
||||||
assert p.service == 0x01
|
|
||||||
assert p.pid[0] == 0x89
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 02 PID 00
|
|
||||||
|
|
||||||
p = OBD(b'\x02\x00\x01')
|
|
||||||
assert p.service == 0x02
|
|
||||||
assert p.requests[0].pid == 0x00
|
|
||||||
assert p.requests[0].frame_no == 0x01
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 02 PID 75
|
|
||||||
|
|
||||||
p = OBD(b'\x02\x75\x01')
|
|
||||||
assert p.service == 0x02
|
|
||||||
assert p.requests[0].pid == 0x75
|
|
||||||
assert p.requests[0].frame_no == 0x01
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 02 PID 78
|
|
||||||
|
|
||||||
p = OBD(b'\x02\x78\x01')
|
|
||||||
assert p.service == 0x02
|
|
||||||
assert p.requests[0].pid == 0x78
|
|
||||||
assert p.requests[0].frame_no == 0x01
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 02 PID 7F
|
|
||||||
|
|
||||||
p = OBD(b'\x02\x7F\x01')
|
|
||||||
assert p.service == 0x02
|
|
||||||
assert p.requests[0].pid == 0x7F
|
|
||||||
assert p.requests[0].frame_no == 0x01
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 02 PID 89
|
|
||||||
|
|
||||||
p = OBD(b'\x02\x89\x01')
|
|
||||||
assert p.service == 0x02
|
|
||||||
assert p.requests[0].pid == 0x89
|
|
||||||
assert p.requests[0].frame_no == 0x01
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 03
|
|
||||||
|
|
||||||
p = OBD(b'\x03')
|
|
||||||
assert p.service == 0x03
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 06
|
|
||||||
|
|
||||||
p = OBD(b'\x06\x01')
|
|
||||||
assert p.service == 0x06
|
|
||||||
assert p.mid[0] == 0x01
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 06 MID 00
|
|
||||||
|
|
||||||
p = OBD(b'\x06\x00')
|
|
||||||
assert p.service == 0x06
|
|
||||||
assert p.mid[0] == 0x00
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 06 MID 00,01,02,03,04
|
|
||||||
|
|
||||||
p = OBD(b'\x06\x00\x01\x02\x03\x04')
|
|
||||||
assert p.service == 0x06
|
|
||||||
assert p.mid[0] == 0x00
|
|
||||||
assert p.mid[1] == 0x01
|
|
||||||
assert p.mid[2] == 0x02
|
|
||||||
assert p.mid[3] == 0x03
|
|
||||||
assert p.mid[4] == 0x04
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 06 MID 00
|
|
||||||
|
|
||||||
p = OBD(b'\x46\x00\x00\x00\x00\x00')
|
|
||||||
assert p.service == 0x46
|
|
||||||
assert p.data_records[0].mid == 0x00
|
|
||||||
assert p.data_records[0].supported_mids == ""
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 06 MID 00 and MID 20
|
|
||||||
|
|
||||||
p = OBD(b'\x46\x00\x01\x02\x03\x04\x20\x01\x02\x03\x04')
|
|
||||||
assert p.service == 0x46
|
|
||||||
assert p.data_records[0].mid == 0x00
|
|
||||||
assert p.data_records[0].supported_mids == "MID1E+MID18+MID17+MID0F+MID08"
|
|
||||||
assert p.data_records[1].mid == 0x20
|
|
||||||
assert p.data_records[1].supported_mids == "MID3E+MID38+MID37+MID2F+MID28"
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 06 MID 00, 20, 40, 60, 80, A0
|
|
||||||
|
|
||||||
p = OBD(b'\x46\x00\x01\x02\x03\x04\x20\x01\x02\x03\x04\x40\x01\x02\x03\x04\x60\x01\x02\x03\x04\x80\x01\x02\x03\x04\xA0\x01\x02\x03\x04')
|
|
||||||
assert p.service == 0x46
|
|
||||||
assert p.data_records[0].mid == 0x00
|
|
||||||
assert p.data_records[0].supported_mids == "MID1E+MID18+MID17+MID0F+MID08"
|
|
||||||
assert p.data_records[1].mid == 0x20
|
|
||||||
assert p.data_records[1].supported_mids == "MID3E+MID38+MID37+MID2F+MID28"
|
|
||||||
assert p.data_records[2].mid == 0x40
|
|
||||||
assert p.data_records[2].supported_mids == "MID5E+MID58+MID57+MID4F+MID48"
|
|
||||||
assert p.data_records[3].mid == 0x60
|
|
||||||
assert p.data_records[3].supported_mids == "MID7E+MID78+MID77+MID6F+MID68"
|
|
||||||
assert p.data_records[4].mid == 0x80
|
|
||||||
assert p.data_records[4].supported_mids == "MID9E+MID98+MID97+MID8F+MID88"
|
|
||||||
assert p.data_records[5].mid == 0xA0
|
|
||||||
assert p.data_records[5].supported_mids == "MIDBE+MIDB8+MIDB7+MIDAF+MIDA8"
|
|
||||||
assert len(p.data_records) == 6
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 06 MID 01
|
|
||||||
|
|
||||||
p = OBD(b'\x46\x01\x01\x0A\x0B\xB0\x0B\xB0\x0B\xB0\x01\x05\x10\x00\x48\x00\x00\x00\x64\x01\x85\x24\x00\x96\x00\x4B\xFF\xFF')
|
|
||||||
assert p.service == 0x46
|
|
||||||
assert p.data_records[0].mid == 0x01
|
|
||||||
assert p.data_records[0].standardized_test_id == 1
|
|
||||||
assert p.data_records[0].unit_and_scaling_id == 10
|
|
||||||
assert p.data_records[0].test_value == 365.024
|
|
||||||
assert p.data_records[0].min_limit == 365.024
|
|
||||||
assert p.data_records[0].max_limit == 365.024
|
|
||||||
assert "Voltage" in p.data_records[0].__repr__()
|
|
||||||
assert "365.024 mV" in p.data_records[0].__repr__()
|
|
||||||
assert p.data_records[1].mid == 0x01
|
|
||||||
assert p.data_records[1].standardized_test_id == 5
|
|
||||||
assert p.data_records[1].unit_and_scaling_id == 16
|
|
||||||
assert p.data_records[1].test_value == 72
|
|
||||||
assert p.data_records[1].min_limit == 0
|
|
||||||
assert p.data_records[1].max_limit == 100
|
|
||||||
assert "Time" in p.data_records[1].__repr__()
|
|
||||||
assert "72 ms" in p.data_records[1].__repr__()
|
|
||||||
assert p.data_records[2].mid == 0x01
|
|
||||||
assert p.data_records[2].standardized_test_id == 0x85
|
|
||||||
assert p.data_records[2].unit_and_scaling_id == 0x24
|
|
||||||
assert p.data_records[2].test_value == 150
|
|
||||||
assert p.data_records[2].min_limit == 75
|
|
||||||
assert p.data_records[2].max_limit == 65535
|
|
||||||
assert "Counts" in p.data_records[2].__repr__()
|
|
||||||
assert "150 counts" in p.data_records[2].__repr__()
|
|
||||||
assert len(p.data_records) == 3
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 06 MID 21
|
|
||||||
|
|
||||||
p = OBD(b'\x46\x21\x87\x2F\x00\x00\x00\x00\x00\x00')
|
|
||||||
p.show()
|
|
||||||
assert p.service == 0x46
|
|
||||||
assert p.data_records[0].mid == 0x21
|
|
||||||
assert p.data_records[0].standardized_test_id == 135
|
|
||||||
assert p.data_records[0].unit_and_scaling_id == 0x2F
|
|
||||||
assert p.data_records[0].test_value == 0
|
|
||||||
assert p.data_records[0].min_limit == 0
|
|
||||||
assert p.data_records[0].max_limit == 0
|
|
||||||
assert "Percent" in p.data_records[0].__repr__()
|
|
||||||
assert "0 %" in p.data_records[0].__repr__()
|
|
||||||
assert len(p.data_records) == 1
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 09 IID 00
|
|
||||||
|
|
||||||
p = OBD(b'\x09\x00')
|
|
||||||
assert p.service == 0x09
|
|
||||||
assert p.iid[0] == 0x00
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 09 IID 02
|
|
||||||
|
|
||||||
p = OBD(b'\x09\x02')
|
|
||||||
assert p.service == 0x09
|
|
||||||
assert p.iid[0] == 0x02
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 09 IID 04
|
|
||||||
|
|
||||||
p = OBD(b'\x09\x04')
|
|
||||||
assert p.service == 0x09
|
|
||||||
assert p.iid[0] == 0x04
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 09 IID 00 and IID 02 and IID 04
|
|
||||||
|
|
||||||
p = OBD(b'\x09\x00\x02\x04')
|
|
||||||
assert p.service == 0x09
|
|
||||||
assert p.iid[0] == 0x00
|
|
||||||
assert p.iid[1] == 0x02
|
|
||||||
assert p.iid[2] == 0x04
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a request for Service 09 IID 0A
|
|
||||||
|
|
||||||
p = OBD(b'\x09\x0A')
|
|
||||||
assert p.service == 0x09
|
|
||||||
assert p.iid[0] == 0x0A
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 01 PID 75
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x75\x0a\x00\x11\x22\x33\x44\x55')
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 0x75
|
|
||||||
assert p.data_records[0].reserved == 0
|
|
||||||
assert p.data_records[0].turbo_a_turbine_outlet_temperature_supported == 1
|
|
||||||
assert p.data_records[0].turbo_a_turbine_inlet_temperature_supported == 0
|
|
||||||
assert p.data_records[0].turbo_a_compressor_outlet_temperature_supported == 1
|
|
||||||
assert p.data_records[0].turbo_a_compressor_inlet_temperature_supported == 0
|
|
||||||
assert p.data_records[0].turbocharger_a_compressor_inlet_temperature == 0x00-40
|
|
||||||
assert p.data_records[0].turbocharger_a_compressor_outlet_temperature == 0x11-40
|
|
||||||
assert p.data_records[0].turbocharger_a_turbine_inlet_temperature == \
|
|
||||||
round((0x2233 * 0.1) - 40, 3)
|
|
||||||
assert p.data_records[0].turbocharger_a_turbine_outlet_temperature == \
|
|
||||||
round((0x4455 * 0.1) - 40, 3)
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 01 PID 00 and PID 20
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x00\xBF\xBF\xA8\x91\x20\x80\x00\x00\x00')
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 0
|
|
||||||
assert p.data_records[0].supported_pids == "PID20+PID1C+PID19+PID15+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID09+PID08+PID07+PID06+PID05+PID04+PID03+PID01"
|
|
||||||
assert p.data_records[1].pid == 0x20
|
|
||||||
assert p.data_records[1].supported_pids == "PID21"
|
|
||||||
assert len(p.data_records) == 2
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 01 PID 05,01,15,0C,03
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x05\x6e\x01\x83\x33\xff\x63\x15\xa0\x78\x0c\x0a\x6b\x03\x02\x00')
|
|
||||||
p.show()
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 5
|
|
||||||
assert p.data_records[0].data == 70.0
|
|
||||||
assert p.data_records[1].pid == 0x1
|
|
||||||
assert p.data_records[2].pid == 0x15
|
|
||||||
assert p.data_records[2].outputVoltage == 0.8
|
|
||||||
assert p.data_records[2].trim == -6.25
|
|
||||||
assert p.data_records[3].pid == 12
|
|
||||||
assert p.data_records[3].data == 666.75
|
|
||||||
assert p.data_records[4].pid == 3
|
|
||||||
assert p.data_records[4].fuel_system1 == 0x02
|
|
||||||
assert p.data_records[4].fuel_system2 == 0
|
|
||||||
assert len(p.data_records) == 5
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x00\xBF\xBF\xA8\x91\x20\x80\x00\x00\x00')
|
|
||||||
p.show()
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 0
|
|
||||||
assert p.data_records[0].supported_pids == "PID20+PID1C+PID19+PID15+PID13+PID11+PID10+PID0F+PID0E+PID0D+PID0C+PID0B+PID09+PID08+PID07+PID06+PID05+PID04+PID03+PID01"
|
|
||||||
assert p.data_records[1].pid == 0x20
|
|
||||||
assert p.data_records[1].supported_pids == "PID21"
|
|
||||||
assert len(p.data_records) == 2
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 01 PID 78
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x78ABCDEFGHI')
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 0x78
|
|
||||||
assert p.data_records[0].reserved == 4
|
|
||||||
assert p.data_records[0].sensor1_supported == 1
|
|
||||||
assert p.data_records[0].sensor2_supported == 0
|
|
||||||
assert p.data_records[0].sensor3_supported == 0
|
|
||||||
assert p.data_records[0].sensor4_supported == 0
|
|
||||||
assert p.data_records[0].sensor1 == 1656.3
|
|
||||||
assert p.data_records[0].sensor2 == 1707.7
|
|
||||||
assert p.data_records[0].sensor3 == 1759.1
|
|
||||||
assert p.data_records[0].sensor4 == 1810.5
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 01 PID 7F
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x7F\x0a'
|
|
||||||
b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
|
|
||||||
b'\x01\x02\x03\x04\x05\x06\x07\x08'
|
|
||||||
b'\x00\x11\x22\x33\x44\x55\x66\x77')
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 0x7F
|
|
||||||
assert p.data_records[0].reserved == 1
|
|
||||||
assert p.data_records[0].total_with_pto_active_supported == 0
|
|
||||||
assert p.data_records[0].total_idle_supported == 1
|
|
||||||
assert p.data_records[0].total_supported == 0
|
|
||||||
assert p.data_records[0].total == 0xFFFFFFFFFFFFFFFF
|
|
||||||
assert p.data_records[0].total_idle == 0x0102030405060708
|
|
||||||
assert p.data_records[0].total_with_pto_active == 0x0011223344556677
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 01 PID 89
|
|
||||||
|
|
||||||
p = OBD(b'\x41\x89ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP')
|
|
||||||
assert p.service == 0x41
|
|
||||||
assert p.data_records[0].pid == 0x89
|
|
||||||
assert p.data_records[0].data == b'ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 02 PID 75
|
|
||||||
|
|
||||||
p = OBD(b'\x42\x75\01\x0a\x00\x11\x22\x33\x44\x55')
|
|
||||||
assert p.service == 0x42
|
|
||||||
assert p.data_records[0].pid == 0x75
|
|
||||||
assert p.data_records[0].frame_no == 0x01
|
|
||||||
assert p.data_records[0].reserved == 0
|
|
||||||
assert p.data_records[0].turbo_a_turbine_outlet_temperature_supported == 1
|
|
||||||
assert p.data_records[0].turbo_a_turbine_inlet_temperature_supported == 0
|
|
||||||
assert p.data_records[0].turbo_a_compressor_outlet_temperature_supported == 1
|
|
||||||
assert p.data_records[0].turbo_a_compressor_inlet_temperature_supported == 0
|
|
||||||
assert p.data_records[0].turbocharger_a_compressor_inlet_temperature == 0x00 - 40
|
|
||||||
assert p.data_records[0].turbocharger_a_compressor_outlet_temperature == 0x11 - 40
|
|
||||||
assert p.data_records[0].turbocharger_a_turbine_inlet_temperature == \
|
|
||||||
round((0x2233 * 0.1) - 40, 3)
|
|
||||||
assert p.data_records[0].turbocharger_a_turbine_outlet_temperature == \
|
|
||||||
round((0x4455 * 0.1) - 40, 3)
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 02 PID 78
|
|
||||||
|
|
||||||
p = OBD(b'\x42\x78\x05ABCDEFGHI')
|
|
||||||
assert p.service == 0x42
|
|
||||||
assert p.data_records[0].pid == 0x78
|
|
||||||
assert p.data_records[0].frame_no == 0x05
|
|
||||||
assert p.data_records[0].reserved == 4
|
|
||||||
assert p.data_records[0].sensor1_supported == 1
|
|
||||||
assert p.data_records[0].sensor2_supported == 0
|
|
||||||
assert p.data_records[0].sensor3_supported == 0
|
|
||||||
assert p.data_records[0].sensor4_supported == 0
|
|
||||||
assert p.data_records[0].sensor1 == 1656.3
|
|
||||||
assert p.data_records[0].sensor2 == 1707.7
|
|
||||||
assert p.data_records[0].sensor3 == 1759.1
|
|
||||||
assert p.data_records[0].sensor4 == 1810.5
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 02 PID 7F
|
|
||||||
|
|
||||||
p = OBD(b'\x42\x7F\x01\x03'
|
|
||||||
b'\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF'
|
|
||||||
b'\x01\x02\x03\x04\x05\x06\x07\x08'
|
|
||||||
b'\x00\x11\x22\x33\x44\x55\x66\x77')
|
|
||||||
assert p.service == 0x42
|
|
||||||
assert p.data_records[0].pid == 0x7F
|
|
||||||
assert p.data_records[0].frame_no == 0x01
|
|
||||||
assert p.data_records[0].reserved == 0
|
|
||||||
assert p.data_records[0].total_with_pto_active_supported == 0
|
|
||||||
assert p.data_records[0].total_idle_supported == 1
|
|
||||||
assert p.data_records[0].total_supported == 1
|
|
||||||
assert p.data_records[0].total == 0xFFFFFFFFFFFFFFFF
|
|
||||||
assert p.data_records[0].total_idle == 0x0102030405060708
|
|
||||||
assert p.data_records[0].total_with_pto_active == 0x0011223344556677
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 02 PID 89
|
|
||||||
|
|
||||||
p = OBD(b'\x42\x89\x01ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP')
|
|
||||||
assert p.service == 0x42
|
|
||||||
assert p.data_records[0].pid == 0x89
|
|
||||||
assert p.data_records[0].frame_no == 0x01
|
|
||||||
assert p.data_records[0].data == b'ABCDEFGHIKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 02 PID 0C, 05, 04
|
|
||||||
|
|
||||||
p = OBD(b'\x42\x0c\x00\x20\x80\x04\x00\x80\x05\x00\x28')
|
|
||||||
assert p.service == 0x42
|
|
||||||
assert p.data_records[0].pid == 0x0C
|
|
||||||
assert p.data_records[0].frame_no == 0x0
|
|
||||||
assert p.data_records[0].data == 2080
|
|
||||||
assert p.data_records[1].pid == 0x04
|
|
||||||
assert p.data_records[1].frame_no == 0x0
|
|
||||||
assert p.data_records[1].data == 50.196
|
|
||||||
assert p.data_records[2].pid == 0x05
|
|
||||||
assert p.data_records[2].frame_no == 0x0
|
|
||||||
assert p.data_records[2].data == 0.0
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 03
|
|
||||||
|
|
||||||
p = OBD(b'\x43\x06\x01\x43\x01\x96\x02\x34\x02\xcd\x03\x57\x0a\x24')
|
|
||||||
assert p.service == 0x43
|
|
||||||
assert p.count == 6
|
|
||||||
assert bytes(p.dtcs[0]) == b'\x01\x43'
|
|
||||||
assert bytes(p.dtcs[1]) == b'\x01\x96'
|
|
||||||
assert bytes(p.dtcs[2]) == b'\x02\x34'
|
|
||||||
assert bytes(p.dtcs[3]) == b'\x02\xcd'
|
|
||||||
assert bytes(p.dtcs[4]) == b'\x03\x57'
|
|
||||||
assert bytes(p.dtcs[5]) == b'\x0a\x24'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 07
|
|
||||||
|
|
||||||
p = OBD(b'\x47\x06\x01\x43\x01\x96\x02\x34\x02\xcd\x03\x57\x0a\x24')
|
|
||||||
assert p.service == 0x47
|
|
||||||
assert p.count == 6
|
|
||||||
assert bytes(p.dtcs[0]) == b'\x01\x43'
|
|
||||||
assert bytes(p.dtcs[1]) == b'\x01\x96'
|
|
||||||
assert bytes(p.dtcs[2]) == b'\x02\x34'
|
|
||||||
assert bytes(p.dtcs[3]) == b'\x02\xcd'
|
|
||||||
assert bytes(p.dtcs[4]) == b'\x03\x57'
|
|
||||||
assert bytes(p.dtcs[5]) == b'\x0a\x24'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 08 Tid 00
|
|
||||||
|
|
||||||
p = OBD(b'\x48\x00ABCD')
|
|
||||||
assert p.service == 0x48
|
|
||||||
assert p.data_records[0].tid == 0x00
|
|
||||||
assert p.data_records[0].supported_tids == "TID1E+TID1A+TID18+TID17+TID12+TID0F+TID0A+TID08+TID02"
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 08 Tid 01
|
|
||||||
|
|
||||||
p = OBD(b'\x48\x01\x00\x00"\xffd')
|
|
||||||
assert p.service == 0x48
|
|
||||||
assert p.data_records[0].tid == 0x01
|
|
||||||
assert p.data_records[0].data_a == 0.0
|
|
||||||
assert p.data_records[0].data_b == 0.0
|
|
||||||
assert p.data_records[0].data_c == 0.17
|
|
||||||
assert p.data_records[0].data_d == 1.275
|
|
||||||
assert p.data_records[0].data_e == 0.5
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 08 Tid 05
|
|
||||||
|
|
||||||
p = OBD(b'\x48\x05\x00\x00\x2b\xff\x7d')
|
|
||||||
assert p.service == 0x48
|
|
||||||
assert p.data_records[0].tid == 0x05
|
|
||||||
assert p.data_records[0].data_a == 0.0
|
|
||||||
assert p.data_records[0].data_b == 0.0
|
|
||||||
assert p.data_records[0].data_c == 0.172
|
|
||||||
assert p.data_records[0].data_d == 1.02
|
|
||||||
assert p.data_records[0].data_e == 0.5
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 08 Tid 09
|
|
||||||
|
|
||||||
p = OBD(b'\x48\x09\x00\x00\x04\x1a\x0c')
|
|
||||||
assert p.service == 0x48
|
|
||||||
assert p.data_records[0].tid == 0x09
|
|
||||||
assert p.data_records[0].data_a == 0.0
|
|
||||||
assert p.data_records[0].data_b == 0.0
|
|
||||||
assert p.data_records[0].data_c == 0.16
|
|
||||||
assert p.data_records[0].data_d == 1.04
|
|
||||||
assert p.data_records[0].data_e == 0.48
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 00
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x00ABCD')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x00
|
|
||||||
assert p.data_records[0].supported_iids == "IID1E+IID1A+IID18+IID17+IID12+IID0F+IID0A+IID08+IID02"
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 02 with one VIN
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x02\x01W0L000051T2123456')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x02
|
|
||||||
assert p.data_records[0].count == 0x01
|
|
||||||
assert p.data_records[0].vehicle_identification_numbers[0] == b'W0L000051T2123456'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 02 with two VINs
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x02\x02W0L000051T2123456W0L000051T2123456')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x02
|
|
||||||
assert p.data_records[0].count == 0x02
|
|
||||||
assert p.data_records[0].vehicle_identification_numbers[0] == b'W0L000051T2123456'
|
|
||||||
assert p.data_records[0].vehicle_identification_numbers[1] == b'W0L000051T2123456'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 04 with one CID
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x04\x01ABCDEFGHIJKLMNOP')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x04
|
|
||||||
assert p.data_records[0].count == 0x01
|
|
||||||
assert p.data_records[0].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 04 with two CID
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x04\x02ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x04
|
|
||||||
assert p.data_records[0].count == 0x02
|
|
||||||
assert p.data_records[0].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
assert p.data_records[0].calibration_identifications[1] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 06
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x06\x02ABCDEFGH')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x06
|
|
||||||
assert p.data_records[0].count == 0x02
|
|
||||||
assert p.data_records[0].calibration_verification_numbers[0] == b'ABCD'
|
|
||||||
assert p.data_records[0].calibration_verification_numbers[1] == b'EFGH'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 08
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x08\x09\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\xFF\xFF')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x08
|
|
||||||
assert p.data_records[0].count == 0x09
|
|
||||||
assert p.data_records[0].data[0] == 1
|
|
||||||
assert p.data_records[0].data[1] == 2
|
|
||||||
assert p.data_records[0].data[2] == 3
|
|
||||||
assert p.data_records[0].data[3] == 4
|
|
||||||
assert p.data_records[0].data[4] == 5
|
|
||||||
assert p.data_records[0].data[5] == 6
|
|
||||||
assert p.data_records[0].data[6] == 7
|
|
||||||
assert p.data_records[0].data[7] == 8
|
|
||||||
assert p.data_records[0].data[8] == 65535
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 0A
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x0A\x01ECM\x00-Engine Control\x00')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x0A
|
|
||||||
assert p.data_records[0].count == 0x01
|
|
||||||
assert p.data_records[0].ecu_names[0] == b'ECM\x00-Engine Control\x00'
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 0B
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x0B\x05\x00\x01\x00\x02\x00\x03\x00\x04\xFF\xFF')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x0B
|
|
||||||
assert p.data_records[0].count == 0x05
|
|
||||||
assert p.data_records[0].data[0] == 1
|
|
||||||
assert p.data_records[0].data[1] == 2
|
|
||||||
assert p.data_records[0].data[2] == 3
|
|
||||||
assert p.data_records[0].data[3] == 4
|
|
||||||
assert p.data_records[0].data[4] == 65535
|
|
||||||
|
|
||||||
|
|
||||||
= Check dissecting a response for Service 09 IID 02 and IID 04
|
|
||||||
|
|
||||||
p = OBD(b'\x49\x02\x01ABCDEFGHIJKLMNOPQ\x04\x01ABCDEFGHIJKLMNOP')
|
|
||||||
assert p.service == 0x49
|
|
||||||
assert p.data_records[0].iid == 0x02
|
|
||||||
assert p.data_records[0].count == 0x01
|
|
||||||
assert p.data_records[0].vehicle_identification_numbers[0] == b'ABCDEFGHIJKLMNOPQ'
|
|
||||||
assert p.data_records[1].iid == 0x04
|
|
||||||
assert p.data_records[1].count == 0x01
|
|
||||||
assert p.data_records[1].calibration_identifications[0] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x02'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
assert b[3:20] == b'ABCDEFGHIJKLMNOPQ'
|
|
||||||
assert b[20:21] == b'\x04'
|
|
||||||
assert b[21:22] == b'\x01'
|
|
||||||
assert b[22:] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 01 PID 00
|
|
||||||
|
|
||||||
p = OBD()/OBD_S01(pid=0x00)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x01'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 01 PID 75
|
|
||||||
|
|
||||||
p = OBD()/OBD_S01(pid=0x75)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x01'
|
|
||||||
assert b[1:2] == b'\x75'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 01 PID 78
|
|
||||||
|
|
||||||
p = OBD()/OBD_S01(pid=0x78)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x01'
|
|
||||||
assert b[1:2] == b'\x78'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 01 PID 7F
|
|
||||||
|
|
||||||
p = OBD()/OBD_S01(pid=0x7F)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x01'
|
|
||||||
assert b[1:2] == b'\x7F'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 01 PID 89
|
|
||||||
|
|
||||||
p = OBD()/OBD_S01(pid=0x89)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x01'
|
|
||||||
assert b[1:2] == b'\x89'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 02 PID 00
|
|
||||||
|
|
||||||
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x00, frame_no=0x01)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x02'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 02 PID 75
|
|
||||||
|
|
||||||
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x75, frame_no=0x01)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x02'
|
|
||||||
assert b[1:2] == b'\x75'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 02 PID 78
|
|
||||||
|
|
||||||
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x78, frame_no=0x01)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x02'
|
|
||||||
assert b[1:2] == b'\x78'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 02 PID 7F
|
|
||||||
|
|
||||||
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x7F, frame_no=0x01)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x02'
|
|
||||||
assert b[1:2] == b'\x7F'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 02 PID 89
|
|
||||||
|
|
||||||
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x89, frame_no=0x01)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x02'
|
|
||||||
assert b[1:2] == b'\x89'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 03
|
|
||||||
|
|
||||||
p = OBD()/OBD_S03()
|
|
||||||
assert p.service == 0x03
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 02 PID 7F
|
|
||||||
|
|
||||||
p = OBD()/OBD_S02(requests=[OBD_S02_Record(pid=0x7F, frame_no=0x01)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x02'
|
|
||||||
assert b[1:2] == b'\x7F'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 09 IID 00
|
|
||||||
|
|
||||||
p = OBD()/OBD_S09(iid=0x00)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x09'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 09 IID 02
|
|
||||||
|
|
||||||
p = OBD()/OBD_S09(iid=0x02)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x09'
|
|
||||||
assert b[1:2] == b'\x02'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 09 IID 04
|
|
||||||
|
|
||||||
p = OBD()/OBD_S09(iid=0x04)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x09'
|
|
||||||
assert b[1:2] == b'\x04'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 09 IID 00 and IID 02 and IID 04
|
|
||||||
|
|
||||||
p = OBD()/OBD_S09(iid=[0x00, 0x02, 0x04])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x09'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
assert b[2:3] == b'\x02'
|
|
||||||
assert b[3:4] == b'\x04'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a request for Service 09 IID 0A
|
|
||||||
|
|
||||||
p = OBD()/OBD_S09(iid=0x0A)
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x09'
|
|
||||||
assert b[1:2] == b'\x0A'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 03
|
|
||||||
|
|
||||||
p = OBD()/OBD_S03_PR(dtcs=[OBD_DTC(), OBD_DTC(location='Powertrain', code1=1, code2=3, code3=0, code4=1)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x43'
|
|
||||||
assert b[1:2] == b'\x02'
|
|
||||||
assert b[2:4] == b'\x00\x00'
|
|
||||||
assert b[4:6] == b'\x13\x01'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a default response for Service 03
|
|
||||||
|
|
||||||
p = OBD()/OBD_S03_PR()
|
|
||||||
b = bytes(p)
|
|
||||||
assert len(p) == 2
|
|
||||||
assert b[0:1] == b'\x43'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
assert p.dtcs == []
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 07
|
|
||||||
|
|
||||||
p = OBD()/OBD_S07_PR(dtcs=[OBD_DTC(location='Chassis', code1=0, code2=5, code3=1, code4=0)])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x47'
|
|
||||||
assert b[1:2] == b'\x01'
|
|
||||||
assert b[2:4] == b'\x45\x10'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a default response for Service 07
|
|
||||||
|
|
||||||
p = OBD()/OBD_S07_PR()
|
|
||||||
b = bytes(p)
|
|
||||||
assert len(p) == 2
|
|
||||||
assert b[0:1] == b'\x47'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
assert p.dtcs == []
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 0A
|
|
||||||
|
|
||||||
p = OBD()/OBD_S0A_PR(dtcs=[OBD_DTC(), OBD_DTC(location='Body', code1=1, code2=7, code3=8, code4=2), OBD_DTC()])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x4A'
|
|
||||||
assert b[1:2] == b'\x03'
|
|
||||||
assert b[2:4] == b'\x00\x00'
|
|
||||||
assert b[4:6] == b'\x97\x82'
|
|
||||||
assert b[6:8] == b'\x00\x00'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a default response for Service 0A
|
|
||||||
|
|
||||||
p = OBD()/OBD_S0A_PR()
|
|
||||||
b = bytes(p)
|
|
||||||
assert len(p) == 2
|
|
||||||
assert b[0:1] == b'\x4A'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
assert p.dtcs == []
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 00
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID00(b'ABCD'))
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x00'
|
|
||||||
assert b[2:] == b'ABCD'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 02 with one VIN
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=b'W0L000051T2123456'))
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x02'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
assert b[3:] == b'W0L000051T2123456'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 02 with two VINs
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=[b'W0L000051T2123456', b'W0L000051T2123456']))
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x02'
|
|
||||||
assert b[2:3] == b'\x02'
|
|
||||||
assert b[3:20] == b'W0L000051T2123456'
|
|
||||||
assert b[20:] == b'W0L000051T2123456'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 04 with one CID
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=b'ABCDEFGHIJKLMNOP'))
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x04'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
assert b[3:] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 04 with two CID
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=[b'ABCDEFGHIJKLMNOP', b'ABCDEFGHIJKLMNOP']))
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x04'
|
|
||||||
assert b[2:3] == b'\x02'
|
|
||||||
assert b[3:19] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
assert b[19:] == b'ABCDEFGHIJKLMNOP'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 0A
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=OBD_S09_PR_Record()/OBD_IID0A(ecu_names=b'ABCDEFGHIJKLMNOPQRST'))
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x0A'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
assert b[3:] == b'ABCDEFGHIJKLMNOPQRST'
|
|
||||||
|
|
||||||
|
|
||||||
= Check building a response for Service 09 IID 02 and IID 04
|
|
||||||
|
|
||||||
p = OBD(service=0x49)/OBD_S02_PR(data_records=[
|
|
||||||
OBD_S09_PR_Record()/OBD_IID02(vehicle_identification_numbers=b'ABCDEFGHIJKLMNOPQ'),
|
|
||||||
OBD_S09_PR_Record()/OBD_IID04(calibration_identifications=b'ABCDEFGHIJKLMNOP')
|
|
||||||
])
|
|
||||||
b = bytes(p)
|
|
||||||
assert b[0:1] == b'\x49'
|
|
||||||
assert b[1:2] == b'\x02'
|
|
||||||
assert b[2:3] == b'\x01'
|
|
||||||
assert b[3:20] == b'ABCDEFGHIJKLMNOPQ'
|
|
||||||
assert b[20:21] == b'\x04'
|
|
||||||
assert b[21:22] == b'\x01'
|
|
||||||
assert b[22:] == b'ABCDEFGHIJKLMNOP'
|
|
|
@ -1,14 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.packet import Packet
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_Packet(Packet):
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return '', s
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive obd specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,390 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.fields import PacketListField
|
|
||||||
|
|
||||||
from scapy.contrib.automotive.obd.services import OBD_S01, OBD_S02
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids_00_1F import *
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids_20_3F import *
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids_40_5F import *
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids_60_7F import *
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids_80_9F import *
|
|
||||||
from scapy.contrib.automotive.obd.pid.pids_A0_C0 import *
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S01_PR_Record(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField("pid", 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S01_PR(Packet):
|
|
||||||
name = "Parameter IDs"
|
|
||||||
fields_desc = [
|
|
||||||
PacketListField("data_records", [], OBD_S01_PR_Record)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S01 \
|
|
||||||
and all(r.pid in other.pid for r in self.data_records)
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S02_PR_Record(Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField("pid", 0),
|
|
||||||
XByteField("frame_no", 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S02_PR(Packet):
|
|
||||||
name = "Parameter IDs"
|
|
||||||
fields_desc = [
|
|
||||||
PacketListField("data_records", [], OBD_S02_PR_Record)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S02 \
|
|
||||||
and all(r.pid in [o.pid for o in other.requests]
|
|
||||||
for r in self.data_records)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID00, pid=0x00)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID01, pid=0x01)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID02, pid=0x02)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID03, pid=0x03)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID04, pid=0x04)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID05, pid=0x05)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID06, pid=0x06)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID07, pid=0x07)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID08, pid=0x08)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID09, pid=0x09)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID0A, pid=0x0A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID0B, pid=0x0B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID0C, pid=0x0C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID0D, pid=0x0D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID0E, pid=0x0E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID0F, pid=0x0F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID10, pid=0x10)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID11, pid=0x11)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID12, pid=0x12)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID13, pid=0x13)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID14, pid=0x14)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID15, pid=0x15)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID16, pid=0x16)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID17, pid=0x17)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID18, pid=0x18)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID19, pid=0x19)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID1A, pid=0x1A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID1B, pid=0x1B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID1C, pid=0x1C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID1D, pid=0x1D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID1E, pid=0x1E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID1F, pid=0x1F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID20, pid=0x20)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID21, pid=0x21)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID22, pid=0x22)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID23, pid=0x23)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID24, pid=0x24)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID25, pid=0x25)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID26, pid=0x26)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID27, pid=0x27)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID28, pid=0x28)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID29, pid=0x29)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID2A, pid=0x2A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID2B, pid=0x2B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID2C, pid=0x2C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID2D, pid=0x2D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID2E, pid=0x2E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID2F, pid=0x2F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID30, pid=0x30)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID31, pid=0x31)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID32, pid=0x32)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID33, pid=0x33)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID34, pid=0x34)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID35, pid=0x35)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID36, pid=0x36)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID37, pid=0x37)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID38, pid=0x38)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID39, pid=0x39)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID3A, pid=0x3A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID3B, pid=0x3B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID3C, pid=0x3C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID3D, pid=0x3D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID3E, pid=0x3E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID3F, pid=0x3F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID40, pid=0x40)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID41, pid=0x41)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID42, pid=0x42)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID43, pid=0x43)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID44, pid=0x44)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID45, pid=0x45)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID46, pid=0x46)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID47, pid=0x47)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID48, pid=0x48)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID49, pid=0x49)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID4A, pid=0x4A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID4B, pid=0x4B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID4C, pid=0x4C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID4D, pid=0x4D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID4E, pid=0x4E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID4F, pid=0x4F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID50, pid=0x50)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID51, pid=0x51)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID52, pid=0x52)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID53, pid=0x53)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID54, pid=0x54)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID55, pid=0x55)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID56, pid=0x56)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID57, pid=0x57)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID58, pid=0x58)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID59, pid=0x59)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID5A, pid=0x5A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID5B, pid=0x5B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID5C, pid=0x5C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID5D, pid=0x5D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID5E, pid=0x5E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID5F, pid=0x5F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID60, pid=0x60)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID61, pid=0x61)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID62, pid=0x62)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID63, pid=0x63)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID64, pid=0x64)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID65, pid=0x65)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID66, pid=0x66)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID67, pid=0x67)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID68, pid=0x68)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID69, pid=0x69)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID6A, pid=0x6A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID6B, pid=0x6B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID6C, pid=0x6C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID6D, pid=0x6D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID6E, pid=0x6E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID6F, pid=0x6F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID70, pid=0x70)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID71, pid=0x71)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID72, pid=0x72)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID73, pid=0x73)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID74, pid=0x74)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID75, pid=0x75)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID76, pid=0x76)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID77, pid=0x77)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID78, pid=0x78)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID79, pid=0x79)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID7A, pid=0x7A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID7B, pid=0x7B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID7C, pid=0x7C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID7D, pid=0x7D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID7E, pid=0x7E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID7F, pid=0x7F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID80, pid=0x80)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID81, pid=0x81)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID82, pid=0x82)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID83, pid=0x83)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID84, pid=0x84)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID85, pid=0x85)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID86, pid=0x86)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID87, pid=0x87)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID88, pid=0x88)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID89, pid=0x89)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID8A, pid=0x8A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID8B, pid=0x8B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID8C, pid=0x8C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID8D, pid=0x8D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID8E, pid=0x8E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID8F, pid=0x8F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID90, pid=0x90)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID91, pid=0x91)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID92, pid=0x92)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID93, pid=0x93)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID94, pid=0x94)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID98, pid=0x98)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID99, pid=0x99)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID9A, pid=0x9A)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID9B, pid=0x9B)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID9C, pid=0x9C)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID9D, pid=0x9D)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID9E, pid=0x9E)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PID9F, pid=0x9F)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA0, pid=0xA0)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA1, pid=0xA1)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA2, pid=0xA2)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA3, pid=0xA3)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA4, pid=0xA4)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA5, pid=0xA5)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDA6, pid=0xA6)
|
|
||||||
bind_layers(OBD_S01_PR_Record, OBD_PIDC0, pid=0xC0)
|
|
||||||
|
|
||||||
|
|
||||||
# Service 2
|
|
||||||
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID00, pid=0x00)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID01, pid=0x01)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID02, pid=0x02)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID03, pid=0x03)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID04, pid=0x04)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID05, pid=0x05)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID06, pid=0x06)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID07, pid=0x07)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID08, pid=0x08)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID09, pid=0x09)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID0A, pid=0x0A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID0B, pid=0x0B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID0C, pid=0x0C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID0D, pid=0x0D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID0E, pid=0x0E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID0F, pid=0x0F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID10, pid=0x10)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID11, pid=0x11)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID12, pid=0x12)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID13, pid=0x13)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID14, pid=0x14)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID15, pid=0x15)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID16, pid=0x16)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID17, pid=0x17)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID18, pid=0x18)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID19, pid=0x19)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID1A, pid=0x1A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID1B, pid=0x1B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID1C, pid=0x1C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID1D, pid=0x1D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID1E, pid=0x1E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID1F, pid=0x1F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID20, pid=0x20)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID21, pid=0x21)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID22, pid=0x22)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID23, pid=0x23)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID24, pid=0x24)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID25, pid=0x25)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID26, pid=0x26)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID27, pid=0x27)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID28, pid=0x28)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID29, pid=0x29)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID2A, pid=0x2A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID2B, pid=0x2B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID2C, pid=0x2C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID2D, pid=0x2D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID2E, pid=0x2E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID2F, pid=0x2F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID30, pid=0x30)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID31, pid=0x31)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID32, pid=0x32)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID33, pid=0x33)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID34, pid=0x34)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID35, pid=0x35)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID36, pid=0x36)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID37, pid=0x37)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID38, pid=0x38)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID39, pid=0x39)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID3A, pid=0x3A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID3B, pid=0x3B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID3C, pid=0x3C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID3D, pid=0x3D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID3E, pid=0x3E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID3F, pid=0x3F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID40, pid=0x40)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID41, pid=0x41)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID42, pid=0x42)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID43, pid=0x43)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID44, pid=0x44)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID45, pid=0x45)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID46, pid=0x46)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID47, pid=0x47)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID48, pid=0x48)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID49, pid=0x49)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID4A, pid=0x4A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID4B, pid=0x4B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID4C, pid=0x4C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID4D, pid=0x4D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID4E, pid=0x4E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID4F, pid=0x4F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID50, pid=0x50)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID51, pid=0x51)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID52, pid=0x52)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID53, pid=0x53)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID54, pid=0x54)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID55, pid=0x55)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID56, pid=0x56)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID57, pid=0x57)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID58, pid=0x58)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID59, pid=0x59)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID5A, pid=0x5A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID5B, pid=0x5B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID5C, pid=0x5C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID5D, pid=0x5D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID5E, pid=0x5E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID5F, pid=0x5F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID60, pid=0x60)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID61, pid=0x61)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID62, pid=0x62)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID63, pid=0x63)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID64, pid=0x64)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID65, pid=0x65)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID66, pid=0x66)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID67, pid=0x67)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID68, pid=0x68)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID69, pid=0x69)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID6A, pid=0x6A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID6B, pid=0x6B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID6C, pid=0x6C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID6D, pid=0x6D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID6E, pid=0x6E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID6F, pid=0x6F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID70, pid=0x70)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID71, pid=0x71)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID72, pid=0x72)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID73, pid=0x73)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID74, pid=0x74)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID75, pid=0x75)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID76, pid=0x76)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID77, pid=0x77)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID78, pid=0x78)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID79, pid=0x79)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID7A, pid=0x7A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID7B, pid=0x7B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID7C, pid=0x7C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID7D, pid=0x7D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID7E, pid=0x7E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID7F, pid=0x7F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID80, pid=0x80)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID81, pid=0x81)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID82, pid=0x82)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID83, pid=0x83)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID84, pid=0x84)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID85, pid=0x85)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID86, pid=0x86)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID87, pid=0x87)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID88, pid=0x88)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID89, pid=0x89)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID8A, pid=0x8A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID8B, pid=0x8B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID8C, pid=0x8C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID8D, pid=0x8D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID8E, pid=0x8E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID8F, pid=0x8F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID90, pid=0x90)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID91, pid=0x91)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID92, pid=0x92)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID93, pid=0x93)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID94, pid=0x94)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID98, pid=0x98)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID99, pid=0x99)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID9A, pid=0x9A)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID9B, pid=0x9B)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID9C, pid=0x9C)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID9D, pid=0x9D)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID9E, pid=0x9E)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PID9F, pid=0x9F)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA0, pid=0xA0)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA1, pid=0xA1)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA2, pid=0xA2)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA3, pid=0xA3)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA4, pid=0xA4)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA5, pid=0xA5)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDA6, pid=0xA6)
|
|
||||||
bind_layers(OBD_S02_PR_Record, OBD_PIDC0, pid=0xC0)
|
|
|
@ -1,378 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import BitEnumField, BitField, ScalingField, \
|
|
||||||
FlagsField, XByteEnumField, PacketField
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
from scapy.contrib.automotive.obd.services import OBD_DTC
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
|
|
||||||
# PID = Parameter IDentification
|
|
||||||
|
|
||||||
class OBD_PID00(OBD_Packet):
|
|
||||||
name = "PID_00_PIDsSupported"
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', b'', 32, [
|
|
||||||
'PID20',
|
|
||||||
'PID1F',
|
|
||||||
'PID1E',
|
|
||||||
'PID1D',
|
|
||||||
'PID1C',
|
|
||||||
'PID1B',
|
|
||||||
'PID1A',
|
|
||||||
'PID19',
|
|
||||||
'PID18',
|
|
||||||
'PID17',
|
|
||||||
'PID16',
|
|
||||||
'PID15',
|
|
||||||
'PID14',
|
|
||||||
'PID13',
|
|
||||||
'PID12',
|
|
||||||
'PID11',
|
|
||||||
'PID10',
|
|
||||||
'PID0F',
|
|
||||||
'PID0E',
|
|
||||||
'PID0D',
|
|
||||||
'PID0C',
|
|
||||||
'PID0B',
|
|
||||||
'PID0A',
|
|
||||||
'PID09',
|
|
||||||
'PID08',
|
|
||||||
'PID07',
|
|
||||||
'PID06',
|
|
||||||
'PID05',
|
|
||||||
'PID04',
|
|
||||||
'PID03',
|
|
||||||
'PID02',
|
|
||||||
'PID01'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID01(OBD_Packet):
|
|
||||||
name = "PID_01_MonitorStatusSinceDtcsCleared"
|
|
||||||
|
|
||||||
onOff = {
|
|
||||||
0: 'off',
|
|
||||||
1: 'on'
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
BitEnumField('mil', 0, 1, onOff),
|
|
||||||
BitField('dtc_count', 0, 7),
|
|
||||||
|
|
||||||
BitField('reserved1', 0, 1),
|
|
||||||
FlagsField('continuous_tests_ready', 0, 3, [
|
|
||||||
'misfire',
|
|
||||||
'fuelSystem',
|
|
||||||
'components'
|
|
||||||
]),
|
|
||||||
|
|
||||||
BitField('reserved2', 0, 1),
|
|
||||||
FlagsField('continuous_tests_supported', 0, 3, [
|
|
||||||
'misfire',
|
|
||||||
'fuel_system',
|
|
||||||
'components'
|
|
||||||
]),
|
|
||||||
|
|
||||||
FlagsField('once_per_trip_tests_supported', 0, 8, [
|
|
||||||
'egr',
|
|
||||||
'oxygenSensorHeater',
|
|
||||||
'oxygenSensor',
|
|
||||||
'acSystemRefrigerant',
|
|
||||||
'secondaryAirSystem',
|
|
||||||
'evaporativeSystem',
|
|
||||||
'heatedCatalyst',
|
|
||||||
'catalyst'
|
|
||||||
]),
|
|
||||||
|
|
||||||
FlagsField('once_per_trip_tests_ready', 0, 8, [
|
|
||||||
'egr',
|
|
||||||
'oxygenSensorHeater',
|
|
||||||
'oxygenSensor',
|
|
||||||
'acSystemRefrigerant',
|
|
||||||
'secondaryAirSystem',
|
|
||||||
'evaporativeSystem',
|
|
||||||
'heatedCatalyst',
|
|
||||||
'catalyst'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID02(OBD_Packet):
|
|
||||||
name = "PID_02_FreezeDtc"
|
|
||||||
fields_desc = [
|
|
||||||
PacketField('dtc', b'', OBD_DTC)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID03(OBD_Packet):
|
|
||||||
name = "PID_03_FuelSystemStatus"
|
|
||||||
|
|
||||||
loopStates = {
|
|
||||||
0x00: 'OpenLoopInsufficientEngineTemperature',
|
|
||||||
0x02: 'ClosedLoop',
|
|
||||||
0x04: 'OpenLoopEngineLoadOrFuelCut',
|
|
||||||
0x08: 'OpenLoopDueSystemFailure',
|
|
||||||
0x10: 'ClosedLoopWithFault'
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('fuel_system1', 0, loopStates),
|
|
||||||
XByteEnumField('fuel_system2', 0, loopStates)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID04(OBD_Packet):
|
|
||||||
name = "PID_04_CalculatedEngineLoad"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID05(OBD_Packet):
|
|
||||||
name = "PID_05_EngineCoolantTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="deg. C", offset=-40.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID06(OBD_Packet):
|
|
||||||
name = "PID_06_ShortTermFuelTrimBank1"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 128.,
|
|
||||||
unit="%", offset=-100.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID07(OBD_Packet):
|
|
||||||
name = "PID_07_LongTermFuelTrimBank1"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 128.,
|
|
||||||
unit="%", offset=-100.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID08(OBD_Packet):
|
|
||||||
name = "PID_08_ShortTermFuelTrimBank2"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 128.,
|
|
||||||
unit="%", offset=-100.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID09(OBD_Packet):
|
|
||||||
name = "PID_09_LongTermFuelTrimBank2"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 128.,
|
|
||||||
unit="%", offset=-100.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID0A(OBD_Packet):
|
|
||||||
name = "PID_0A_FuelPressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=3, unit="kPa")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID0B(OBD_Packet):
|
|
||||||
name = "PID_0B_IntakeManifoldAbsolutePressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="kPa")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID0C(OBD_Packet):
|
|
||||||
name = "PID_0C_EngineRpm"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=1 / 4., unit="min-1", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID0D(OBD_Packet):
|
|
||||||
name = "PID_0D_VehicleSpeed"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="km/h")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID0E(OBD_Packet):
|
|
||||||
name = "PID_0E_TimingAdvance"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=1 / 2., unit="deg.", offset=-64.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID0F(OBD_Packet):
|
|
||||||
name = "PID_0F_IntakeAirTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="deg. C", offset=-40.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID10(OBD_Packet):
|
|
||||||
name = "PID_10_MafAirFlowRate"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=1 / 100., unit="g/s")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID11(OBD_Packet):
|
|
||||||
name = "PID_11_ThrottlePosition"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID12(OBD_Packet):
|
|
||||||
name = "PID_12_CommandedSecondaryAirStatus"
|
|
||||||
|
|
||||||
states = {
|
|
||||||
0x00: 'upstream',
|
|
||||||
0x02: 'downstreamCatalyticConverter',
|
|
||||||
0x04: 'outsideAtmosphereOrOff',
|
|
||||||
0x08: 'pumpCommanded'
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('data', 0, states)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID13(OBD_Packet):
|
|
||||||
name = "PID_13_OxygenSensorsPresent"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('sensors_present', b'', 8, [
|
|
||||||
'Bank1Sensor1',
|
|
||||||
'Bank1Sensor2',
|
|
||||||
'Bank1Sensor3',
|
|
||||||
'Bank1Sensor4',
|
|
||||||
'Bank2Sensor1',
|
|
||||||
'Bank2Sensor2',
|
|
||||||
'Bank2Sensor3',
|
|
||||||
'Bank2Sensor4'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_PID14_1B(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('outputVoltage', 0, scaling=0.005, unit="V"),
|
|
||||||
ScalingField('trim', 0, scaling=100 / 128.,
|
|
||||||
unit="%", offset=-100)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID14(_OBD_PID14_1B):
|
|
||||||
name = "PID_14_OxygenSensor1"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID15(_OBD_PID14_1B):
|
|
||||||
name = "PID_15_OxygenSensor2"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID16(_OBD_PID14_1B):
|
|
||||||
name = "PID_16_OxygenSensor3"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID17(_OBD_PID14_1B):
|
|
||||||
name = "PID_17_OxygenSensor4"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID18(_OBD_PID14_1B):
|
|
||||||
name = "PID_18_OxygenSensor5"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID19(_OBD_PID14_1B):
|
|
||||||
name = "PID_19_OxygenSensor6"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID1A(_OBD_PID14_1B):
|
|
||||||
name = "PID_1A_OxygenSensor7"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID1B(_OBD_PID14_1B):
|
|
||||||
name = "PID_1B_OxygenSensor8"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID1C(OBD_Packet):
|
|
||||||
name = "PID_1C_ObdStandardsThisVehicleConformsTo"
|
|
||||||
|
|
||||||
obdStandards = {
|
|
||||||
0x01: 'OBD-II as defined by the CARB',
|
|
||||||
0x02: 'OBD as defined by the EPA',
|
|
||||||
0x03: 'OBD and OBD-II',
|
|
||||||
0x04: 'OBD-I',
|
|
||||||
0x05: 'Not OBD compliant',
|
|
||||||
0x06: 'EOBD (Europe)',
|
|
||||||
0x07: 'EOBD and OBD-II',
|
|
||||||
0x08: 'EOBD and OBD',
|
|
||||||
0x09: 'EOBD, OBD and OBD II',
|
|
||||||
0x0A: 'JOBD (Japan)',
|
|
||||||
0x0B: 'JOBD and OBD II',
|
|
||||||
0x0C: 'JOBD and EOBD',
|
|
||||||
0x0D: 'JOBD, EOBD, and OBD II',
|
|
||||||
0x0E: 'Reserved',
|
|
||||||
0x0F: 'Reserved',
|
|
||||||
0x10: 'Reserved',
|
|
||||||
0x11: 'Engine Manufacturer Diagnostics (EMD)',
|
|
||||||
0x12: 'Engine Manufacturer Diagnostics Enhanced (EMD+)',
|
|
||||||
0x13: 'Heavy Duty On-Board Diagnostics (Child/Partial) (HD OBD-C)',
|
|
||||||
0x14: 'Heavy Duty On-Board Diagnostics (HD OBD)',
|
|
||||||
0x15: 'World Wide Harmonized OBD (WWH OBD)',
|
|
||||||
0x16: 'Reserved',
|
|
||||||
0x17: 'Heavy Duty Euro OBD Stage I without NOx control (HD EOBD-I)',
|
|
||||||
0x18: 'Heavy Duty Euro OBD Stage I with NOx control (HD EOBD-I N)',
|
|
||||||
0x19: 'Heavy Duty Euro OBD Stage II without NOx control (HD EOBD-II)',
|
|
||||||
0x1A: 'Heavy Duty Euro OBD Stage II with NOx control (HD EOBD-II N)',
|
|
||||||
0x1B: 'Reserved',
|
|
||||||
0x1C: 'Brazil OBD Phase 1 (OBDBr-1)',
|
|
||||||
0x1D: 'Brazil OBD Phase 2 (OBDBr-2)',
|
|
||||||
0x1E: 'Korean OBD (KOBD)',
|
|
||||||
0x1F: 'India OBD I (IOBD I)',
|
|
||||||
0x20: 'India OBD II (IOBD II)',
|
|
||||||
0x21: 'Heavy Duty Euro OBD Stage VI (HD EOBD-IV)',
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteEnumField('data', 0, obdStandards)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID1D(OBD_Packet):
|
|
||||||
name = "PID_1D_OxygenSensorsPresent"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('sensors_present', 0, 8, [
|
|
||||||
'Bank1Sensor1',
|
|
||||||
'Bank1Sensor2',
|
|
||||||
'Bank2Sensor1',
|
|
||||||
'Bank2Sensor2',
|
|
||||||
'Bank3Sensor1',
|
|
||||||
'Bank3Sensor2',
|
|
||||||
'Bank4Sensor1',
|
|
||||||
'Bank4Sensor2'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID1E(OBD_Packet):
|
|
||||||
name = "PID_1E_AuxiliaryInputStatus"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 7),
|
|
||||||
BitEnumField('pto_status', 0, 1, OBD_PID01.onOff)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID1F(OBD_Packet):
|
|
||||||
name = "PID_1F_RunTimeSinceEngineStart"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="s", fmt="H")
|
|
||||||
]
|
|
|
@ -1,242 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import FlagsField, ScalingField
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
|
|
||||||
# PID = Parameter IDentification
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID20(OBD_Packet):
|
|
||||||
name = "PID_20_PIDsSupported"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', 0, 32, [
|
|
||||||
'PID40',
|
|
||||||
'PID3F',
|
|
||||||
'PID3E',
|
|
||||||
'PID3D',
|
|
||||||
'PID3C',
|
|
||||||
'PID3B',
|
|
||||||
'PID3A',
|
|
||||||
'PID39',
|
|
||||||
'PID38',
|
|
||||||
'PID37',
|
|
||||||
'PID36',
|
|
||||||
'PID35',
|
|
||||||
'PID34',
|
|
||||||
'PID33',
|
|
||||||
'PID32',
|
|
||||||
'PID31',
|
|
||||||
'PID30',
|
|
||||||
'PID2F',
|
|
||||||
'PID2E',
|
|
||||||
'PID2D',
|
|
||||||
'PID2C',
|
|
||||||
'PID2B',
|
|
||||||
'PID2A',
|
|
||||||
'PID29',
|
|
||||||
'PID28',
|
|
||||||
'PID27',
|
|
||||||
'PID26',
|
|
||||||
'PID25',
|
|
||||||
'PID24',
|
|
||||||
'PID23',
|
|
||||||
'PID22',
|
|
||||||
'PID21'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID21(OBD_Packet):
|
|
||||||
name = "PID_21_DistanceTraveledWithMalfunctionIndicatorLampOn"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="km", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID22(OBD_Packet):
|
|
||||||
name = "PID_22_FuelRailPressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.079, unit="kPa", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID23(OBD_Packet):
|
|
||||||
name = "PID_23_FuelRailGaugePressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=10, unit="kPa", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_PID24_2B(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('equivalence_ratio', 0, scaling=0.0000305, fmt="H"),
|
|
||||||
ScalingField('voltage', 0, scaling=0.000122, unit="V", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID24(_OBD_PID24_2B):
|
|
||||||
name = "PID_24_OxygenSensor1"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID25(_OBD_PID24_2B):
|
|
||||||
name = "PID_25_OxygenSensor2"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID26(_OBD_PID24_2B):
|
|
||||||
name = "PID_26_OxygenSensor3"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID27(_OBD_PID24_2B):
|
|
||||||
name = "PID_27_OxygenSensor4"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID28(_OBD_PID24_2B):
|
|
||||||
name = "PID_28_OxygenSensor5"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID29(_OBD_PID24_2B):
|
|
||||||
name = "PID_29_OxygenSensor6"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID2A(_OBD_PID24_2B):
|
|
||||||
name = "PID_2A_OxygenSensor7"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID2B(_OBD_PID24_2B):
|
|
||||||
name = "PID_2B_OxygenSensor8"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID2C(OBD_Packet):
|
|
||||||
name = "PID_2C_CommandedEgr"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID2D(OBD_Packet):
|
|
||||||
name = "PID_2D_EgrError"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 128.,
|
|
||||||
unit="%", offset=-100.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID2E(OBD_Packet):
|
|
||||||
name = "PID_2E_CommandedEvaporativePurge"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID2F(OBD_Packet):
|
|
||||||
name = "PID_2F_FuelTankLevelInput"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID30(OBD_Packet):
|
|
||||||
name = "PID_30_WarmUpsSinceCodesCleared"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID31(OBD_Packet):
|
|
||||||
name = "PID_31_DistanceTraveledSinceCodesCleared"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="km", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID32(OBD_Packet):
|
|
||||||
name = "PID_32_EvapSystemVaporPressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.25, unit="Pa", fmt="h")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID33(OBD_Packet):
|
|
||||||
name = "PID_33_AbsoluteBarometricPressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="kPa")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_PID34_3B(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('equivalence_ratio', 0, scaling=0.0000305, fmt="H"),
|
|
||||||
ScalingField('current', 0, scaling=0.00390625, unit="mA", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID34(_OBD_PID34_3B):
|
|
||||||
name = "PID_34_OxygenSensor1"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID35(_OBD_PID34_3B):
|
|
||||||
name = "PID_35_OxygenSensor2"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID36(_OBD_PID34_3B):
|
|
||||||
name = "PID_36_OxygenSensor3"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID37(_OBD_PID34_3B):
|
|
||||||
name = "PID_37_OxygenSensor4"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID38(_OBD_PID34_3B):
|
|
||||||
name = "PID_38_OxygenSensor5"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID39(_OBD_PID34_3B):
|
|
||||||
name = "PID_39_OxygenSensor6"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID3A(_OBD_PID34_3B):
|
|
||||||
name = "PID_3A_OxygenSensor7"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID3B(_OBD_PID34_3B):
|
|
||||||
name = "PID_3B_OxygenSensor8"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID3C(OBD_Packet):
|
|
||||||
name = "PID_3C_CatalystTemperatureBank1Sensor1"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.1, unit="deg. C",
|
|
||||||
offset=-40.0, fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID3D(OBD_Packet):
|
|
||||||
name = "PID_3D_CatalystTemperatureBank2Sensor1"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.1, unit="deg. C",
|
|
||||||
offset=-40.0, fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID3E(OBD_Packet):
|
|
||||||
name = "PID_3E_CatalystTemperatureBank1Sensor2"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.1, unit="deg. C",
|
|
||||||
offset=-40.0, fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID3F(OBD_Packet):
|
|
||||||
name = "PID_3F_CatalystTemperatureBank2Sensor2"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.1, unit="deg. C",
|
|
||||||
offset=-40.0, fmt="H")
|
|
||||||
]
|
|
|
@ -1,335 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import ByteEnumField, BitField, FlagsField, XByteField, \
|
|
||||||
ScalingField, ThreeBytesField
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
|
|
||||||
# PID = Parameter IDentification
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID40(OBD_Packet):
|
|
||||||
name = "PID_40_PIDsSupported"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', 0, 32, [
|
|
||||||
'PID60',
|
|
||||||
'PID5F',
|
|
||||||
'PID5E',
|
|
||||||
'PID5D',
|
|
||||||
'PID5C',
|
|
||||||
'PID5B',
|
|
||||||
'PID5A',
|
|
||||||
'PID59',
|
|
||||||
'PID58',
|
|
||||||
'PID57',
|
|
||||||
'PID56',
|
|
||||||
'PID55',
|
|
||||||
'PID54',
|
|
||||||
'PID53',
|
|
||||||
'PID52',
|
|
||||||
'PID51',
|
|
||||||
'PID50',
|
|
||||||
'PID4F',
|
|
||||||
'PID4E',
|
|
||||||
'PID4D',
|
|
||||||
'PID4C',
|
|
||||||
'PID4B',
|
|
||||||
'PID4A',
|
|
||||||
'PID49',
|
|
||||||
'PID48',
|
|
||||||
'PID47',
|
|
||||||
'PID46',
|
|
||||||
'PID45',
|
|
||||||
'PID44',
|
|
||||||
'PID43',
|
|
||||||
'PID42',
|
|
||||||
'PID41'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID41(OBD_Packet):
|
|
||||||
name = "PID_41_MonitorStatusThisDriveCycle"
|
|
||||||
onOff = {
|
|
||||||
0: 'off',
|
|
||||||
1: 'on'
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('reserved', 0),
|
|
||||||
|
|
||||||
BitField('reserved1', 0, 1),
|
|
||||||
FlagsField('continuous_tests_ready', 0, 3, [
|
|
||||||
'misfire',
|
|
||||||
'fuelSystem',
|
|
||||||
'components'
|
|
||||||
]),
|
|
||||||
|
|
||||||
BitField('reserved2', 0, 1),
|
|
||||||
FlagsField('continuous_tests_supported', 0, 3, [
|
|
||||||
'misfire',
|
|
||||||
'fuelSystem',
|
|
||||||
'components'
|
|
||||||
]),
|
|
||||||
|
|
||||||
FlagsField('once_per_trip_tests_supported', 0, 8, [
|
|
||||||
'egr',
|
|
||||||
'oxygenSensorHeater',
|
|
||||||
'oxygenSensor',
|
|
||||||
'acSystemRefrigerant',
|
|
||||||
'secondaryAirSystem',
|
|
||||||
'evaporativeSystem',
|
|
||||||
'heatedCatalyst',
|
|
||||||
'catalyst'
|
|
||||||
]),
|
|
||||||
|
|
||||||
FlagsField('once_per_trip_tests_ready', 0, 8, [
|
|
||||||
'egr',
|
|
||||||
'oxygenSensorHeater',
|
|
||||||
'oxygenSensor',
|
|
||||||
'acSystemRefrigerant',
|
|
||||||
'secondaryAirSystem',
|
|
||||||
'evaporativeSystem',
|
|
||||||
'heatedCatalyst',
|
|
||||||
'catalyst'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID42(OBD_Packet):
|
|
||||||
name = "PID_42_ControlModuleVoltage"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.001, unit="V", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID43(OBD_Packet):
|
|
||||||
name = "PID_43_AbsoluteLoadValue"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID44(OBD_Packet):
|
|
||||||
name = "PID_44_FuelAirCommandedEquivalenceRatio"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.0000305, fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_PercentPacket(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=100 / 255., unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID45(_OBD_PercentPacket):
|
|
||||||
name = "PID_45_RelativeThrottlePosition"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID46(OBD_Packet):
|
|
||||||
name = "PID_46_AmbientAirTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="deg. C", offset=-40.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID47(_OBD_PercentPacket):
|
|
||||||
name = "PID_47_AbsoluteThrottlePositionB"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID48(_OBD_PercentPacket):
|
|
||||||
name = "PID_48_AbsoluteThrottlePositionC"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID49(_OBD_PercentPacket):
|
|
||||||
name = "PID_49_AcceleratorPedalPositionD"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID4A(_OBD_PercentPacket):
|
|
||||||
name = "PID_4A_AcceleratorPedalPositionE"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID4B(_OBD_PercentPacket):
|
|
||||||
name = "PID_4B_AcceleratorPedalPositionF"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID4C(_OBD_PercentPacket):
|
|
||||||
name = "PID_4C_CommandedThrottleActuator"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID4D(OBD_Packet):
|
|
||||||
name = "PID_4D_TimeRunWithMilOn"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="min", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID4E(OBD_Packet):
|
|
||||||
name = "PID_4E_TimeSinceTroubleCodesCleared"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="min", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID4F(OBD_Packet):
|
|
||||||
name = "PID_4F_VariousMaxValues"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('equivalence_ratio', 0),
|
|
||||||
ScalingField('sensor_voltage', 0, unit="V"),
|
|
||||||
ScalingField('sensor_current', 0, unit="mA"),
|
|
||||||
ScalingField('intake_manifold_absolute_pressure', 0,
|
|
||||||
scaling=10, unit="kPa")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID50(OBD_Packet):
|
|
||||||
name = "PID_50_MaximumValueForAirFlowRateFromMassAirFlowSensor"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=10, unit="g/s"),
|
|
||||||
ThreeBytesField('reserved', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID51(OBD_Packet):
|
|
||||||
name = "PID_51_FuelType"
|
|
||||||
|
|
||||||
fuelTypes = {
|
|
||||||
0: 'Not available',
|
|
||||||
1: 'Gasoline',
|
|
||||||
2: 'Methanol',
|
|
||||||
3: 'Ethanol',
|
|
||||||
4: 'Diesel',
|
|
||||||
5: 'LPG',
|
|
||||||
6: 'CNG',
|
|
||||||
7: 'Propane',
|
|
||||||
8: 'Electric',
|
|
||||||
9: 'Bifuel running Gasoline',
|
|
||||||
10: 'Bifuel running Methanol',
|
|
||||||
11: 'Bifuel running Ethanol',
|
|
||||||
12: 'Bifuel running LPG',
|
|
||||||
13: 'Bifuel running CNG',
|
|
||||||
14: 'Bifuel running Propane',
|
|
||||||
15: 'Bifuel running Electricity',
|
|
||||||
16: 'Bifuel running electric and combustion engine',
|
|
||||||
17: 'Hybrid gasoline',
|
|
||||||
18: 'Hybrid Ethanol',
|
|
||||||
19: 'Hybrid Diesel',
|
|
||||||
20: 'Hybrid Electric',
|
|
||||||
21: 'Hybrid running electric and combustion engine',
|
|
||||||
22: 'Hybrid Regenerative',
|
|
||||||
23: 'Bifuel running diesel'}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('data', 0, fuelTypes)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID52(_OBD_PercentPacket):
|
|
||||||
name = "PID_52_EthanolFuel"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID53(OBD_Packet):
|
|
||||||
name = "PID_53_AbsoluteEvapSystemVaporPressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=1 / 200., unit="kPa", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID54(OBD_Packet):
|
|
||||||
name = "PID_54_EvapSystemVaporPressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="Pa", fmt="h")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_SensorTrimPacket1(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('bank1', 0, scaling=100 / 128.,
|
|
||||||
offset=-100, unit="%"),
|
|
||||||
ScalingField('bank3', 0, scaling=100 / 128.,
|
|
||||||
offset=-100, unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_SensorTrimPacket2(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('bank2', 0, scaling=100 / 128.,
|
|
||||||
offset=-100, unit="%"),
|
|
||||||
ScalingField('bank4', 0, scaling=100 / 128.,
|
|
||||||
offset=-100, unit="%")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID55(_OBD_SensorTrimPacket1):
|
|
||||||
name = "PID_55_ShortTermSecondaryOxygenSensorTrim"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID56(_OBD_SensorTrimPacket1):
|
|
||||||
name = "PID_56_LongTermSecondaryOxygenSensorTrim"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID57(_OBD_SensorTrimPacket2):
|
|
||||||
name = "PID_57_ShortTermSecondaryOxygenSensorTrim"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID58(_OBD_SensorTrimPacket2):
|
|
||||||
name = "PID_58_LongTermSecondaryOxygenSensorTrim"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID59(OBD_Packet):
|
|
||||||
name = "PID_59_FuelRailAbsolutePressure"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=10, unit="kPa", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID5A(_OBD_PercentPacket):
|
|
||||||
name = "PID_5A_RelativeAcceleratorPedalPosition"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID5B(_OBD_PercentPacket):
|
|
||||||
name = "PID_5B_HybridBatteryPackRemainingLife"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID5C(OBD_Packet):
|
|
||||||
name = "PID_5C_EngineOilTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="deg. C", offset=-40.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID5D(OBD_Packet):
|
|
||||||
name = "PID_5D_FuelInjectionTiming"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=1 / 128., offset=-210,
|
|
||||||
unit="deg.", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID5E(OBD_Packet):
|
|
||||||
name = "PID_5E_EngineFuelRate"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, scaling=0.05, unit="L/h", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID5F(OBD_Packet):
|
|
||||||
name = "PID_5F_EmissionRequirementsToWhichVehicleIsDesigned"
|
|
||||||
|
|
||||||
emissionRequirementTypes = {
|
|
||||||
0xE: 'Heavy Duty Vehicles (EURO IV) B1',
|
|
||||||
0xF: 'Heavy Duty Vehicles (EURO V) B2',
|
|
||||||
0x10: 'Heavy Duty Vehicles (EURO EEV) C',
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
ByteEnumField('data', 0, emissionRequirementTypes)
|
|
||||||
]
|
|
|
@ -1,501 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import BitField, FlagsField, ScalingField
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
|
|
||||||
# PID = Parameter IDentification
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID60(OBD_Packet):
|
|
||||||
name = "PID_60_PIDsSupported"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', 0, 32, [
|
|
||||||
'PID80',
|
|
||||||
'PID7F',
|
|
||||||
'PID7E',
|
|
||||||
'PID7D',
|
|
||||||
'PID7C',
|
|
||||||
'PID7B',
|
|
||||||
'PID7A',
|
|
||||||
'PID79',
|
|
||||||
'PID78',
|
|
||||||
'PID77',
|
|
||||||
'PID76',
|
|
||||||
'PID75',
|
|
||||||
'PID74',
|
|
||||||
'PID73',
|
|
||||||
'PID72',
|
|
||||||
'PID71',
|
|
||||||
'PID70',
|
|
||||||
'PID6F',
|
|
||||||
'PID6E',
|
|
||||||
'PID6D',
|
|
||||||
'PID6C',
|
|
||||||
'PID6B',
|
|
||||||
'PID6A',
|
|
||||||
'PID69',
|
|
||||||
'PID68',
|
|
||||||
'PID67',
|
|
||||||
'PID66',
|
|
||||||
'PID65',
|
|
||||||
'PID64',
|
|
||||||
'PID63',
|
|
||||||
'PID62',
|
|
||||||
'PID61'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID61(OBD_Packet):
|
|
||||||
name = "PID_61_DriverSDemandEnginePercentTorque"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="%", offset=-125.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID62(OBD_Packet):
|
|
||||||
name = "PID_62_ActualEnginePercentTorque"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="%", offset=-125.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID63(OBD_Packet):
|
|
||||||
name = "PID_63_EngineReferenceTorque"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data', 0, unit="Nm", fmt="H")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID64(OBD_Packet):
|
|
||||||
name = "PID_64_EnginePercentTorqueData"
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('at_point1', 0, unit="%", offset=-125.0),
|
|
||||||
ScalingField('at_point2', 0, unit="%", offset=-125.0),
|
|
||||||
ScalingField('at_point3', 0, unit="%", offset=-125.0),
|
|
||||||
ScalingField('at_point4', 0, unit="%", offset=-125.0),
|
|
||||||
ScalingField('at_point5', 0, unit="%", offset=-125.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID65(OBD_Packet):
|
|
||||||
name = "PID_65_AuxiliaryInputOutputSupported"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved1', 0, 4),
|
|
||||||
BitField('glow_plug_lamp_status_supported', 0, 1),
|
|
||||||
BitField('manual_trans_neutral_drive_status_supported', 0, 1),
|
|
||||||
BitField('auto_trans_neutral_drive_status_supported', 0, 1),
|
|
||||||
BitField('power_take_off_status_supported', 0, 1),
|
|
||||||
|
|
||||||
BitField('reserved2', 0, 4),
|
|
||||||
BitField('glow_plug_lamp_status', 0, 1),
|
|
||||||
BitField('manual_trans_neutral_drive_status', 0, 1),
|
|
||||||
BitField('auto_trans_neutral_drive_status', 0, 1),
|
|
||||||
BitField('power_take_off_status', 0, 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID66(OBD_Packet):
|
|
||||||
name = "PID_66_MassAirFlowSensor"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('sensor_b_supported', 0, 1),
|
|
||||||
BitField('sensor_a_supported', 0, 1),
|
|
||||||
ScalingField('sensor_a', 0, scaling=0.03125, unit="g/s", fmt="H"),
|
|
||||||
ScalingField('sensor_b', 0, scaling=0.03125, unit="g/s", fmt="H"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID67(OBD_Packet):
|
|
||||||
name = "PID_67_EngineCoolantTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('sensor2_supported', 0, 1),
|
|
||||||
BitField('sensor1_supported', 0, 1),
|
|
||||||
ScalingField('sensor1', 0, unit="deg. C", offset=-40.0),
|
|
||||||
ScalingField('sensor2', 0, unit="deg. C", offset=-40.0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID68(OBD_Packet):
|
|
||||||
name = "PID_68_IntakeAirTemperatureSensor"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 2),
|
|
||||||
BitField('bank2_sensor3_supported', 0, 1),
|
|
||||||
BitField('bank2_sensor2_supported', 0, 1),
|
|
||||||
BitField('bank2_sensor1_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor3_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor2_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor1_supported', 0, 1),
|
|
||||||
ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank1_sensor3', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor3', 0, unit="deg. C", offset=-40)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID69(OBD_Packet):
|
|
||||||
name = "PID_69_CommandedEgrAndEgrError"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 2),
|
|
||||||
BitField('egr_b_error_supported', 0, 1),
|
|
||||||
BitField('actual_egr_b_duty_cycle_supported', 0, 1),
|
|
||||||
BitField('commanded_egr_b_duty_cycle_supported', 0, 1),
|
|
||||||
BitField('egr_a_error_supported', 0, 1),
|
|
||||||
BitField('actual_egr_a_duty_cycle_supported', 0, 1),
|
|
||||||
BitField('commanded_egr_a_duty_cycle_supported', 0, 1),
|
|
||||||
ScalingField('commanded_egr_a_duty_cycle', 0, scaling=100 / 255.,
|
|
||||||
unit="%"),
|
|
||||||
ScalingField('actual_egr_a_duty_cycle', 0, scaling=100 / 255.,
|
|
||||||
unit="%"),
|
|
||||||
ScalingField('egr_a_error', 0, scaling=100 / 128., unit="%",
|
|
||||||
offset=-100),
|
|
||||||
ScalingField('commanded_egr_b_duty_cycle', 0, scaling=100 / 255.,
|
|
||||||
unit="%"),
|
|
||||||
ScalingField('actual_egr_b_duty_cycle', 0, scaling=100 / 255.,
|
|
||||||
unit="%"),
|
|
||||||
ScalingField('egr_b_error', 0, scaling=100 / 128., unit="%",
|
|
||||||
offset=-100),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID6A(OBD_Packet):
|
|
||||||
name = "PID_6A_CommandedDieselIntakeAirFlowControl" \
|
|
||||||
"AndRelativeIntakeAirFlowPosition"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('relative_intake_air_flow_b_position_supported', 0, 1),
|
|
||||||
BitField('commanded_intake_air_flow_b_control_supported', 0, 1),
|
|
||||||
BitField('relative_intake_air_flow_a_position_supported', 0, 1),
|
|
||||||
BitField('commanded_intake_air_flow_a_control_supported', 0, 1),
|
|
||||||
ScalingField('commanded_intake_air_flow_a_control', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('relative_intake_air_flow_a_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('commanded_intake_air_flow_b_control', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('relative_intake_air_flow_b_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID6B(OBD_Packet):
|
|
||||||
name = "PID_6B_ExhaustGasRecirculationTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('bank2_sensor2_supported', 0, 1),
|
|
||||||
BitField('bank2_sensor1_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor2_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor1_supported', 0, 1),
|
|
||||||
ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID6C(OBD_Packet):
|
|
||||||
name = "PID_6C_CommandedThrottleActuatorControlAndRelativeThrottlePosition"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('relative_throttle_b_position_supported', 0, 1),
|
|
||||||
BitField('commanded_throttle_actuator_b_control_supported', 0, 1),
|
|
||||||
BitField('relative_throttle_a_position_supported', 0, 1),
|
|
||||||
BitField('commanded_throttle_actuator_a_control_supported', 0, 1),
|
|
||||||
ScalingField('commanded_throttle_actuator_a_control', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('relative_throttle_a_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('commanded_throttle_actuator_b_control', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('relative_throttle_b_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID6D(OBD_Packet):
|
|
||||||
name = "PID_6D_FuelPressureControlSystem"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 5),
|
|
||||||
BitField('fuel_temperature_supported', 0, 1),
|
|
||||||
BitField('fuel_rail_pressure_supported', 0, 1),
|
|
||||||
BitField('commanded_fuel_rail_pressure_supported', 0, 1),
|
|
||||||
ScalingField('commanded_fuel_rail_pressure', 0, scaling=10, unit="kPa",
|
|
||||||
fmt='H'),
|
|
||||||
ScalingField('fuel_rail_pressure', 0, scaling=10, unit="kPa",
|
|
||||||
fmt='H'),
|
|
||||||
ScalingField('fuel_rail_temperature', 0, unit="deg. C", offset=-40)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID6E(OBD_Packet):
|
|
||||||
name = "PID_6E_InjectionPressureControlSystem"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('injection_control_pressure_supported', 0, 1),
|
|
||||||
BitField('commanded_injection_control_pressure_supported', 0, 1),
|
|
||||||
ScalingField('commanded_injection_control_pressure', 0, scaling=10,
|
|
||||||
unit="kPa", fmt='H'),
|
|
||||||
ScalingField('injection_control_pressure', 0, scaling=10,
|
|
||||||
unit="kPa", fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID6F(OBD_Packet):
|
|
||||||
name = "PID_6F_TurbochargerCompressorInletPressure"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('sensor_b_supported', 0, 1),
|
|
||||||
BitField('sensor_a_supported', 0, 1),
|
|
||||||
ScalingField('sensor_a', 0, unit="kPa"),
|
|
||||||
ScalingField('sensor_b', 0, unit="kPa"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID70(OBD_Packet):
|
|
||||||
name = "PID_70_BoostPressureControl"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('boost_pressure_sensor_b_supported', 0, 1),
|
|
||||||
BitField('commanded_boost_pressure_b_supported', 0, 1),
|
|
||||||
BitField('boost_pressure_sensor_a_supported', 0, 1),
|
|
||||||
BitField('commanded_boost_pressure_a_supported', 0, 1),
|
|
||||||
ScalingField('commanded_boost_pressure_a', 0, scaling=0.03125,
|
|
||||||
unit="kPa", fmt='H'),
|
|
||||||
ScalingField('boost_pressure_sensor_a', 0, scaling=0.03125,
|
|
||||||
unit="kPa", fmt='H'),
|
|
||||||
ScalingField('commanded_boost_pressure_b', 0, scaling=0.03125,
|
|
||||||
unit="kPa", fmt='H'),
|
|
||||||
ScalingField('boost_pressure_sensor_b', 0, scaling=0.03125,
|
|
||||||
unit="kPa", fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID71(OBD_Packet):
|
|
||||||
name = "PID_71_VariableGeometryTurboControl"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('vgt_b_position_supported', 0, 1),
|
|
||||||
BitField('commanded_vgt_b_position_supported', 0, 1),
|
|
||||||
BitField('vgt_a_position_supported', 0, 1),
|
|
||||||
BitField('commanded_vgt_a_position_supported', 0, 1),
|
|
||||||
ScalingField('commanded_variable_geometry_turbo_a_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('variable_geometry_turbo_a_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('commanded_variable_geometry_turbo_b_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('variable_geometry_turbo_b_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID72(OBD_Packet):
|
|
||||||
name = "PID_72_WastegateControl"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('wastegate_b_position_supported', 0, 1),
|
|
||||||
BitField('commanded_wastegate_b_position_supported', 0, 1),
|
|
||||||
BitField('wastegate_a_position_supported', 0, 1),
|
|
||||||
BitField('commanded_wastegate_a_position_supported', 0, 1),
|
|
||||||
ScalingField('commanded_wastegate_a_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('wastegate_a_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('commanded_wastegate_b_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
ScalingField('wastegate_b_position', 0,
|
|
||||||
scaling=100 / 255., unit="%"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID73(OBD_Packet):
|
|
||||||
name = "PID_73_ExhaustPressure"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('sensor_bank2_supported', 0, 1),
|
|
||||||
BitField('sensor_bank1_supported', 0, 1),
|
|
||||||
ScalingField('sensor_bank1', 0, scaling=0.01, unit="kPa", fmt='H'),
|
|
||||||
ScalingField('sensor_bank2', 0, scaling=0.01, unit="kPa", fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID74(OBD_Packet):
|
|
||||||
name = "PID_74_TurbochargerRpm"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('b_supported', 0, 1),
|
|
||||||
BitField('a_supported', 0, 1),
|
|
||||||
ScalingField('a_rpm', 0, unit="min-1", fmt='H'),
|
|
||||||
ScalingField('b_rpm', 0, unit="min-1", fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID75(OBD_Packet):
|
|
||||||
name = "PID_75_TurbochargerATemperature"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('turbo_a_turbine_outlet_temperature_supported', 0, 1),
|
|
||||||
BitField('turbo_a_turbine_inlet_temperature_supported', 0, 1),
|
|
||||||
BitField('turbo_a_compressor_outlet_temperature_supported', 0, 1),
|
|
||||||
BitField('turbo_a_compressor_inlet_temperature_supported', 0, 1),
|
|
||||||
ScalingField('turbocharger_a_compressor_inlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40),
|
|
||||||
ScalingField('turbocharger_a_compressor_outlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40),
|
|
||||||
ScalingField('turbocharger_a_turbine_inlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40, fmt='H',
|
|
||||||
scaling=0.1),
|
|
||||||
ScalingField('turbocharger_a_turbine_outlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40, fmt='H',
|
|
||||||
scaling=0.1),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID76(OBD_Packet):
|
|
||||||
name = "PID_76_TurbochargerBTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('turbo_a_turbine_outlet_temperature_supported', 0, 1),
|
|
||||||
BitField('turbo_a_turbine_inlet_temperature_supported', 0, 1),
|
|
||||||
BitField('turbo_a_compressor_outlet_temperature_supported', 0, 1),
|
|
||||||
BitField('turbo_a_compressor_inlet_temperature_supported', 0, 1),
|
|
||||||
ScalingField('turbocharger_a_compressor_inlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40),
|
|
||||||
ScalingField('turbocharger_a_compressor_outlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40),
|
|
||||||
ScalingField('turbocharger_a_turbine_inlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40, fmt='H',
|
|
||||||
scaling=0.1),
|
|
||||||
ScalingField('turbocharger_a_turbine_outlet_temperature', 0,
|
|
||||||
unit="deg. C", offset=-40, fmt='H',
|
|
||||||
scaling=0.1),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID77(OBD_Packet):
|
|
||||||
name = "PID_77_ChargeAirCoolerTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('bank2_sensor2_supported', 0, 1),
|
|
||||||
BitField('bank2_sensor1_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor2_supported', 0, 1),
|
|
||||||
BitField('bank1_sensor1_supported', 0, 1),
|
|
||||||
ScalingField('bank1_sensor1', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank1_sensor2', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor1', 0, unit="deg. C", offset=-40),
|
|
||||||
ScalingField('bank2_sensor2', 0, unit="deg. C", offset=-40),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_PID_ExhaustGasTemperatureBank(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('sensor4_supported', 0, 1),
|
|
||||||
BitField('sensor3_supported', 0, 1),
|
|
||||||
BitField('sensor2_supported', 0, 1),
|
|
||||||
BitField('sensor1_supported', 0, 1),
|
|
||||||
ScalingField('sensor1', 0, unit="deg. C", offset=-40,
|
|
||||||
scaling=0.1, fmt='H'),
|
|
||||||
ScalingField('sensor2', 0, unit="deg. C", offset=-40,
|
|
||||||
scaling=0.1, fmt='H'),
|
|
||||||
ScalingField('sensor3', 0, unit="deg. C", offset=-40,
|
|
||||||
scaling=0.1, fmt='H'),
|
|
||||||
ScalingField('sensor4', 0, unit="deg. C", offset=-40,
|
|
||||||
scaling=0.1, fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID78(_OBD_PID_ExhaustGasTemperatureBank):
|
|
||||||
name = "PID_78_ExhaustGasTemperatureBank1"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID79(_OBD_PID_ExhaustGasTemperatureBank):
|
|
||||||
name = "PID_79_ExhaustGasTemperatureBank2"
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_PID_DieselParticulateFilter(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 5),
|
|
||||||
BitField('outlet_pressure_supported', 0, 1),
|
|
||||||
BitField('inlet_pressure_supported', 0, 1),
|
|
||||||
BitField('delta_pressure_supported', 0, 1),
|
|
||||||
ScalingField('delta_pressure', 0,
|
|
||||||
unit='kPa', offset=-327.68, scaling=0.01, fmt='H'),
|
|
||||||
ScalingField('particulate_filter', 0,
|
|
||||||
unit='kPa', scaling=0.01, fmt='H'),
|
|
||||||
ScalingField('outlet_pressure', 0,
|
|
||||||
unit='kPa', scaling=0.01, fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID7A(_OBD_PID_DieselParticulateFilter):
|
|
||||||
name = "PID_7A_DieselParticulateFilter1"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID7B(_OBD_PID_DieselParticulateFilter):
|
|
||||||
name = "PID_7B_DieselParticulateFilter2"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID7C(OBD_Packet):
|
|
||||||
name = "PID_7C_DieselParticulateFilterTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('bank2_outlet_temperature_supported', 0, 1),
|
|
||||||
BitField('bank2_inlet_temperature_supported', 0, 1),
|
|
||||||
BitField('bank1_outlet_temperature_supported', 0, 1),
|
|
||||||
BitField('bank1_inlet_temperature_supported', 0, 1),
|
|
||||||
ScalingField('bank1_inlet_temperature_sensor', 0,
|
|
||||||
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
|
|
||||||
ScalingField('bank1_outlet_temperature_sensor', 0,
|
|
||||||
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
|
|
||||||
ScalingField('bank2_inlet_temperature_sensor', 0,
|
|
||||||
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
|
|
||||||
ScalingField('bank2_outlet_temperature_sensor', 0,
|
|
||||||
unit="deg. C", offset=-40, scaling=0.1, fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID7D(OBD_Packet):
|
|
||||||
name = "PID_7D_NoxNteControlAreaStatus"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('nte_deficiency_for_nox_active_area', 0, 1),
|
|
||||||
BitField('inside_manufacturer_specific_nox_nte_carve_out_area', 0, 1),
|
|
||||||
BitField('outside', 0, 1),
|
|
||||||
BitField('inside', 0, 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID7E(OBD_Packet):
|
|
||||||
name = "PID_7E_PmNteControlAreaStatus"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 4),
|
|
||||||
BitField('nte_deficiency_for_pm_active_area', 0, 1),
|
|
||||||
BitField('inside_manufacturer_specific_pm_nte_carve_out_area', 0, 1),
|
|
||||||
BitField('outside', 0, 1),
|
|
||||||
BitField('inside', 0, 1),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID7F(OBD_Packet):
|
|
||||||
name = "PID_7F_EngineRunTime"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 5),
|
|
||||||
BitField('total_with_pto_active_supported', 0, 1),
|
|
||||||
BitField('total_idle_supported', 0, 1),
|
|
||||||
BitField('total_supported', 0, 1),
|
|
||||||
ScalingField('total', 0, unit='sec', fmt='Q'),
|
|
||||||
ScalingField('total_idle', 0, unit='sec', fmt='Q'),
|
|
||||||
ScalingField('total_with_pto_active', 0, unit='sec', fmt='Q'),
|
|
||||||
]
|
|
|
@ -1,287 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import StrFixedLenField, FlagsField, ScalingField, BitField
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
|
|
||||||
# PID = Parameter IDentification
|
|
||||||
|
|
||||||
class OBD_PID80(OBD_Packet):
|
|
||||||
name = "PID_80_PIDsSupported"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', 0, 32, [
|
|
||||||
'PIDA0',
|
|
||||||
'PID9F',
|
|
||||||
'PID9E',
|
|
||||||
'PID9D',
|
|
||||||
'PID9C',
|
|
||||||
'PID9B',
|
|
||||||
'PID9A',
|
|
||||||
'PID99',
|
|
||||||
'PID98',
|
|
||||||
'PID97',
|
|
||||||
'PID96',
|
|
||||||
'PID95',
|
|
||||||
'PID94',
|
|
||||||
'PID93',
|
|
||||||
'PID92',
|
|
||||||
'PID91',
|
|
||||||
'PID90',
|
|
||||||
'PID8F',
|
|
||||||
'PID8E',
|
|
||||||
'PID8D',
|
|
||||||
'PID8C',
|
|
||||||
'PID8B',
|
|
||||||
'PID8A',
|
|
||||||
'PID89',
|
|
||||||
'PID88',
|
|
||||||
'PID87',
|
|
||||||
'PID86',
|
|
||||||
'PID85',
|
|
||||||
'PID84',
|
|
||||||
'PID83',
|
|
||||||
'PID82',
|
|
||||||
'PID81'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID81(OBD_Packet):
|
|
||||||
name = "PID_81_EngineRunTimeForAuxiliaryEmissionsControlDevice"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 3),
|
|
||||||
BitField('total_run_time_with_ei_aecd5_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd4_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd3_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd2_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd1_supported', 0, 1),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd1', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd2', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd3', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd4', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd5', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID82(OBD_Packet):
|
|
||||||
name = "PID_82_EngineRunTimeForAuxiliaryEmissionsControlDevice"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 3),
|
|
||||||
BitField('total_run_time_with_ei_aecd10_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd9_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd8_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd7_supported', 0, 1),
|
|
||||||
BitField('total_run_time_with_ei_aecd6_supported', 0, 1),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd6', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd7', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd8', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd9', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
ScalingField('total_run_time_with_ei_aecd10', 0, unit='sec',
|
|
||||||
fmt='Q'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID83(OBD_Packet):
|
|
||||||
name = "PID_83_NOxSensor"
|
|
||||||
fields_desc = [
|
|
||||||
BitField('reserved', 0, 6),
|
|
||||||
BitField('nox_sensor_concentration_bank2_sensor1_supported', 0, 1),
|
|
||||||
BitField('nox_sensor_concentration_bank1_sensor1_supported', 0, 1),
|
|
||||||
ScalingField('nox_sensor_concentration_bank1_sensor1', 0, unit='ppm',
|
|
||||||
fmt='H'),
|
|
||||||
ScalingField('nox_sensor_concentration_bank2_sensor1', 0, unit='ppm',
|
|
||||||
fmt='H'),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID84(OBD_Packet):
|
|
||||||
name = "PID_84_ManifoldSurfaceTemperature"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID85(OBD_Packet):
|
|
||||||
name = "PID_85_NoxReagentSystem"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 10)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID86(OBD_Packet):
|
|
||||||
name = "PID_86_ParticulateMatterSensor"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID87(OBD_Packet):
|
|
||||||
name = "PID_87_IntakeManifoldAbsolutePressure"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID88(OBD_Packet):
|
|
||||||
name = "PID_88_ScrInduceSystem"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 13)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID89(OBD_Packet):
|
|
||||||
# 11 - 15
|
|
||||||
name = "PID_89_RunTimeForAecd"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 41)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID8A(OBD_Packet):
|
|
||||||
# 16 - 20
|
|
||||||
name = "PID_8A_RunTimeForAecd"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 41)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID8B(OBD_Packet):
|
|
||||||
name = "PID_8B_DieselAftertreatment"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 7)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID8C(OBD_Packet):
|
|
||||||
name = "PID_8C_O2Sensor"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 16)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID8D(OBD_Packet):
|
|
||||||
name = "PID_8D_ThrottlePositionG"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID8E(OBD_Packet):
|
|
||||||
name = "PID_8E_EngineFrictionPercentTorque"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID8F(OBD_Packet):
|
|
||||||
name = "PID_8F_PmSensorBank1And2"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID90(OBD_Packet):
|
|
||||||
name = "PID_90_WwhObdVehicleObdSystemInformation"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID91(OBD_Packet):
|
|
||||||
name = "PID_91_WwhObdVehicleObdSystemInformation"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 5)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID92(OBD_Packet):
|
|
||||||
name = "PID_92_FuelSystemControl"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID93(OBD_Packet):
|
|
||||||
name = "PID_93_WwhObdVehicleObdCountersSupport"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 3)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID94(OBD_Packet):
|
|
||||||
name = "PID_94_NoxWarningAndInducementSystem"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 12)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID98(OBD_Packet):
|
|
||||||
name = "PID_98_ExhaustGasTemperatureSensor"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 9)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID99(OBD_Packet):
|
|
||||||
name = "PID_99_ExhaustGasTemperatureSensor"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 9)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID9A(OBD_Packet):
|
|
||||||
name = "PID_9A_HybridEvVehicleSystemDataBatteryVoltage"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 6)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID9B(OBD_Packet):
|
|
||||||
name = "PID_9B_DieselExhaustFluidSensorData"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID9C(OBD_Packet):
|
|
||||||
name = "PID_9C_O2SensorData"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 17)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID9D(OBD_Packet):
|
|
||||||
name = "PID_9D_EngineFuelRate"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID9E(OBD_Packet):
|
|
||||||
name = "PID_9E_EngineExhaustFlowRate"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PID9F(OBD_Packet):
|
|
||||||
name = "PID_9F_FuelSystemPercentageUse"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 9)
|
|
||||||
]
|
|
|
@ -1,135 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import StrFixedLenField, FlagsField
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
|
|
||||||
|
|
||||||
# See https://en.wikipedia.org/wiki/OBD-II_PIDs for further information
|
|
||||||
# PID = Parameter IDentification
|
|
||||||
|
|
||||||
class OBD_PIDA0(OBD_Packet):
|
|
||||||
name = "PID_A0_PIDsSupported"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', 0, 32, [
|
|
||||||
'PIDC0',
|
|
||||||
'PIDBF',
|
|
||||||
'PIDBE',
|
|
||||||
'PIDBD',
|
|
||||||
'PIDBC',
|
|
||||||
'PIDBB',
|
|
||||||
'PIDBA',
|
|
||||||
'PIDB9',
|
|
||||||
'PIDB8',
|
|
||||||
'PIDB7',
|
|
||||||
'PIDB6',
|
|
||||||
'PIDB5',
|
|
||||||
'PIDB4',
|
|
||||||
'PIDB3',
|
|
||||||
'PIDB2',
|
|
||||||
'PIDB1',
|
|
||||||
'PIDB0',
|
|
||||||
'PIDAF',
|
|
||||||
'PIDAE',
|
|
||||||
'PIDAD',
|
|
||||||
'PIDAC',
|
|
||||||
'PIDAB',
|
|
||||||
'PIDAA',
|
|
||||||
'PIDA9',
|
|
||||||
'PIDA8',
|
|
||||||
'PIDA7',
|
|
||||||
'PIDA6',
|
|
||||||
'PIDA5',
|
|
||||||
'PIDA4',
|
|
||||||
'PIDA3',
|
|
||||||
'PIDA2',
|
|
||||||
'PIDA1'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDA1(OBD_Packet):
|
|
||||||
name = "PID_A1_NoxSensorCorrectedData"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 9)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDA2(OBD_Packet):
|
|
||||||
name = "PID_A2_CylinderFuelRate"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 2)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDA3(OBD_Packet):
|
|
||||||
name = "PID_A3_EvapSystemVaporPressure"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 9)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDA4(OBD_Packet):
|
|
||||||
name = "PID_A4_TransmissionActualGear"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDA5(OBD_Packet):
|
|
||||||
name = "PID_A5_DieselExhaustFluidDosing"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDA6(OBD_Packet):
|
|
||||||
name = "PID_A6_Odometer"
|
|
||||||
fields_desc = [
|
|
||||||
StrFixedLenField('data', b'', 4)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_PIDC0(OBD_Packet):
|
|
||||||
name = "PID_C0_PIDsSupported"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_pids', 0, 32, [
|
|
||||||
'PIDE0',
|
|
||||||
'PIDDF',
|
|
||||||
'PIDDE',
|
|
||||||
'PIDDD',
|
|
||||||
'PIDDC',
|
|
||||||
'PIDDB',
|
|
||||||
'PIDDA',
|
|
||||||
'PIDD9',
|
|
||||||
'PIDD8',
|
|
||||||
'PIDD7',
|
|
||||||
'PIDD6',
|
|
||||||
'PIDD5',
|
|
||||||
'PIDD4',
|
|
||||||
'PIDD3',
|
|
||||||
'PIDD2',
|
|
||||||
'PIDD1',
|
|
||||||
'PIDD0',
|
|
||||||
'PIDCF',
|
|
||||||
'PIDCE',
|
|
||||||
'PIDCD',
|
|
||||||
'PIDCC',
|
|
||||||
'PIDCB',
|
|
||||||
'PIDCA',
|
|
||||||
'PIDC9',
|
|
||||||
'PIDC8',
|
|
||||||
'PIDC7',
|
|
||||||
'PIDC6',
|
|
||||||
'PIDC5',
|
|
||||||
'PIDC4',
|
|
||||||
'PIDC3',
|
|
||||||
'PIDC2',
|
|
||||||
'PIDC1'
|
|
||||||
])
|
|
||||||
]
|
|
|
@ -1,231 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.korb@e-mundo.de>
|
|
||||||
# Copyright (C) Friedrich Feigel <friedrich.feigel@e-mundo.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = OnBoardDiagnosticScanner
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
# XXX TODO This file contains illegal E501 issues D:
|
|
||||||
|
|
||||||
from scapy.compat import chb
|
|
||||||
from scapy.contrib.automotive.obd.obd import OBD, OBD_S03, OBD_S07, OBD_S0A, \
|
|
||||||
OBD_S01, OBD_S06, OBD_S08, OBD_S09
|
|
||||||
|
|
||||||
|
|
||||||
def _supported_id_numbers(socket, timeout, service_class, id_name, verbose):
|
|
||||||
""" Check which Parameter IDs are supported by the vehicle
|
|
||||||
|
|
||||||
Args:
|
|
||||||
socket: is the ISOTPSocket, over which the OBD-Services communicate.
|
|
||||||
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
|
|
||||||
timeout: only required for the OBD Simulator, since it might tell it
|
|
||||||
supports a PID, while it actually doesn't and won't respond to this PID.
|
|
||||||
If this happens with a real ECU, it is an implementation error.
|
|
||||||
service_class: specifies, which OBD-Service should be queried.
|
|
||||||
id_name: describes the car domain (e.g.: mid = IDs in Motor Domain).
|
|
||||||
verbose: specifies, whether the sr1()-method gives feedback or not.
|
|
||||||
|
|
||||||
This method sends a query message via a ISOTPSocket, which will be responded by the ECUs with
|
|
||||||
a message containing Bits, representing whether a PID is supported by the vehicle's protocol implementation or not.
|
|
||||||
The first Message has the PID 0x00 and contains 32 Bits, which indicate by their index and value, which PIDs are
|
|
||||||
supported.
|
|
||||||
If the PID 0x20 is supported, that means, there are more supported PIDs within the next 32 PIDs, which will result
|
|
||||||
in a new query message being sent, that contains the next 32 Bits.
|
|
||||||
There is a maximum of 256 possible PIDs.
|
|
||||||
The supported PIDs will be returned as set.
|
|
||||||
"""
|
|
||||||
|
|
||||||
supported_id_numbers = set()
|
|
||||||
supported_prop = 'supported_' + id_name + 's'
|
|
||||||
|
|
||||||
# ID 0x00 requests the first range of supported IDs in OBD
|
|
||||||
supported_ids_req = OBD() / service_class(b'\x00')
|
|
||||||
|
|
||||||
while supported_ids_req is not None:
|
|
||||||
resp = socket.sr1(supported_ids_req, timeout=timeout, verbose=verbose)
|
|
||||||
|
|
||||||
# If None, the device did not respond.
|
|
||||||
# Usually only occurs, if device is off.
|
|
||||||
if resp is None or resp.service == 0x7f:
|
|
||||||
break
|
|
||||||
|
|
||||||
supported_ids_req = None
|
|
||||||
|
|
||||||
all_supported_in_range = getattr(resp.data_records[0], supported_prop)
|
|
||||||
|
|
||||||
for supported in all_supported_in_range:
|
|
||||||
id_number = int(supported[-2:], 16)
|
|
||||||
supported_id_numbers.add(id_number)
|
|
||||||
|
|
||||||
# send a new query if the next PID range is supported
|
|
||||||
if id_number % 0x20 == 0:
|
|
||||||
supported_ids_req = OBD() / service_class(chb(id_number))
|
|
||||||
|
|
||||||
return supported_id_numbers
|
|
||||||
|
|
||||||
|
|
||||||
def _scan_id_service(socket, timeout, service_class, id_numbers, verbose):
|
|
||||||
""" Queries certain PIDs and stores their return value
|
|
||||||
|
|
||||||
Args:
|
|
||||||
socket: is the ISOTPSocket, over which the OBD-Services communicate.
|
|
||||||
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
|
|
||||||
timeout: only required for the OBD Simulator, since it might tell it
|
|
||||||
supports a PID, while it actually doesn't and won't respond to this PID.
|
|
||||||
If this happens with a real ECU, it is an implementation error.
|
|
||||||
service_class: specifies, which OBD-Service should be queried.
|
|
||||||
id_numbers: a set of PIDs, which should be queried by the method.
|
|
||||||
verbose: specifies, whether the sr1()-method gives feedback or not.
|
|
||||||
|
|
||||||
This method queries the specified id_numbers and stores their responses in a dictionary, which is then returned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
data = dict()
|
|
||||||
|
|
||||||
for id_number in id_numbers:
|
|
||||||
id_byte = chb(id_number)
|
|
||||||
# assemble request packet
|
|
||||||
pkt = OBD() / service_class(id_byte)
|
|
||||||
resp = socket.sr1(pkt, timeout=timeout, verbose=verbose)
|
|
||||||
|
|
||||||
if resp is not None:
|
|
||||||
data[id_number] = bytes(resp)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def _scan_dtc_service(socket, timeout, service_class, verbose):
|
|
||||||
""" Queries Diagnostic Trouble Code Parameters and stores their return value
|
|
||||||
|
|
||||||
Args:
|
|
||||||
socket: is the ISOTPSocket, over which the OBD-Services communicate.
|
|
||||||
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
|
|
||||||
timeout: only required for the OBD Simulator, since it might tell it
|
|
||||||
supports a PID, while it actually doesn't and won't respond to this PID.
|
|
||||||
If this happens with a real ECU, it is an implementation error.
|
|
||||||
service_class: specifies, which OBD-Service should be queried.
|
|
||||||
verbose: specifies, whether the sr1()-method gives feedback or not.
|
|
||||||
|
|
||||||
This method queries the specified Diagnostic Trouble Code Parameters and stores their responses in a dictionary,
|
|
||||||
which is then returned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
req = OBD() / service_class()
|
|
||||||
resp = socket.sr1(req, timeout=timeout, verbose=verbose)
|
|
||||||
if resp is not None:
|
|
||||||
return bytes(resp)
|
|
||||||
|
|
||||||
|
|
||||||
def obd_scan(socket, timeout=0.1, supported_ids=False,
|
|
||||||
unsupported_ids=False, verbose=False):
|
|
||||||
""" Scans for all accessible information of each commonly used OBD service classes and prints the results
|
|
||||||
|
|
||||||
Args:
|
|
||||||
socket: is the ISOTPSocket, over which the OBD-Services communicate.
|
|
||||||
the id 0x7df acts as a broadcast address for all obd-supporting ECUs.
|
|
||||||
timeout: only required for the OBD Simulator, since it might tell it
|
|
||||||
supports a PID, while it actually doesn't and won't respond to this PID.
|
|
||||||
If this happens with a real ECU, it is an implementation error.
|
|
||||||
supported_ids: specifies, whether to check for supported Parameter IDs.
|
|
||||||
The OBD-Protocol offers querying, which PIDs the implemented ECUs support.
|
|
||||||
unsupported_ids: specifies, whether to check for unsupported or hidden Parameter IDs.
|
|
||||||
There is a possibility of PIDs answering, which are addressed directly, but which are
|
|
||||||
not listed in the supported query response. We call these PIDs unsupported PIDs, because
|
|
||||||
they are seemingly unsupported.
|
|
||||||
verbose: specifies, whether the sr1()-method gives feedback or not and turns.
|
|
||||||
|
|
||||||
This method queries the Diagnostic Trouble Code Parameters and if selected, supported and/or unsupported PIDS and
|
|
||||||
prints the results.
|
|
||||||
"""
|
|
||||||
|
|
||||||
dtc = dict()
|
|
||||||
supported = dict()
|
|
||||||
unsupported = dict()
|
|
||||||
|
|
||||||
if verbose:
|
|
||||||
print("\nStarting OBD-Scan...")
|
|
||||||
|
|
||||||
print("\nScanning Diagnostic Trouble Codes:")
|
|
||||||
# Emission-related DTCs
|
|
||||||
dtc[3] = _scan_dtc_service(socket, timeout, OBD_S03, verbose)
|
|
||||||
# Emission-related DTCs detected during current or last completed driving
|
|
||||||
# cycle
|
|
||||||
dtc[7] = _scan_dtc_service(socket, timeout, OBD_S07, verbose)
|
|
||||||
# Permanent DTCs
|
|
||||||
dtc[10] = _scan_dtc_service(socket, timeout, OBD_S0A, verbose)
|
|
||||||
print("Service 3:")
|
|
||||||
print(dtc[3])
|
|
||||||
print("Service 7:")
|
|
||||||
print(dtc[7])
|
|
||||||
print("Service 10:")
|
|
||||||
print(dtc[10])
|
|
||||||
|
|
||||||
if not supported_ids and not unsupported_ids:
|
|
||||||
return dtc
|
|
||||||
|
|
||||||
# Powertrain
|
|
||||||
supported_ids_s01 = _supported_id_numbers(
|
|
||||||
socket, timeout, OBD_S01, 'pid', verbose)
|
|
||||||
# On-board monitoring test results for non-continuously monitored systems
|
|
||||||
supported_ids_s06 = _supported_id_numbers(
|
|
||||||
socket, timeout, OBD_S06, 'mid', verbose)
|
|
||||||
# Control of on-board system, test or component
|
|
||||||
supported_ids_s08 = _supported_id_numbers(
|
|
||||||
socket, timeout, OBD_S08, 'tid', verbose)
|
|
||||||
# On-board monitoring test results for non-continuously monitored systems
|
|
||||||
supported_ids_s09 = _supported_id_numbers(
|
|
||||||
socket, timeout, OBD_S09, 'iid', verbose)
|
|
||||||
|
|
||||||
if supported_ids:
|
|
||||||
print("\nScanning supported Parameter IDs")
|
|
||||||
supported[1] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S01, supported_ids_s01, verbose)
|
|
||||||
supported[6] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S06, supported_ids_s06, verbose)
|
|
||||||
supported[8] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S08, supported_ids_s08, verbose)
|
|
||||||
supported[9] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S09, supported_ids_s09, verbose)
|
|
||||||
print("\nSupported PIDs of Service 1:")
|
|
||||||
print(supported[1])
|
|
||||||
print("Supported PIDs of Service 6:")
|
|
||||||
print(supported[6])
|
|
||||||
print("Supported PIDs of Service 8:")
|
|
||||||
print(supported[8])
|
|
||||||
print("Supported PIDs of Service 9:")
|
|
||||||
|
|
||||||
# this option will slow down the test a lot, since it tests for seemingly unsupported ids
|
|
||||||
# the chances of those actually responding will be small, so a lot of
|
|
||||||
# timeouts can be expected
|
|
||||||
if unsupported_ids:
|
|
||||||
# the complete id range is from 1 to 255
|
|
||||||
all_ids_set = set(range(1, 256))
|
|
||||||
# the unsupported id ranges are obtained by creating the compliment set
|
|
||||||
# excluding 0
|
|
||||||
unsupported_ids_s01 = all_ids_set - supported_ids_s01
|
|
||||||
unsupported_ids_s06 = all_ids_set - supported_ids_s06
|
|
||||||
unsupported_ids_s08 = all_ids_set - supported_ids_s08
|
|
||||||
unsupported_ids_s09 = all_ids_set - supported_ids_s09
|
|
||||||
|
|
||||||
print("\nScanning unsupported Parameter IDs")
|
|
||||||
if verbose:
|
|
||||||
print("This may take a while...")
|
|
||||||
unsupported[1] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S01, unsupported_ids_s01, verbose)
|
|
||||||
unsupported[6] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S06, unsupported_ids_s06, verbose)
|
|
||||||
unsupported[8] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S08, unsupported_ids_s08, verbose)
|
|
||||||
unsupported[9] = _scan_id_service(
|
|
||||||
socket, timeout, OBD_S09, unsupported_ids_s09, verbose)
|
|
||||||
print("\nUnsupported PIDs of Service 1:")
|
|
||||||
print(unsupported[1])
|
|
||||||
print("Unsupported PIDs of Service 6:")
|
|
||||||
print(unsupported[6])
|
|
||||||
print("unsupported PIDs of Service 8:")
|
|
||||||
print(unsupported[8])
|
|
||||||
print("Unsupported PIDs of Service 9:")
|
|
||||||
print(unsupported[9])
|
|
||||||
|
|
||||||
return dtc, supported, unsupported
|
|
|
@ -1,153 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import ByteField, XByteField, BitEnumField, \
|
|
||||||
PacketListField, XBitField, XByteEnumField, FieldListField, FieldLenField
|
|
||||||
from scapy.packet import Packet
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
from scapy.config import conf
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_DTC(OBD_Packet):
|
|
||||||
name = "DiagnosticTroubleCode"
|
|
||||||
|
|
||||||
locations = {
|
|
||||||
0b00: 'Powertrain',
|
|
||||||
0b01: 'Chassis',
|
|
||||||
0b10: 'Body',
|
|
||||||
0b11: 'Network',
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
BitEnumField('location', 0, 2, locations),
|
|
||||||
XBitField('code1', 0, 2),
|
|
||||||
XBitField('code2', 0, 4),
|
|
||||||
XBitField('code3', 0, 4),
|
|
||||||
XBitField('code4', 0, 4),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_NR(Packet):
|
|
||||||
name = "NegativeResponse"
|
|
||||||
|
|
||||||
responses = {
|
|
||||||
0x10: 'generalReject',
|
|
||||||
0x11: 'serviceNotSupported',
|
|
||||||
0x12: 'subFunctionNotSupported-InvalidFormat',
|
|
||||||
0x21: 'busy-RepeatRequest',
|
|
||||||
0x22: 'conditionsNotCorrectOrRequestSequenceError',
|
|
||||||
0x78: 'requestCorrectlyReceived-ResponsePending'
|
|
||||||
}
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('request_service_id', 0),
|
|
||||||
XByteEnumField('response_code', 0, responses)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return self.request_service_id == other.service and \
|
|
||||||
(self.response_code != 0x78 or
|
|
||||||
conf.contribs['OBD']['treat-response-pending-as-answer'])
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S01(Packet):
|
|
||||||
name = "S1_CurrentData"
|
|
||||||
fields_desc = [
|
|
||||||
FieldListField("pid", [], XByteField('', 0))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S02_Record(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
XByteField('pid', 0),
|
|
||||||
ByteField('frame_no', 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S02(Packet):
|
|
||||||
name = "S2_FreezeFrameData"
|
|
||||||
fields_desc = [
|
|
||||||
PacketListField("requests", [], OBD_S02_Record)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S03(Packet):
|
|
||||||
name = "S3_RequestDTCs"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S03_PR(Packet):
|
|
||||||
name = "S3_ResponseDTCs"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='dtcs', fmt='B'),
|
|
||||||
PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S03
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S04(Packet):
|
|
||||||
name = "S4_ClearDTCs"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S04_PR(Packet):
|
|
||||||
name = "S4_ClearDTCsPositiveResponse"
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S04
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S06(Packet):
|
|
||||||
name = "S6_OnBoardDiagnosticMonitoring"
|
|
||||||
fields_desc = [
|
|
||||||
FieldListField("mid", [], XByteField('', 0))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S07(Packet):
|
|
||||||
name = "S7_RequestPendingDTCs"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S07_PR(Packet):
|
|
||||||
name = "S7_ResponsePendingDTCs"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='dtcs', fmt='B'),
|
|
||||||
PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S07
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S08(Packet):
|
|
||||||
name = "S8_RequestControlOfSystem"
|
|
||||||
fields_desc = [
|
|
||||||
FieldListField("tid", [], XByteField('', 0))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S09(Packet):
|
|
||||||
name = "S9_VehicleInformation"
|
|
||||||
fields_desc = [
|
|
||||||
FieldListField("iid", [], XByteField('', 0))
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S0A(Packet):
|
|
||||||
name = "S0A_RequestPermanentDTCs"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S0A_PR(Packet):
|
|
||||||
name = "S0A_ResponsePermanentDTCs"
|
|
||||||
fields_desc = [
|
|
||||||
FieldLenField('count', None, count_of='dtcs', fmt='B'),
|
|
||||||
PacketListField('dtcs', [], OBD_DTC, count_from=lambda pkt: pkt.count)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S0A
|
|
|
@ -1,12 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive obd specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
|
@ -1,153 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Andreas Korb <andreas.d.korb@gmail.com>
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
from scapy.fields import FlagsField, ByteField, ScalingField, PacketListField
|
|
||||||
from scapy.packet import bind_layers, Packet
|
|
||||||
from scapy.contrib.automotive.obd.packet import OBD_Packet
|
|
||||||
from scapy.contrib.automotive.obd.services import OBD_S08
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_TID_Voltage(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data_a', 0, 0.005, "V"),
|
|
||||||
ScalingField('data_b', 0, 0.005, "V"),
|
|
||||||
ScalingField('data_c', 0, 0.005, "V"),
|
|
||||||
ScalingField('data_d', 0, 0.005, "V"),
|
|
||||||
ScalingField('data_e', 0, 0.005, "V"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_TID_Time(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data_a', 0, 0.004, "s"),
|
|
||||||
ScalingField('data_b', 0, 0.004, "s"),
|
|
||||||
ScalingField('data_c', 0, 0.004, "s"),
|
|
||||||
ScalingField('data_d', 0, 0.004, "s"),
|
|
||||||
ScalingField('data_e', 0, 0.004, "s"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _OBD_TID_Period(OBD_Packet):
|
|
||||||
fields_desc = [
|
|
||||||
ScalingField('data_a', 0, 0.04, "s"),
|
|
||||||
ScalingField('data_b', 0, 0.04, "s"),
|
|
||||||
ScalingField('data_c', 0, 0.04, "s"),
|
|
||||||
ScalingField('data_d', 0, 0.04, "s"),
|
|
||||||
ScalingField('data_e', 0, 0.04, "s"),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID00(OBD_Packet):
|
|
||||||
name = "TID_00_Service8SupportedTestIdentifiers"
|
|
||||||
fields_desc = [
|
|
||||||
FlagsField('supported_tids', 0, 32, [
|
|
||||||
'TID20',
|
|
||||||
'TID1F',
|
|
||||||
'TID1E',
|
|
||||||
'TID1D',
|
|
||||||
'TID1C',
|
|
||||||
'TID1B',
|
|
||||||
'TID1A',
|
|
||||||
'TID19',
|
|
||||||
'TID18',
|
|
||||||
'TID17',
|
|
||||||
'TID16',
|
|
||||||
'TID15',
|
|
||||||
'TID14',
|
|
||||||
'TID13',
|
|
||||||
'TID12',
|
|
||||||
'TID11',
|
|
||||||
'TID10',
|
|
||||||
'TID0F',
|
|
||||||
'TID0E',
|
|
||||||
'TID0D',
|
|
||||||
'TID0C',
|
|
||||||
'TID0B',
|
|
||||||
'TID0A',
|
|
||||||
'TID09',
|
|
||||||
'TID08',
|
|
||||||
'TID07',
|
|
||||||
'TID06',
|
|
||||||
'TID05',
|
|
||||||
'TID04',
|
|
||||||
'TID03',
|
|
||||||
'TID02',
|
|
||||||
'TID01'
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID01(_OBD_TID_Voltage):
|
|
||||||
name = "TID_01_RichToLeanSensorThresholdVoltage"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID02(_OBD_TID_Voltage):
|
|
||||||
name = "TID_02_LeanToRichSensorThresholdVoltage"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID03(_OBD_TID_Voltage):
|
|
||||||
name = "TID_03_LowSensorVoltageForSwitchTimeCalculation"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID04(_OBD_TID_Voltage):
|
|
||||||
name = "TID_04_HighSensorVoltageForSwitchTimeCalculation"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID05(_OBD_TID_Time):
|
|
||||||
name = "TID_05_RichToLeanSensorSwitchTime"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID06(_OBD_TID_Time):
|
|
||||||
name = "TID_06_LeanToRichSensorSwitchTime"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID07(_OBD_TID_Voltage):
|
|
||||||
name = "TID_07_MinimumSensorVoltageForTestCycle"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID08(_OBD_TID_Voltage):
|
|
||||||
name = "TID_08_MaximumSensorVoltageForTestCycle"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID09(_OBD_TID_Period):
|
|
||||||
name = "TID_09_TimeBetweenSensorTransitions"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_TID0A(_OBD_TID_Period):
|
|
||||||
name = "TID_0A_SensorPeriod"
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S08_PR_Record(Packet):
|
|
||||||
name = "Control Operation ID"
|
|
||||||
fields_desc = [
|
|
||||||
ByteField("tid", 0),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class OBD_S08_PR(Packet):
|
|
||||||
name = "Control Operation IDs"
|
|
||||||
fields_desc = [
|
|
||||||
PacketListField("data_records", [], OBD_S08_PR_Record)
|
|
||||||
]
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
return other.__class__ == OBD_S08 \
|
|
||||||
and all(r.tid in other.tid for r in self.data_records)
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID00, tid=0x00)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID01, tid=0x01)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID02, tid=0x02)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID03, tid=0x03)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID04, tid=0x04)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID05, tid=0x05)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID06, tid=0x06)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID07, tid=0x07)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID08, tid=0x08)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID09, tid=0x09)
|
|
||||||
bind_layers(OBD_S08_PR_Record, OBD_TID0A, tid=0x0A)
|
|
|
@ -1,526 +0,0 @@
|
||||||
# MIT License
|
|
||||||
|
|
||||||
# Copyright (c) 2018 Jose Amores
|
|
||||||
|
|
||||||
# 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 information
|
|
||||||
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = Scalable service-Oriented MiddlewarE/IP (SOME/IP)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import collections
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from scapy.layers.inet import TCP, UDP
|
|
||||||
from scapy.layers.inet6 import IP6Field
|
|
||||||
from scapy.compat import raw, orb
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.modules.six.moves import range
|
|
||||||
from scapy.packet import Packet, Raw, bind_top_down, bind_bottom_up
|
|
||||||
from scapy.fields import XShortField, BitEnumField, ConditionalField, \
|
|
||||||
BitField, XBitField, IntField, XByteField, ByteEnumField, \
|
|
||||||
ShortField, X3BytesField, StrLenField, IPField, FieldLenField, \
|
|
||||||
PacketListField, XIntField
|
|
||||||
|
|
||||||
|
|
||||||
class SOMEIP(Packet):
|
|
||||||
""" SOME/IP Packet."""
|
|
||||||
|
|
||||||
PROTOCOL_VERSION = 0x01
|
|
||||||
INTERFACE_VERSION = 0x01
|
|
||||||
LEN_OFFSET = 0x08
|
|
||||||
LEN_OFFSET_TP = 0x0c
|
|
||||||
TYPE_REQUEST = 0x00
|
|
||||||
TYPE_REQUEST_NO_RET = 0x01
|
|
||||||
TYPE_NOTIFICATION = 0x02
|
|
||||||
TYPE_REQUEST_ACK = 0x40
|
|
||||||
TYPE_REQUEST_NORET_ACK = 0x41
|
|
||||||
TYPE_NOTIFICATION_ACK = 0x42
|
|
||||||
TYPE_RESPONSE = 0x80
|
|
||||||
TYPE_ERROR = 0x81
|
|
||||||
TYPE_RESPONSE_ACK = 0xc0
|
|
||||||
TYPE_ERROR_ACK = 0xc1
|
|
||||||
TYPE_TP_REQUEST = 0x20
|
|
||||||
TYPE_TP_REQUEST_NO_RET = 0x21
|
|
||||||
TYPE_TP_NOTIFICATION = 0x22
|
|
||||||
TYPE_TP_RESPONSE = 0x23
|
|
||||||
TYPE_TP_ERROR = 0x24
|
|
||||||
RET_E_OK = 0x00
|
|
||||||
RET_E_NOT_OK = 0x01
|
|
||||||
RET_E_UNKNOWN_SERVICE = 0x02
|
|
||||||
RET_E_UNKNOWN_METHOD = 0x03
|
|
||||||
RET_E_NOT_READY = 0x04
|
|
||||||
RET_E_NOT_REACHABLE = 0x05
|
|
||||||
RET_E_TIMEOUT = 0x06
|
|
||||||
RET_E_WRONG_PROTOCOL_V = 0x07
|
|
||||||
RET_E_WRONG_INTERFACE_V = 0x08
|
|
||||||
RET_E_MALFORMED_MSG = 0x09
|
|
||||||
RET_E_WRONG_MESSAGE_TYPE = 0x0a
|
|
||||||
|
|
||||||
_OVERALL_LEN_NOPAYLOAD = 16
|
|
||||||
|
|
||||||
name = "SOME/IP"
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
XShortField("srv_id", 0),
|
|
||||||
BitEnumField("sub_id", 0, 1, {0: "METHOD_ID", 1: "EVENT_ID"}),
|
|
||||||
ConditionalField(XBitField("method_id", 0, 15),
|
|
||||||
lambda pkt: pkt.sub_id == 0),
|
|
||||||
ConditionalField(XBitField("event_id", 0, 15),
|
|
||||||
lambda pkt: pkt.sub_id == 1),
|
|
||||||
IntField("len", None),
|
|
||||||
XShortField("client_id", 0),
|
|
||||||
XShortField("session_id", 0),
|
|
||||||
XByteField("proto_ver", PROTOCOL_VERSION),
|
|
||||||
XByteField("iface_ver", INTERFACE_VERSION),
|
|
||||||
ByteEnumField("msg_type", TYPE_REQUEST, {
|
|
||||||
TYPE_REQUEST: "REQUEST",
|
|
||||||
TYPE_REQUEST_NO_RET: "REQUEST_NO_RETURN",
|
|
||||||
TYPE_NOTIFICATION: "NOTIFICATION",
|
|
||||||
TYPE_REQUEST_ACK: "REQUEST_ACK",
|
|
||||||
TYPE_REQUEST_NORET_ACK: "REQUEST_NO_RETURN_ACK",
|
|
||||||
TYPE_NOTIFICATION_ACK: "NOTIFICATION_ACK",
|
|
||||||
TYPE_RESPONSE: "RESPONSE",
|
|
||||||
TYPE_ERROR: "ERROR",
|
|
||||||
TYPE_RESPONSE_ACK: "RESPONSE_ACK",
|
|
||||||
TYPE_ERROR_ACK: "ERROR_ACK",
|
|
||||||
TYPE_TP_REQUEST: "TP_REQUEST",
|
|
||||||
TYPE_TP_REQUEST_NO_RET: "TP_REQUEST_NO_RETURN",
|
|
||||||
TYPE_TP_NOTIFICATION: "TP_NOTIFICATION",
|
|
||||||
TYPE_TP_RESPONSE: "TP_RESPONSE",
|
|
||||||
TYPE_TP_ERROR: "TP_ERROR",
|
|
||||||
}),
|
|
||||||
ByteEnumField("retcode", 0, {
|
|
||||||
RET_E_OK: "E_OK",
|
|
||||||
RET_E_NOT_OK: "E_NOT_OK",
|
|
||||||
RET_E_UNKNOWN_SERVICE: "E_UNKNOWN_SERVICE",
|
|
||||||
RET_E_UNKNOWN_METHOD: "E_UNKNOWN_METHOD",
|
|
||||||
RET_E_NOT_READY: "E_NOT_READY",
|
|
||||||
RET_E_NOT_REACHABLE: "E_NOT_REACHABLE",
|
|
||||||
RET_E_TIMEOUT: "E_TIMEOUT",
|
|
||||||
RET_E_WRONG_PROTOCOL_V: "E_WRONG_PROTOCOL_VERSION",
|
|
||||||
RET_E_WRONG_INTERFACE_V: "E_WRONG_INTERFACE_VERSION",
|
|
||||||
RET_E_MALFORMED_MSG: "E_MALFORMED_MESSAGE",
|
|
||||||
RET_E_WRONG_MESSAGE_TYPE: "E_WRONG_MESSAGE_TYPE",
|
|
||||||
}),
|
|
||||||
ConditionalField(BitField("offset", 0, 28),
|
|
||||||
lambda pkt: SOMEIP._is_tp(pkt)),
|
|
||||||
ConditionalField(BitField("res", 0, 3),
|
|
||||||
lambda pkt: SOMEIP._is_tp(pkt)),
|
|
||||||
ConditionalField(BitField("more_seg", 0, 1),
|
|
||||||
lambda pkt: SOMEIP._is_tp(pkt))
|
|
||||||
]
|
|
||||||
|
|
||||||
def post_build(self, pkt, pay):
|
|
||||||
length = self.len
|
|
||||||
if length is None:
|
|
||||||
if SOMEIP._is_tp(self):
|
|
||||||
length = SOMEIP.LEN_OFFSET_TP + len(pay)
|
|
||||||
else:
|
|
||||||
length = SOMEIP.LEN_OFFSET + len(pay)
|
|
||||||
|
|
||||||
pkt = pkt[:4] + struct.pack("!I", length) + pkt[8:]
|
|
||||||
return pkt + pay
|
|
||||||
|
|
||||||
def answers(self, other):
|
|
||||||
if other.__class__ == self.__class__:
|
|
||||||
if self.msg_type in [SOMEIP.TYPE_REQUEST_NO_RET,
|
|
||||||
SOMEIP.TYPE_REQUEST_NORET_ACK,
|
|
||||||
SOMEIP.TYPE_NOTIFICATION,
|
|
||||||
SOMEIP.TYPE_TP_REQUEST_NO_RET,
|
|
||||||
SOMEIP.TYPE_TP_NOTIFICATION]:
|
|
||||||
return 0
|
|
||||||
return self.payload.answers(other.payload)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_tp(pkt):
|
|
||||||
"""Returns true if pkt is using SOMEIP-TP, else returns false."""
|
|
||||||
|
|
||||||
tp = [SOMEIP.TYPE_TP_REQUEST, SOMEIP.TYPE_TP_REQUEST_NO_RET,
|
|
||||||
SOMEIP.TYPE_TP_NOTIFICATION, SOMEIP.TYPE_TP_RESPONSE,
|
|
||||||
SOMEIP.TYPE_TP_ERROR]
|
|
||||||
if isinstance(pkt, Packet):
|
|
||||||
return pkt.msg_type in tp
|
|
||||||
else:
|
|
||||||
return pkt[15] in tp
|
|
||||||
|
|
||||||
def fragment(self, fragsize=1392):
|
|
||||||
"""Fragment SOME/IP-TP"""
|
|
||||||
fnb = 0
|
|
||||||
fl = self
|
|
||||||
lst = list()
|
|
||||||
while fl.underlayer is not None:
|
|
||||||
fnb += 1
|
|
||||||
fl = fl.underlayer
|
|
||||||
|
|
||||||
for p in fl:
|
|
||||||
s = raw(p[fnb].payload)
|
|
||||||
nb = (len(s) + fragsize) // fragsize
|
|
||||||
for i in range(nb):
|
|
||||||
q = p.copy()
|
|
||||||
del q[fnb].payload
|
|
||||||
q[fnb].len = SOMEIP.LEN_OFFSET_TP + \
|
|
||||||
len(s[i * fragsize:(i + 1) * fragsize])
|
|
||||||
q[fnb].more_seg = 1
|
|
||||||
if i == nb - 1:
|
|
||||||
q[fnb].more_seg = 0
|
|
||||||
q[fnb].offset += i * fragsize // 16
|
|
||||||
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 _bind_someip_layers():
|
|
||||||
bind_top_down(UDP, SOMEIP, sport=30490, dport=30490)
|
|
||||||
|
|
||||||
for i in range(15):
|
|
||||||
bind_bottom_up(UDP, SOMEIP, sport=30490 + i)
|
|
||||||
bind_bottom_up(TCP, SOMEIP, sport=30490 + i)
|
|
||||||
bind_bottom_up(UDP, SOMEIP, dport=30490 + i)
|
|
||||||
bind_bottom_up(TCP, SOMEIP, dport=30490 + i)
|
|
||||||
|
|
||||||
|
|
||||||
_bind_someip_layers()
|
|
||||||
|
|
||||||
|
|
||||||
class _SDPacketBase(Packet):
|
|
||||||
""" base class to be used among all SD Packet definitions."""
|
|
||||||
def extract_padding(self, s):
|
|
||||||
return "", s
|
|
||||||
|
|
||||||
|
|
||||||
SDENTRY_TYPE_SRV_FINDSERVICE = 0x00
|
|
||||||
SDENTRY_TYPE_SRV_OFFERSERVICE = 0x01
|
|
||||||
SDENTRY_TYPE_SRV = (SDENTRY_TYPE_SRV_FINDSERVICE,
|
|
||||||
SDENTRY_TYPE_SRV_OFFERSERVICE)
|
|
||||||
SDENTRY_TYPE_EVTGRP_SUBSCRIBE = 0x06
|
|
||||||
SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07
|
|
||||||
SDENTRY_TYPE_EVTGRP = (SDENTRY_TYPE_EVTGRP_SUBSCRIBE,
|
|
||||||
SDENTRY_TYPE_EVTGRP_SUBSCRIBE_ACK)
|
|
||||||
SDENTRY_OVERALL_LEN = 16
|
|
||||||
|
|
||||||
|
|
||||||
def _MAKE_SDENTRY_COMMON_FIELDS_DESC(type):
|
|
||||||
return [
|
|
||||||
XByteField("type", type),
|
|
||||||
XByteField("index_1", 0),
|
|
||||||
XByteField("index_2", 0),
|
|
||||||
XBitField("n_opt_1", 0, 4),
|
|
||||||
XBitField("n_opt_2", 0, 4),
|
|
||||||
XShortField("srv_id", 0),
|
|
||||||
XShortField("inst_id", 0),
|
|
||||||
XByteField("major_ver", 0),
|
|
||||||
X3BytesField("ttl", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDEntry_Service(_SDPacketBase):
|
|
||||||
name = "Service Entry"
|
|
||||||
fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
|
|
||||||
SDENTRY_TYPE_SRV_FINDSERVICE)
|
|
||||||
fields_desc += [
|
|
||||||
XIntField("minor_ver", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDEntry_EventGroup(_SDPacketBase):
|
|
||||||
name = "Eventgroup Entry"
|
|
||||||
fields_desc = _MAKE_SDENTRY_COMMON_FIELDS_DESC(
|
|
||||||
SDENTRY_TYPE_EVTGRP_SUBSCRIBE)
|
|
||||||
fields_desc += [
|
|
||||||
XBitField("res", 0, 12),
|
|
||||||
XBitField("cnt", 0, 4),
|
|
||||||
XShortField("eventgroup_id", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _sdentry_class(payload, **kargs):
|
|
||||||
TYPE_PAYLOAD_I = 0
|
|
||||||
pl_type = orb(payload[TYPE_PAYLOAD_I])
|
|
||||||
cls = None
|
|
||||||
|
|
||||||
if pl_type in SDENTRY_TYPE_SRV:
|
|
||||||
cls = SDEntry_Service
|
|
||||||
elif pl_type in SDENTRY_TYPE_EVTGRP:
|
|
||||||
cls = SDEntry_EventGroup
|
|
||||||
|
|
||||||
return cls(payload, **kargs)
|
|
||||||
|
|
||||||
|
|
||||||
def _sdoption_class(payload, **kargs):
|
|
||||||
pl_type = orb(payload[2])
|
|
||||||
|
|
||||||
cls = {
|
|
||||||
SDOPTION_CFG_TYPE: SDOption_Config,
|
|
||||||
SDOPTION_LOADBALANCE_TYPE: SDOption_LoadBalance,
|
|
||||||
SDOPTION_IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint,
|
|
||||||
SDOPTION_IP4_MCAST_TYPE: SDOption_IP4_Multicast,
|
|
||||||
SDOPTION_IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint,
|
|
||||||
SDOPTION_IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint,
|
|
||||||
SDOPTION_IP6_MCAST_TYPE: SDOption_IP6_Multicast,
|
|
||||||
SDOPTION_IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint
|
|
||||||
}.get(pl_type, Raw)
|
|
||||||
|
|
||||||
return cls(payload, **kargs)
|
|
||||||
|
|
||||||
|
|
||||||
# SD Option
|
|
||||||
SDOPTION_CFG_TYPE = 0x01
|
|
||||||
SDOPTION_LOADBALANCE_TYPE = 0x02
|
|
||||||
SDOPTION_LOADBALANCE_LEN = 0x05
|
|
||||||
SDOPTION_IP4_ENDPOINT_TYPE = 0x04
|
|
||||||
SDOPTION_IP4_ENDPOINT_LEN = 0x0009
|
|
||||||
SDOPTION_IP4_MCAST_TYPE = 0x14
|
|
||||||
SDOPTION_IP4_MCAST_LEN = 0x0009
|
|
||||||
SDOPTION_IP4_SDENDPOINT_TYPE = 0x24
|
|
||||||
SDOPTION_IP4_SDENDPOINT_LEN = 0x0009
|
|
||||||
SDOPTION_IP6_ENDPOINT_TYPE = 0x06
|
|
||||||
SDOPTION_IP6_ENDPOINT_LEN = 0x0015
|
|
||||||
SDOPTION_IP6_MCAST_TYPE = 0x16
|
|
||||||
SDOPTION_IP6_MCAST_LEN = 0x0015
|
|
||||||
SDOPTION_IP6_SDENDPOINT_TYPE = 0x26
|
|
||||||
SDOPTION_IP6_SDENDPOINT_LEN = 0x0015
|
|
||||||
|
|
||||||
|
|
||||||
def _MAKE_COMMON_SDOPTION_FIELDS_DESC(type, length=None):
|
|
||||||
return [
|
|
||||||
ShortField("len", length),
|
|
||||||
XByteField("type", type),
|
|
||||||
XByteField("res_hdr", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC():
|
|
||||||
return [
|
|
||||||
XByteField("res_tail", 0),
|
|
||||||
ByteEnumField("l4_proto", 0x11, {0x06: "TCP", 0x11: "UDP"}),
|
|
||||||
ShortField("port", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_Config(_SDPacketBase):
|
|
||||||
name = "Config Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(SDOPTION_CFG_TYPE) + [
|
|
||||||
StrLenField("cfg_str", "\x00", length_from=lambda pkt: pkt.len - 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
def post_build(self, pkt, pay):
|
|
||||||
if self.len is None:
|
|
||||||
length = len(self.cfg_str) + 1 # res_hdr field takes 1 byte
|
|
||||||
pkt = struct.pack("!H", length) + pkt[2:]
|
|
||||||
return pkt + pay
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def make_string(data):
|
|
||||||
# Build a valid null-terminated configuration string from a dict or a
|
|
||||||
# list with key-value pairs.
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# >>> SDOption_Config.make_string({ "hello": "world" })
|
|
||||||
# b'\x0bhello=world\x00'
|
|
||||||
#
|
|
||||||
# >>> SDOption_Config.make_string([
|
|
||||||
# ... ("x", "y"),
|
|
||||||
# ... ("abc", "def"),
|
|
||||||
# ... ("123", "456")
|
|
||||||
# ... ])
|
|
||||||
# b'\x03x=y\x07abc=def\x07123=456\x00'
|
|
||||||
|
|
||||||
if isinstance(data, dict):
|
|
||||||
data = data.items()
|
|
||||||
|
|
||||||
# combine entries
|
|
||||||
data = ("{}={}".format(k, v) for k, v in data)
|
|
||||||
# prepend length
|
|
||||||
data = ("{}{}".format(chr(len(v)), v) for v in data)
|
|
||||||
# concatenate
|
|
||||||
data = "".join(data)
|
|
||||||
data += "\x00"
|
|
||||||
|
|
||||||
return data.encode("utf8")
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_LoadBalance(_SDPacketBase):
|
|
||||||
name = "LoadBalance Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_LOADBALANCE_TYPE, SDOPTION_LOADBALANCE_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
ShortField("priority", 0),
|
|
||||||
ShortField("weight", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP4_EndPoint(_SDPacketBase):
|
|
||||||
name = "IP4 EndPoint Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_IP4_ENDPOINT_TYPE, SDOPTION_IP4_ENDPOINT_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
IPField("addr", "0.0.0.0"),
|
|
||||||
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP4_Multicast(_SDPacketBase):
|
|
||||||
name = "IP4 Multicast Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_IP4_MCAST_TYPE, SDOPTION_IP4_MCAST_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
IPField("addr", "0.0.0.0"),
|
|
||||||
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP4_SD_EndPoint(_SDPacketBase):
|
|
||||||
name = "IP4 SDEndPoint Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_IP4_SDENDPOINT_TYPE, SDOPTION_IP4_SDENDPOINT_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
IPField("addr", "0.0.0.0"),
|
|
||||||
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP6_EndPoint(_SDPacketBase):
|
|
||||||
name = "IP6 EndPoint Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_IP6_ENDPOINT_TYPE, SDOPTION_IP6_ENDPOINT_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
IP6Field("addr", "::"),
|
|
||||||
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP6_Multicast(_SDPacketBase):
|
|
||||||
name = "IP6 Multicast Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_IP6_MCAST_TYPE, SDOPTION_IP6_MCAST_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
IP6Field("addr", "::"),
|
|
||||||
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP6_SD_EndPoint(_SDPacketBase):
|
|
||||||
name = "IP6 SDEndPoint Option"
|
|
||||||
fields_desc = _MAKE_COMMON_SDOPTION_FIELDS_DESC(
|
|
||||||
SDOPTION_IP6_SDENDPOINT_TYPE, SDOPTION_IP6_SDENDPOINT_LEN)
|
|
||||||
fields_desc += [
|
|
||||||
IP6Field("addr", "::"),
|
|
||||||
] + _MAKE_COMMON_IP_SDOPTION_FIELDS_DESC()
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# SD PACKAGE DEFINITION
|
|
||||||
##
|
|
||||||
class SD(_SDPacketBase):
|
|
||||||
"""
|
|
||||||
SD Packet
|
|
||||||
|
|
||||||
NOTE : when adding 'entries' or 'options', do not use list.append()
|
|
||||||
method but create a new list
|
|
||||||
e.g. : p = SD()
|
|
||||||
p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()]
|
|
||||||
"""
|
|
||||||
SOMEIP_MSGID_SRVID = 0xffff
|
|
||||||
SOMEIP_MSGID_SUBID = 0x1
|
|
||||||
SOMEIP_MSGID_EVENTID = 0x100
|
|
||||||
SOMEIP_CLIENT_ID = 0x0000
|
|
||||||
SOMEIP_MINIMUM_SESSION_ID = 0x0001
|
|
||||||
SOMEIP_PROTO_VER = 0x01
|
|
||||||
SOMEIP_IFACE_VER = 0x01
|
|
||||||
SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION
|
|
||||||
SOMEIP_RETCODE = SOMEIP.RET_E_OK
|
|
||||||
|
|
||||||
_sdFlag = collections.namedtuple('Flag', 'mask offset')
|
|
||||||
FLAGSDEF = {
|
|
||||||
"REBOOT": _sdFlag(mask=0x80, offset=7),
|
|
||||||
"UNICAST": _sdFlag(mask=0x40, offset=6)
|
|
||||||
}
|
|
||||||
|
|
||||||
name = "SD"
|
|
||||||
fields_desc = [
|
|
||||||
XByteField("flags", 0),
|
|
||||||
X3BytesField("res", 0),
|
|
||||||
FieldLenField("len_entry_array", None,
|
|
||||||
length_of="entry_array", fmt="!I"),
|
|
||||||
PacketListField("entry_array", None, cls=_sdentry_class,
|
|
||||||
length_from=lambda pkt: pkt.len_entry_array),
|
|
||||||
FieldLenField("len_option_array", None,
|
|
||||||
length_of="option_array", fmt="!I"),
|
|
||||||
PacketListField("option_array", None, cls=_sdoption_class,
|
|
||||||
length_from=lambda pkt: pkt.len_option_array)
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_flag(self, name):
|
|
||||||
name = name.upper()
|
|
||||||
if name in self.FLAGSDEF:
|
|
||||||
return ((self.flags & self.FLAGSDEF[name].mask) >>
|
|
||||||
self.FLAGSDEF[name].offset)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_flag(self, name, value):
|
|
||||||
name = name.upper()
|
|
||||||
if name in self.FLAGSDEF:
|
|
||||||
self.flags = (self.flags &
|
|
||||||
(ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \
|
|
||||||
| ((value & 0x01) << self.FLAGSDEF[name].offset)
|
|
||||||
|
|
||||||
def set_entryArray(self, entry_list):
|
|
||||||
if isinstance(entry_list, list):
|
|
||||||
self.entry_array = entry_list
|
|
||||||
else:
|
|
||||||
self.entry_array = [entry_list]
|
|
||||||
|
|
||||||
def set_optionArray(self, option_list):
|
|
||||||
if isinstance(option_list, list):
|
|
||||||
self.option_array = option_list
|
|
||||||
else:
|
|
||||||
self.option_array = [option_list]
|
|
||||||
|
|
||||||
|
|
||||||
bind_top_down(SOMEIP, SD,
|
|
||||||
srv_id=SD.SOMEIP_MSGID_SRVID,
|
|
||||||
sub_id=SD.SOMEIP_MSGID_SUBID,
|
|
||||||
client_id=SD.SOMEIP_CLIENT_ID,
|
|
||||||
session_id=SD.SOMEIP_MINIMUM_SESSION_ID,
|
|
||||||
event_id=SD.SOMEIP_MSGID_EVENTID,
|
|
||||||
proto_ver=SD.SOMEIP_PROTO_VER,
|
|
||||||
iface_ver=SD.SOMEIP_IFACE_VER,
|
|
||||||
msg_type=SD.SOMEIP_MSG_TYPE,
|
|
||||||
retcode=SD.SOMEIP_RETCODE)
|
|
||||||
|
|
||||||
bind_bottom_up(SOMEIP, SD,
|
|
||||||
srv_id=SD.SOMEIP_MSGID_SRVID,
|
|
||||||
sub_id=SD.SOMEIP_MSGID_SUBID,
|
|
||||||
event_id=SD.SOMEIP_MSGID_EVENTID,
|
|
||||||
proto_ver=SD.SOMEIP_PROTO_VER,
|
|
||||||
iface_ver=SD.SOMEIP_IFACE_VER,
|
|
||||||
msg_type=SD.SOMEIP_MSG_TYPE,
|
|
||||||
retcode=SD.SOMEIP_RETCODE)
|
|
||||||
|
|
||||||
# FIXME: Service Discovery messages shall be transported over UDP
|
|
||||||
# (TR_SOMEIP_00248)
|
|
||||||
# FIXME: The port 30490 (UDP and TCP as well) shall be only used for SOME/IP-SD
|
|
||||||
# and not used for applications communicating over SOME/IP
|
|
||||||
# (TR_SOMEIP_00020)
|
|
|
@ -1,257 +0,0 @@
|
||||||
# MIT License
|
|
||||||
|
|
||||||
# Copyright (c) 2018 Jose Amores
|
|
||||||
|
|
||||||
# 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 information
|
|
||||||
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
##########
|
|
||||||
##########
|
|
||||||
|
|
||||||
|
|
||||||
+ Test MessageId
|
|
||||||
|
|
||||||
= Load module
|
|
||||||
|
|
||||||
load_contrib("automotive.someip")
|
|
||||||
import binascii
|
|
||||||
|
|
||||||
= Check MessageId with method_id
|
|
||||||
|
|
||||||
p = SOMEIP().msg_id
|
|
||||||
p.srv_id = 0x1111
|
|
||||||
p.method_id = 0x0222
|
|
||||||
p.event_id = 0x0333
|
|
||||||
|
|
||||||
p.sub_id = 0
|
|
||||||
|
|
||||||
assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
|
|
||||||
|
|
||||||
assert((struct.unpack("!B", bytes(p)[2:3])[0] & 0x80) == 0x00)
|
|
||||||
|
|
||||||
assert((struct.unpack("!H", bytes(p)[2:4])[0] & ~0x8000) == 0x0222)
|
|
||||||
|
|
||||||
assert(bytes(p) == b"\x11\x11\x02\x22")
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
= Dissect MessageId with method_id
|
|
||||||
|
|
||||||
|
|
||||||
p = SOMEIP(b'\x22\x22\x03\x33')
|
|
||||||
|
|
||||||
assert(p.msg_id.srv_id == 0x2222)
|
|
||||||
|
|
||||||
assert(p.msg_id.method_id == 0x0333)
|
|
||||||
|
|
||||||
assert(p.msg_id.sub_id == 0)
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
= Build MessageId with event_id
|
|
||||||
|
|
||||||
p = SOMEIP().msg_id
|
|
||||||
p.srv_id = 0x1111
|
|
||||||
p.method_id = 0x0222
|
|
||||||
p.event_id = 0x0333
|
|
||||||
p.sub_id = 1
|
|
||||||
|
|
||||||
assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
|
|
||||||
|
|
||||||
assert((struct.unpack("!B", bytes(p)[2:3])[0] & 0x80) == 0x80)
|
|
||||||
|
|
||||||
assert((struct.unpack("!H", bytes(p)[2:4])[0] & ~0x8000) == 0x0333)
|
|
||||||
|
|
||||||
assert(bytes(p) == b"\x11\x11\x83\x33")
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
= Dissect MessageId with event_id
|
|
||||||
|
|
||||||
p = SOMEIP(b'\x33\x33\x82\x22')
|
|
||||||
|
|
||||||
assert(p.msg_id.srv_id == 0x3333)
|
|
||||||
|
|
||||||
assert(p.msg_id.event_id == 0x0222)
|
|
||||||
|
|
||||||
assert(p.msg_id.sub_id == 1)
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
+ Test RequestId
|
|
||||||
|
|
||||||
= Request Id
|
|
||||||
|
|
||||||
p = SOMEIP().req_id
|
|
||||||
p.client_id = 0x1111
|
|
||||||
p.session_id = 0x2222
|
|
||||||
|
|
||||||
assert(struct.unpack("!H", bytes(p)[:2])[0] == 0x1111)
|
|
||||||
|
|
||||||
assert(struct.unpack("!H", bytes(p)[2:4])[0] == 0x2222)
|
|
||||||
|
|
||||||
assert(bytes(p) == b"\x11\x11\x22\x22")
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
= Dissect RequestId
|
|
||||||
|
|
||||||
method_id = b'\x22\x22\x03\x33'
|
|
||||||
pktLen = b'\x11\x11\x11\x11'
|
|
||||||
reqId = b'\x22\x22\x33\x33'
|
|
||||||
p = SOMEIP(method_id + pktLen + reqId)
|
|
||||||
|
|
||||||
assert(p.req_id.client_id == 0x2222)
|
|
||||||
|
|
||||||
assert(p.req_id.session_id == 0x3333)
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
+ Test SOMEIP
|
|
||||||
|
|
||||||
= Check SomeIp
|
|
||||||
|
|
||||||
p = SOMEIP()
|
|
||||||
pstr = binascii.hexlify(bytes(p))
|
|
||||||
binstr = binascii.hexlify(b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00")
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
p.payload = Raw(binascii.unhexlify("DEADBEEF"))
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00\xde\xad\xbe\xef"
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
p.payload = Raw('')
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
= Dissect SomeIP packet
|
|
||||||
|
|
||||||
p = SOMEIP(
|
|
||||||
b"\x11\x11\x81\x11\x00\x00\x00\x04\x33\x33\x44\x44\x02\x03\x04\x05")
|
|
||||||
|
|
||||||
assert(p.msg_id.srv_id == 0x1111)
|
|
||||||
|
|
||||||
assert(p.msg_id.event_id == 0x0111)
|
|
||||||
|
|
||||||
assert(p.req_id.client_id == 0x3333)
|
|
||||||
|
|
||||||
assert(p.req_id.session_id == 0x4444)
|
|
||||||
|
|
||||||
assert(p.proto_ver == 0x02)
|
|
||||||
|
|
||||||
assert(p.iface_ver == 0x03)
|
|
||||||
|
|
||||||
assert(p.msg_type == 0x04)
|
|
||||||
|
|
||||||
assert(p.retcode == 0x05)
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
+ Test SOMEIP_SubPackages
|
|
||||||
|
|
||||||
= Check MessageId subpackage
|
|
||||||
|
|
||||||
p = SOMEIP()
|
|
||||||
p.msg_id.srv_id = 0x1111
|
|
||||||
p.msg_id.method_id = 0x0222
|
|
||||||
p.msg_id.event_id = 0x0333
|
|
||||||
|
|
||||||
p.msg_id.sub_id = 0
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b"\x11\x11\x02\x22\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
p.msg_id.sub_id = 1
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b"\x11\x11\x83\x33\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00"
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
del(p)
|
|
||||||
|
|
||||||
= Check RequestId subpackage
|
|
||||||
|
|
||||||
p = SOMEIP()
|
|
||||||
p.req_id.client_id = 0x1111
|
|
||||||
p.req_id.session_id = 0x2222
|
|
||||||
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x08\x11\x11\x22\x22\x01\x01\x00\x00"
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
+ Test SOMEIP_TP
|
|
||||||
|
|
||||||
= Check TP
|
|
||||||
p = SOMEIP()
|
|
||||||
p.msg_type = 0x20
|
|
||||||
|
|
||||||
|
|
||||||
pstr = bytes(p)
|
|
||||||
print(pstr)
|
|
||||||
binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x00'
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
p.more_seg = 1
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x20\x00\x00\x00\x00\x01'
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
p.msg_type = 0x00
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b'\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x01\x01\x00\x00'
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
|
|
||||||
= Dissect TP
|
|
||||||
|
|
||||||
p = SOMEIP(b'\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x21\x00\x00\x00\x00\x01')
|
|
||||||
|
|
||||||
assert(p.msg_type == 0x21)
|
|
||||||
assert(p.more_seg == 1)
|
|
||||||
assert(p.len == 12)
|
|
||||||
|
|
||||||
p.msg_type = 0x00
|
|
||||||
|
|
||||||
pstr = bytes(p)
|
|
||||||
binstr = b"\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x01\x01\x00\x00"
|
|
||||||
assert(pstr == binstr)
|
|
||||||
|
|
||||||
+ Test SOMEIP-TP
|
|
||||||
|
|
||||||
= Build TP fragmented
|
|
||||||
|
|
||||||
p = SOMEIP()
|
|
||||||
p.msg_type = 0x20
|
|
||||||
p.add_payload(Raw("A"*1400))
|
|
||||||
|
|
||||||
f = p.fragment()
|
|
||||||
|
|
||||||
assert(f[0].len == 1404)
|
|
||||||
assert(f[1].len == 20)
|
|
||||||
assert(f[0].payload == Raw("A"*1392))
|
|
||||||
assert(f[1].payload == Raw("A"*8))
|
|
||||||
assert(f[0].more_seg == 1)
|
|
||||||
assert(f[1].more_seg == 0)
|
|
|
@ -1,350 +0,0 @@
|
||||||
#! /usr/bin/env python
|
|
||||||
|
|
||||||
# MIT License
|
|
||||||
|
|
||||||
# Copyright (c) 2018 Jose Amores
|
|
||||||
|
|
||||||
# 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 information
|
|
||||||
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = SOME/IP Service Discovery
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import collections
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from scapy.packet import Packet, Raw
|
|
||||||
from scapy.fields import ByteField, BitField, ShortField, \
|
|
||||||
X3BytesField, IntField, ByteEnumField, StrField, IPField, \
|
|
||||||
FieldLenField, PacketListField
|
|
||||||
from scapy.contrib.automotive.someip import SOMEIP
|
|
||||||
from scapy.layers.inet6 import IP6Field
|
|
||||||
from scapy.compat import orb
|
|
||||||
|
|
||||||
|
|
||||||
class _SDPacketBase(Packet):
|
|
||||||
""" base class to be used among all SD Packet definitions."""
|
|
||||||
# use this dictionary to set default values for desired fields (mostly on
|
|
||||||
# subclasses where not all fields are defined locally)
|
|
||||||
# - key : field_name, value : desired value
|
|
||||||
# - it will be used from 'init_fields' function, upon packet initialization
|
|
||||||
#
|
|
||||||
# example : _defaults =
|
|
||||||
# {'field_1_name':field_1_value,'field_2_name':field_2_value}
|
|
||||||
_defaults = {}
|
|
||||||
|
|
||||||
def _set_defaults(self):
|
|
||||||
for key in self._defaults:
|
|
||||||
try:
|
|
||||||
self.get_field(key)
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.setfieldval(key, self._defaults[key])
|
|
||||||
|
|
||||||
def init_fields(self):
|
|
||||||
super(_SDPacketBase, self).init_fields()
|
|
||||||
self._set_defaults()
|
|
||||||
|
|
||||||
|
|
||||||
# SD ENTRY
|
|
||||||
# - Service
|
|
||||||
# - EventGroup
|
|
||||||
class _SDEntry(_SDPacketBase):
|
|
||||||
TYPE_FMT = ">B"
|
|
||||||
TYPE_PAYLOAD_I = 0
|
|
||||||
TYPE_SRV_FINDSERVICE = 0x00
|
|
||||||
TYPE_SRV_OFFERSERVICE = 0x01
|
|
||||||
TYPE_SRV = (TYPE_SRV_FINDSERVICE, TYPE_SRV_OFFERSERVICE)
|
|
||||||
TYPE_EVTGRP_SUBSCRIBE = 0x06
|
|
||||||
TYPE_EVTGRP_SUBSCRIBE_ACK = 0x07
|
|
||||||
TYPE_EVTGRP = (TYPE_EVTGRP_SUBSCRIBE, TYPE_EVTGRP_SUBSCRIBE_ACK)
|
|
||||||
OVERALL_LEN = 16
|
|
||||||
|
|
||||||
fields_desc = [
|
|
||||||
ByteField("type", 0),
|
|
||||||
ByteField("index_1", 0),
|
|
||||||
ByteField("index_2", 0),
|
|
||||||
BitField("n_opt_1", 0, 4),
|
|
||||||
BitField("n_opt_2", 0, 4),
|
|
||||||
ShortField("srv_id", 0),
|
|
||||||
ShortField("inst_id", 0),
|
|
||||||
ByteField("major_ver", 0),
|
|
||||||
X3BytesField("ttl", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
def guess_payload_class(self, payload):
|
|
||||||
pl_type = orb(payload[_SDEntry.TYPE_PAYLOAD_I])
|
|
||||||
|
|
||||||
if (pl_type in _SDEntry.TYPE_SRV):
|
|
||||||
return (SDEntry_Service)
|
|
||||||
elif (pl_type in _SDEntry.TYPE_EVTGRP):
|
|
||||||
return (SDEntry_EventGroup)
|
|
||||||
|
|
||||||
|
|
||||||
class SDEntry_Service(_SDEntry):
|
|
||||||
_defaults = {"type": _SDEntry.TYPE_SRV_FINDSERVICE}
|
|
||||||
|
|
||||||
name = "Service Entry"
|
|
||||||
fields_desc = [
|
|
||||||
_SDEntry,
|
|
||||||
IntField("minor_ver", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDEntry_EventGroup(_SDEntry):
|
|
||||||
_defaults = {"type": _SDEntry.TYPE_EVTGRP_SUBSCRIBE}
|
|
||||||
|
|
||||||
name = "Eventgroup Entry"
|
|
||||||
fields_desc = [
|
|
||||||
_SDEntry,
|
|
||||||
BitField("res", 0, 12),
|
|
||||||
BitField("cnt", 0, 4),
|
|
||||||
ShortField("eventgroup_id", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# SD Option
|
|
||||||
# - Configuration
|
|
||||||
# - LoadBalancing
|
|
||||||
# - IPv4 EndPoint
|
|
||||||
# - IPv6 EndPoint
|
|
||||||
|
|
||||||
# - IPv4 MultiCast
|
|
||||||
# - IPv6 MultiCast
|
|
||||||
# - IPv4 EndPoint
|
|
||||||
# - IPv6 EndPoint
|
|
||||||
class _SDOption(_SDPacketBase):
|
|
||||||
CFG_TYPE = 0x01
|
|
||||||
CFG_OVERALL_LEN = 4
|
|
||||||
LOADBALANCE_TYPE = 0x02
|
|
||||||
LOADBALANCE_LEN = 0x05
|
|
||||||
LOADBALANCE_OVERALL_LEN = 8
|
|
||||||
IP4_ENDPOINT_TYPE = 0x04
|
|
||||||
IP4_ENDPOINT_LEN = 0x0009
|
|
||||||
IP4_MCAST_TYPE = 0x14
|
|
||||||
IP4_MCAST_LEN = 0x0009
|
|
||||||
IP4_SDENDPOINT_TYPE = 0x24
|
|
||||||
IP4_SDENDPOINT_LEN = 0x0009
|
|
||||||
IP4_OVERALL_LEN = 12
|
|
||||||
IP6_ENDPOINT_TYPE = 0x06
|
|
||||||
IP6_ENDPOINT_LEN = 0x0015
|
|
||||||
IP6_MCAST_TYPE = 0x16
|
|
||||||
IP6_MCAST_LEN = 0x0015
|
|
||||||
IP6_SDENDPOINT_TYPE = 0x26
|
|
||||||
IP6_SDENDPOINT_LEN = 0x0015
|
|
||||||
IP6_OVERALL_LEN = 24
|
|
||||||
|
|
||||||
def guess_payload_class(self, payload):
|
|
||||||
pl_type = orb(payload[2])
|
|
||||||
|
|
||||||
return {
|
|
||||||
_SDOption.CFG_TYPE: SDOption_Config,
|
|
||||||
self.LOADBALANCE_TYPE: SDOption_LoadBalance,
|
|
||||||
self.IP4_ENDPOINT_TYPE: SDOption_IP4_EndPoint,
|
|
||||||
self.IP4_MCAST_TYPE: SDOption_IP4_Multicast,
|
|
||||||
self.IP4_SDENDPOINT_TYPE: SDOption_IP4_SD_EndPoint,
|
|
||||||
self.IP6_ENDPOINT_TYPE: SDOption_IP6_EndPoint,
|
|
||||||
self.IP6_MCAST_TYPE: SDOption_IP6_Multicast,
|
|
||||||
self.IP6_SDENDPOINT_TYPE: SDOption_IP6_SD_EndPoint
|
|
||||||
}.get(pl_type, Raw)
|
|
||||||
|
|
||||||
|
|
||||||
class _SDOption_Header(_SDOption):
|
|
||||||
fields_desc = [
|
|
||||||
ShortField("len", None),
|
|
||||||
ByteField("type", 0),
|
|
||||||
ByteField("res_hdr", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _SDOption_Tail(_SDOption):
|
|
||||||
fields_desc = [
|
|
||||||
ByteField("res_tail", 0),
|
|
||||||
ByteEnumField("l4_proto", 0x06, {0x06: "TCP", 0x11: "UDP"}),
|
|
||||||
ShortField("port", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _SDOption_IP4(_SDOption):
|
|
||||||
fields_desc = [
|
|
||||||
_SDOption_Header,
|
|
||||||
IPField("addr", "0.0.0.0"),
|
|
||||||
_SDOption_Tail
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class _SDOption_IP6(_SDOption):
|
|
||||||
fields_desc = [
|
|
||||||
_SDOption_Header,
|
|
||||||
IP6Field("addr", "2001:cdba:0000:0000:0000:0000:3257:9652"),
|
|
||||||
_SDOption_Tail
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_Config(_SDOption):
|
|
||||||
LEN_OFFSET = 0x01
|
|
||||||
|
|
||||||
name = "Config Option"
|
|
||||||
_defaults = {'type': _SDOption.CFG_TYPE}
|
|
||||||
fields_desc = [
|
|
||||||
_SDOption_Header,
|
|
||||||
StrField("cfg_str", "")
|
|
||||||
]
|
|
||||||
|
|
||||||
def post_build(self, pkt, pay):
|
|
||||||
length = self.len
|
|
||||||
if (length is None):
|
|
||||||
length = len(self.cfg_str) + self.LEN_OFFSET
|
|
||||||
pkt = struct.pack("!H", length) + pkt[2:]
|
|
||||||
return (pkt + pay)
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_LoadBalance(_SDOption):
|
|
||||||
name = "LoadBalance Option"
|
|
||||||
_defaults = {'type': _SDOption.LOADBALANCE_TYPE,
|
|
||||||
'len': _SDOption.LOADBALANCE_LEN}
|
|
||||||
fields_desc = [
|
|
||||||
_SDOption_Header,
|
|
||||||
ShortField("priority", 0),
|
|
||||||
ShortField("weight", 0)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP4_EndPoint(_SDOption_IP4):
|
|
||||||
name = "IP4 EndPoint Option"
|
|
||||||
_defaults = {'type': _SDOption.IP4_ENDPOINT_TYPE,
|
|
||||||
'len': _SDOption.IP4_ENDPOINT_LEN}
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP4_Multicast(_SDOption_IP4):
|
|
||||||
name = "IP4 Multicast Option"
|
|
||||||
_defaults = {'type': _SDOption.IP4_MCAST_TYPE,
|
|
||||||
'len': _SDOption.IP4_MCAST_LEN}
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP4_SD_EndPoint(_SDOption_IP4):
|
|
||||||
name = "IP4 SDEndPoint Option"
|
|
||||||
_defaults = {'type': _SDOption.IP4_SDENDPOINT_TYPE,
|
|
||||||
'len': _SDOption.IP4_SDENDPOINT_LEN}
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP6_EndPoint(_SDOption_IP6):
|
|
||||||
name = "IP6 EndPoint Option"
|
|
||||||
_defaults = {'type': _SDOption.IP6_ENDPOINT_TYPE,
|
|
||||||
'len': _SDOption.IP6_ENDPOINT_LEN}
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP6_Multicast(_SDOption_IP6):
|
|
||||||
name = "IP6 Multicast Option"
|
|
||||||
_defaults = {'type': _SDOption.IP6_MCAST_TYPE,
|
|
||||||
'len': _SDOption.IP6_MCAST_LEN}
|
|
||||||
|
|
||||||
|
|
||||||
class SDOption_IP6_SD_EndPoint(_SDOption_IP6):
|
|
||||||
name = "IP6 SDEndPoint Option"
|
|
||||||
_defaults = {'type': _SDOption.IP6_SDENDPOINT_TYPE,
|
|
||||||
'len': _SDOption.IP6_SDENDPOINT_LEN}
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# SD PACKAGE DEFINITION
|
|
||||||
##
|
|
||||||
class SD(_SDPacketBase):
|
|
||||||
"""
|
|
||||||
SD Packet
|
|
||||||
|
|
||||||
NOTE : when adding 'entries' or 'options', do not use list.append()
|
|
||||||
method but create a new list
|
|
||||||
e.g. : p = SD()
|
|
||||||
p.option_array = [SDOption_Config(),SDOption_IP6_EndPoint()]
|
|
||||||
"""
|
|
||||||
SOMEIP_MSGID_SRVID = 0xffff
|
|
||||||
SOMEIP_MSGID_SUBID = 0x1
|
|
||||||
SOMEIP_MSGID_EVENTID = 0x100
|
|
||||||
SOMEIP_PROTO_VER = 0x01
|
|
||||||
SOMEIP_IFACE_VER = 0x01
|
|
||||||
SOMEIP_MSG_TYPE = SOMEIP.TYPE_NOTIFICATION
|
|
||||||
|
|
||||||
name = "SD"
|
|
||||||
_sdFlag = collections.namedtuple('Flag', 'mask offset')
|
|
||||||
FLAGSDEF = {
|
|
||||||
"REBOOT": _sdFlag(mask=0x80, offset=7),
|
|
||||||
"UNICAST": _sdFlag(mask=0x40, offset=6)
|
|
||||||
}
|
|
||||||
|
|
||||||
name = "SD"
|
|
||||||
fields_desc = [
|
|
||||||
ByteField("flags", 0),
|
|
||||||
X3BytesField("res", 0),
|
|
||||||
FieldLenField("len_entry_array", None,
|
|
||||||
length_of="entry_array", fmt="!I"),
|
|
||||||
PacketListField("entry_array", None, cls=_SDEntry,
|
|
||||||
length_from=lambda pkt: pkt.len_entry_array),
|
|
||||||
FieldLenField("len_option_array", None,
|
|
||||||
length_of="option_array", fmt="!I"),
|
|
||||||
PacketListField("option_array", None, cls=_SDOption,
|
|
||||||
length_from=lambda pkt: pkt.len_option_array)
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_flag(self, name):
|
|
||||||
name = name.upper()
|
|
||||||
if (name in self.FLAGSDEF):
|
|
||||||
return ((self.flags & self.FLAGSDEF[name].mask) >>
|
|
||||||
self.FLAGSDEF[name].offset)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def set_flag(self, name, value):
|
|
||||||
name = name.upper()
|
|
||||||
if (name in self.FLAGSDEF):
|
|
||||||
self.flags = (self.flags &
|
|
||||||
(ctypes.c_ubyte(~self.FLAGSDEF[name].mask).value)) \
|
|
||||||
| ((value & 0x01) << self.FLAGSDEF[name].offset)
|
|
||||||
|
|
||||||
def set_entryArray(self, entry_list):
|
|
||||||
if (isinstance(entry_list, list)):
|
|
||||||
self.entry_array = entry_list
|
|
||||||
else:
|
|
||||||
self.entry_array = [entry_list]
|
|
||||||
|
|
||||||
def set_optionArray(self, option_list):
|
|
||||||
if (isinstance(option_list, list)):
|
|
||||||
self.option_array = option_list
|
|
||||||
else:
|
|
||||||
self.option_array = [option_list]
|
|
||||||
|
|
||||||
def get_someip(self, stacked=False):
|
|
||||||
p = SOMEIP()
|
|
||||||
p.msg_id.srv_id = SD.SOMEIP_MSGID_SRVID
|
|
||||||
p.msg_id.sub_id = SD.SOMEIP_MSGID_SUBID
|
|
||||||
p.msg_id.event_id = SD.SOMEIP_MSGID_EVENTID
|
|
||||||
p.proto_ver = SD.SOMEIP_PROTO_VER
|
|
||||||
p.iface_ver = SD.SOMEIP_IFACE_VER
|
|
||||||
p.msg_type = SD.SOMEIP_MSG_TYPE
|
|
||||||
|
|
||||||
if (stacked):
|
|
||||||
return (p / self)
|
|
||||||
else:
|
|
||||||
return (p)
|
|
|
@ -1,381 +0,0 @@
|
||||||
# MIT License
|
|
||||||
|
|
||||||
# Copyright (c) 2018 Jose Amores
|
|
||||||
|
|
||||||
# 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 information
|
|
||||||
# Copyright (C) Sebastian Baar <sebastian.baar@gmx.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
#########
|
|
||||||
#########
|
|
||||||
|
|
||||||
+ SD Entry Service
|
|
||||||
|
|
||||||
=load module
|
|
||||||
|
|
||||||
load_contrib("automotive.someip_sd")
|
|
||||||
|
|
||||||
= Check packet length
|
|
||||||
|
|
||||||
p = SDEntry_Service()
|
|
||||||
assert(len(bytes(p)) == SDEntry_Service.OVERALL_LEN)
|
|
||||||
|
|
||||||
= Check fields setting
|
|
||||||
|
|
||||||
p.type = SDEntry_Service.TYPE_SRV_OFFERSERVICE
|
|
||||||
p.index_1 = 0x11
|
|
||||||
p.index_2 = 0x22
|
|
||||||
p.srv_id = 0x3333
|
|
||||||
p.inst_id = 0x4444
|
|
||||||
p.major_ver = 0x55
|
|
||||||
p.ttl = 0x666666
|
|
||||||
p.minor_ver = 0xdeadbeef
|
|
||||||
p_str = bytes(p)
|
|
||||||
bin_str = b"\x01\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\xde\xad\xbe\xef"
|
|
||||||
|
|
||||||
assert(p_str == bin_str)
|
|
||||||
|
|
||||||
= Check fields setting2
|
|
||||||
|
|
||||||
p = SDEntry_Service()
|
|
||||||
p.n_opt_1 = 0xf1
|
|
||||||
p.n_opt_2 = 0xf2
|
|
||||||
p_str = bytes(p)
|
|
||||||
bin_str = b"\x00\x00\x00\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
|
|
||||||
|
|
||||||
assert(p_str == bin_str)
|
|
||||||
assert(len(p_str) == SDEntry_Service.OVERALL_LEN)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
p_entry_srv = SDEntry_Service()
|
|
||||||
|
|
||||||
assert(SDEntry_Service().guess_payload_class(bytes(p_entry_srv)) ==
|
|
||||||
SDEntry_Service)
|
|
||||||
|
|
||||||
= Check SDEntry_Service
|
|
||||||
|
|
||||||
p = SDEntry_Service(
|
|
||||||
b"\x01\x22\x33\x00\x44\x44\x55\x55\x66\x77\x77\x77\xde\xad\xbe\xef")
|
|
||||||
|
|
||||||
assert(p.type == SDEntry_Service.TYPE_SRV_OFFERSERVICE)
|
|
||||||
assert(p.index_1 == 0x22)
|
|
||||||
assert(p.index_2 == 0x33)
|
|
||||||
assert(p.srv_id == 0x4444)
|
|
||||||
assert(p.inst_id == 0x5555)
|
|
||||||
assert(p.major_ver == 0x66)
|
|
||||||
assert(p.ttl == 0x777777)
|
|
||||||
assert(p.minor_ver == 0xdeadbeef)
|
|
||||||
|
|
||||||
+ SD Entry Eventgroup
|
|
||||||
|
|
||||||
= Check packet length
|
|
||||||
|
|
||||||
p = SDEntry_EventGroup()
|
|
||||||
|
|
||||||
assert(len(bytes(p)) == SDEntry_EventGroup.OVERALL_LEN)
|
|
||||||
|
|
||||||
= Check fields setting
|
|
||||||
|
|
||||||
p.index_1 = 0x11
|
|
||||||
p.index_2 = 0x22
|
|
||||||
p.srv_id = 0x3333
|
|
||||||
p.inst_id = 0x4444
|
|
||||||
p.major_ver = 0x55
|
|
||||||
p.ttl = 0x666666
|
|
||||||
p.cnt = 0x7
|
|
||||||
p.eventgroup_id = 0x8888
|
|
||||||
p_str = bytes(p)
|
|
||||||
bin_str = b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88"
|
|
||||||
|
|
||||||
assert(p_str == bin_str)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
p_entry_evtgrp = SDEntry_EventGroup()
|
|
||||||
|
|
||||||
assert(SDEntry_EventGroup().guess_payload_class(
|
|
||||||
bytes(p_entry_evtgrp)) == SDEntry_EventGroup)
|
|
||||||
|
|
||||||
+ SD Option Config
|
|
||||||
|
|
||||||
= Check pkg type
|
|
||||||
|
|
||||||
p = SDOption_Config()
|
|
||||||
|
|
||||||
assert(p.type == 0x01)
|
|
||||||
|
|
||||||
= Check length without payload
|
|
||||||
|
|
||||||
assert(len(bytes(p)) == 4)
|
|
||||||
|
|
||||||
= Check add payload and check length
|
|
||||||
import binascii
|
|
||||||
p.cfg_str = binascii.hexlify(b"5abc=x7def=1230")
|
|
||||||
|
|
||||||
assert(bytes(p) == b"\x00\x1f\x01\x00" +
|
|
||||||
binascii.hexlify(b"5abc=x7def=1230"))
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_Config().guess_payload_class(bytes(p)) == SDOption_Config)
|
|
||||||
|
|
||||||
= Check SDEntry_EventGroup
|
|
||||||
|
|
||||||
p = SDEntry_EventGroup(
|
|
||||||
b"\x06\x11\x22\x00\x33\x33\x44\x44\x55\x66\x66\x66\x00\x07\x88\x88")
|
|
||||||
|
|
||||||
assert(p.index_1 == 0x11)
|
|
||||||
assert(p.index_2 == 0x22)
|
|
||||||
assert(p.srv_id == 0x3333)
|
|
||||||
assert(p.inst_id == 0x4444)
|
|
||||||
assert(p.major_ver == 0x55)
|
|
||||||
assert(p.ttl == 0x666666)
|
|
||||||
assert(p.cnt == 0x7)
|
|
||||||
assert(p.eventgroup_id == 0x8888)
|
|
||||||
|
|
||||||
+ SD Option Load Balance
|
|
||||||
|
|
||||||
= Check pkg type & lengths(static and overall)
|
|
||||||
|
|
||||||
p = SDOption_LoadBalance()
|
|
||||||
|
|
||||||
assert(p.type == 0x02)
|
|
||||||
assert(p.len == 0x05)
|
|
||||||
assert(len(bytes(p)) == 8)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_LoadBalance().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_LoadBalance)
|
|
||||||
|
|
||||||
= Check SDOption_LoadBalance
|
|
||||||
|
|
||||||
p = SDOption_LoadBalance(b'\x00\x05\x02\x01\x00\x02\x00\x03')
|
|
||||||
|
|
||||||
assert(p.type == 0x02)
|
|
||||||
assert(p.len == 0x05)
|
|
||||||
assert(p.res_hdr == 0x01)
|
|
||||||
assert(p.priority == 0x02)
|
|
||||||
assert(p.weight == 0x03)
|
|
||||||
|
|
||||||
+SD Option IP4 Endpoint
|
|
||||||
|
|
||||||
= Check pkg type & length
|
|
||||||
|
|
||||||
p = SDOption_IP4_EndPoint()
|
|
||||||
|
|
||||||
assert(p.type == 0x04)
|
|
||||||
assert(p.len == 0x0009)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_IP4_EndPoint().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_IP4_EndPoint)
|
|
||||||
|
|
||||||
= Check SDOption_IP4_EndPoint
|
|
||||||
|
|
||||||
p = SDOption_IP4_EndPoint(b'\x00\x09\x04')
|
|
||||||
|
|
||||||
assert(p.type == 0x04)
|
|
||||||
assert(p.len == 0x0009)
|
|
||||||
|
|
||||||
+SD Option IP4 Multicast
|
|
||||||
|
|
||||||
= Check pkg type & length
|
|
||||||
|
|
||||||
p = SDOption_IP4_Multicast()
|
|
||||||
|
|
||||||
assert(p.type == 0x14)
|
|
||||||
assert(p.len == 0x0009)
|
|
||||||
|
|
||||||
= Payload guess
|
|
||||||
|
|
||||||
assert(SDOption_IP4_Multicast().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_IP4_Multicast)
|
|
||||||
|
|
||||||
= Check SDOption_IP4_Multicast
|
|
||||||
|
|
||||||
p = SDOption_IP4_Multicast(b'\x00\x09\x14')
|
|
||||||
|
|
||||||
assert(p.type == 0x14)
|
|
||||||
assert(p.len == 0x0009)
|
|
||||||
|
|
||||||
+SD OPTION IP4 SD EndPoint
|
|
||||||
|
|
||||||
= Check pkg type & length
|
|
||||||
|
|
||||||
p = SDOption_IP4_SD_EndPoint()
|
|
||||||
|
|
||||||
assert(p.type == 0x24)
|
|
||||||
assert(p.len == 0x0009)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_IP4_SD_EndPoint().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_IP4_SD_EndPoint)
|
|
||||||
|
|
||||||
= Check SDOption_IP4_SD_EndPoint
|
|
||||||
|
|
||||||
p = SDOption_IP4_SD_EndPoint(b'\x00\x09\x24')
|
|
||||||
|
|
||||||
assert(p.type == 0x24)
|
|
||||||
assert(p.len == 0x0009)
|
|
||||||
|
|
||||||
+SD Option IP6 End Point
|
|
||||||
|
|
||||||
= Check pkg type & length
|
|
||||||
|
|
||||||
p = SDOption_IP6_EndPoint()
|
|
||||||
|
|
||||||
assert(p.type == 0x06)
|
|
||||||
assert(p.len == 0x0015)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_IP6_EndPoint().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_IP6_EndPoint)
|
|
||||||
|
|
||||||
= Check SDOption_IP6_EndPoint
|
|
||||||
|
|
||||||
p = SDOption_IP6_EndPoint(b'\x00\x15\x06')
|
|
||||||
|
|
||||||
assert(p.type == 0x06)
|
|
||||||
assert(p.len == 0x0015)
|
|
||||||
|
|
||||||
+SD Option IP6 Multicast
|
|
||||||
|
|
||||||
= Check pkg type & length
|
|
||||||
|
|
||||||
p = SDOption_IP6_Multicast()
|
|
||||||
|
|
||||||
assert(p.type == 0x16)
|
|
||||||
assert(p.len == 0x0015)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_IP6_Multicast().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_IP6_Multicast)
|
|
||||||
|
|
||||||
= Check SDOption_IP6_Multicast
|
|
||||||
p = SDOption_IP6_Multicast(b'\x00\x15\x16')
|
|
||||||
|
|
||||||
assert(p.type == 0x16)
|
|
||||||
assert(p.len == 0x0015)
|
|
||||||
|
|
||||||
+SD OPTION IP6 SD EndPoint
|
|
||||||
|
|
||||||
= Check pkg type & length
|
|
||||||
|
|
||||||
p = SDOption_IP6_SD_EndPoint()
|
|
||||||
|
|
||||||
assert(p.type == 0x26)
|
|
||||||
assert(p.len == 0x015)
|
|
||||||
|
|
||||||
= Check payload guess
|
|
||||||
|
|
||||||
assert(SDOption_IP6_SD_EndPoint().guess_payload_class(
|
|
||||||
bytes(p)) == SDOption_IP6_SD_EndPoint)
|
|
||||||
|
|
||||||
= Check SDOption_IP6_SD_EndPoint
|
|
||||||
|
|
||||||
p = SDOption_IP6_SD_EndPoint(b'\x00\x15\x26')
|
|
||||||
|
|
||||||
assert(p.type == 0x26)
|
|
||||||
assert(p.len == 0x0015)
|
|
||||||
|
|
||||||
+ SD Flags
|
|
||||||
|
|
||||||
= Check the flags
|
|
||||||
|
|
||||||
p = SD()
|
|
||||||
|
|
||||||
p.set_flag("REBOOT", 1)
|
|
||||||
|
|
||||||
assert(p.flags == 0x80)
|
|
||||||
|
|
||||||
p.set_flag("REBOOT", 0)
|
|
||||||
|
|
||||||
assert(p.flags == 0x00)
|
|
||||||
|
|
||||||
p.set_flag("UNICAST", 1)
|
|
||||||
|
|
||||||
assert(p.flags == 0x40)
|
|
||||||
|
|
||||||
p.set_flag("UNICAST", 0)
|
|
||||||
|
|
||||||
assert(p.flags == 0x00)
|
|
||||||
|
|
||||||
p.set_flag("REBOOT", 1)
|
|
||||||
p.set_flag("UNICAST", 1)
|
|
||||||
|
|
||||||
assert(p.flags == 0xc0)
|
|
||||||
|
|
||||||
+SD Get Someip Packet
|
|
||||||
|
|
||||||
= Check someip packet
|
|
||||||
|
|
||||||
p_sd = SD()
|
|
||||||
sd_len = bytes(p_sd)
|
|
||||||
|
|
||||||
p_someip = p_sd.get_someip()
|
|
||||||
|
|
||||||
assert(len(bytes(p_someip)) == SOMEIP._OVERALL_LEN_NOPAYLOAD)
|
|
||||||
|
|
||||||
p = p_sd.get_someip(stacked=True)
|
|
||||||
|
|
||||||
assert(len(bytes(p)) == SOMEIP._OVERALL_LEN_NOPAYLOAD + 12)
|
|
||||||
|
|
||||||
+ SD
|
|
||||||
|
|
||||||
= Check length of package without entries nor options
|
|
||||||
|
|
||||||
p = SD()
|
|
||||||
|
|
||||||
assert(len(bytes(p)) == 12)
|
|
||||||
|
|
||||||
= Check entries to array and size check
|
|
||||||
|
|
||||||
p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
|
|
||||||
|
|
||||||
assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
|
|
||||||
|
|
||||||
p.set_entryArray([])
|
|
||||||
|
|
||||||
assert(struct.unpack("!L", bytes(p)[4:8])[0] == 0)
|
|
||||||
|
|
||||||
= Check Options to array and size check
|
|
||||||
|
|
||||||
p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
|
|
||||||
|
|
||||||
assert(struct.unpack("!L", bytes(p)[8:12])[0] == 24)
|
|
||||||
|
|
||||||
p.set_optionArray([])
|
|
||||||
|
|
||||||
assert(struct.unpack("!L", bytes(p)[8:12])[0] == 0)
|
|
||||||
|
|
||||||
= Check Entries & Options to array and size check
|
|
||||||
|
|
||||||
p.set_entryArray([SDEntry_Service(), SDEntry_EventGroup()])
|
|
||||||
p.set_optionArray([SDOption_IP4_EndPoint(), SDOption_IP4_EndPoint()])
|
|
||||||
|
|
||||||
assert(struct.unpack("!L", bytes(p)[4:8])[0] == 32)
|
|
||||||
assert(struct.unpack("!L", bytes(p)[40:44])[0] == 24)
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,768 +0,0 @@
|
||||||
% Regression tests for the UDS layer
|
|
||||||
|
|
||||||
# More information at http://www.secdev.org/projects/UTscapy/
|
|
||||||
|
|
||||||
|
|
||||||
############
|
|
||||||
############
|
|
||||||
|
|
||||||
+ Basic operations
|
|
||||||
|
|
||||||
= Load module
|
|
||||||
|
|
||||||
load_contrib("automotive.uds")
|
|
||||||
|
|
||||||
= Check if positive response answers
|
|
||||||
|
|
||||||
dsc = UDS(b'\x10')
|
|
||||||
dscpr = UDS(b'\x50')
|
|
||||||
assert dscpr.answers(dsc)
|
|
||||||
|
|
||||||
= Check hashret
|
|
||||||
dsc.hashret() == dscpr.hashret()
|
|
||||||
|
|
||||||
= Check if negative response answers
|
|
||||||
|
|
||||||
dsc = UDS(b'\x10')
|
|
||||||
neg = UDS(b'\x7f\x10')
|
|
||||||
assert neg.answers(dsc)
|
|
||||||
|
|
||||||
= CHECK hashret NEG
|
|
||||||
dsc.hashret() == neg.hashret()
|
|
||||||
|
|
||||||
= Check if negative response answers not
|
|
||||||
|
|
||||||
dsc = UDS(b'\x10')
|
|
||||||
neg = UDS(b'\x7f\x11')
|
|
||||||
assert not neg.answers(dsc)
|
|
||||||
|
|
||||||
= Check if positive response answers not
|
|
||||||
|
|
||||||
dsc = UDS(b'\x10')
|
|
||||||
somePacket = UDS(b'\x49')
|
|
||||||
assert not somePacket.answers(dsc)
|
|
||||||
|
|
||||||
= Check UDS_DSC
|
|
||||||
|
|
||||||
dsc = UDS(b'\x10\x01')
|
|
||||||
assert dsc.service == 0x10
|
|
||||||
assert dsc.diagnosticSessionType == 0x01
|
|
||||||
|
|
||||||
= Check UDS_DSC
|
|
||||||
|
|
||||||
dsc = UDS()/UDS_DSC(b'\x01')
|
|
||||||
assert dsc.service == 0x10
|
|
||||||
assert dsc.diagnosticSessionType == 0x01
|
|
||||||
|
|
||||||
= Check UDS_DSCPR
|
|
||||||
|
|
||||||
dscpr = UDS(b'\x50\x02beef')
|
|
||||||
assert dscpr.service == 0x50
|
|
||||||
assert dscpr.diagnosticSessionType == 0x02
|
|
||||||
|
|
||||||
= Check UDS_DSCPR
|
|
||||||
|
|
||||||
dscpr = UDS()/UDS_DSCPR(b'\x02beef')
|
|
||||||
assert dscpr.service == 0x50
|
|
||||||
assert dscpr.diagnosticSessionType == 0x02
|
|
||||||
assert dscpr.sessionParameterRecord == b"beef"
|
|
||||||
|
|
||||||
= Check UDS_ER
|
|
||||||
|
|
||||||
er = UDS(b'\x11\x01')
|
|
||||||
assert er.service == 0x11
|
|
||||||
assert er.resetType == 0x01
|
|
||||||
|
|
||||||
= Check UDS_ER
|
|
||||||
|
|
||||||
er = UDS()/UDS_ER(resetType="hardReset")
|
|
||||||
assert er.service == 0x11
|
|
||||||
assert er.resetType == 0x01
|
|
||||||
|
|
||||||
= Check UDS_ERPR
|
|
||||||
|
|
||||||
erpr = UDS(b'\x51\x01')
|
|
||||||
assert erpr.service == 0x51
|
|
||||||
assert erpr.resetType == 0x01
|
|
||||||
|
|
||||||
= Check UDS_ERPR
|
|
||||||
|
|
||||||
erpr = UDS(b'\x51\x04\x10')
|
|
||||||
assert erpr.service == 0x51
|
|
||||||
assert erpr.resetType == 0x04
|
|
||||||
assert erpr.powerDownTime == 0x10
|
|
||||||
|
|
||||||
= Check UDS_SA
|
|
||||||
|
|
||||||
sa = UDS(b'\x27\x00c0ffee')
|
|
||||||
assert sa.service == 0x27
|
|
||||||
assert sa.securityAccessType == 0x0
|
|
||||||
assert sa.securityKey == b'c0ffee'
|
|
||||||
|
|
||||||
= Check UDS_SA
|
|
||||||
|
|
||||||
sa = UDS(b'\x27\x01c0ffee')
|
|
||||||
assert sa.service == 0x27
|
|
||||||
assert sa.securityAccessType == 0x1
|
|
||||||
assert sa.securityAccessDataRecord == b'c0ffee'
|
|
||||||
|
|
||||||
= Check UDS_SAPR
|
|
||||||
|
|
||||||
sapr = UDS(b'\x67\x01c0ffee')
|
|
||||||
assert sapr.service == 0x67
|
|
||||||
assert sapr.securityAccessType == 0x1
|
|
||||||
assert sapr.securitySeed == b'c0ffee'
|
|
||||||
|
|
||||||
= Check UDS_SAPR
|
|
||||||
|
|
||||||
sapr = UDS(b'\x67\x00')
|
|
||||||
assert sapr.service == 0x67
|
|
||||||
assert sapr.securityAccessType == 0x0
|
|
||||||
|
|
||||||
= Check UDS_CC
|
|
||||||
|
|
||||||
cc = UDS(b'\x28\x01\xff')
|
|
||||||
assert cc.service == 0x28
|
|
||||||
assert cc.controlType == 0x1
|
|
||||||
assert cc.communicationType0 == 0x3
|
|
||||||
assert cc.communicationType1 == 0x3
|
|
||||||
assert cc.communicationType2 == 0xf
|
|
||||||
|
|
||||||
= Check UDS_CCPR
|
|
||||||
|
|
||||||
ccpr = UDS(b'\x68\x01')
|
|
||||||
assert ccpr.service == 0x68
|
|
||||||
assert ccpr.controlType == 0x1
|
|
||||||
|
|
||||||
= Check UDS_TP
|
|
||||||
|
|
||||||
tp = UDS(b'\x3E\x01')
|
|
||||||
assert tp.service == 0x3e
|
|
||||||
assert tp.subFunction == 0x1
|
|
||||||
|
|
||||||
= Check UDS_TPPR
|
|
||||||
|
|
||||||
tppr = UDS(b'\x7E\x01')
|
|
||||||
assert tppr.service == 0x7e
|
|
||||||
assert tppr.zeroSubFunction == 0x1
|
|
||||||
|
|
||||||
= Check UDS_ATP
|
|
||||||
|
|
||||||
atp = UDS(b'\x83\x01')
|
|
||||||
assert atp.service == 0x83
|
|
||||||
assert atp.timingParameterAccessType == 0x1
|
|
||||||
|
|
||||||
= Check UDS_ATP
|
|
||||||
|
|
||||||
atp = UDS(b'\x83\x04coffee')
|
|
||||||
assert atp.service == 0x83
|
|
||||||
assert atp.timingParameterAccessType == 0x4
|
|
||||||
assert atp.timingParameterRequestRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_ATPPR
|
|
||||||
|
|
||||||
atppr = UDS(b'\xc3\x01')
|
|
||||||
assert atppr.service == 0xc3
|
|
||||||
assert atppr.timingParameterAccessType == 0x1
|
|
||||||
|
|
||||||
= Check UDS_ATPPR
|
|
||||||
|
|
||||||
atppr = UDS(b'\xc3\x03coffee')
|
|
||||||
assert atppr.service == 0xc3
|
|
||||||
assert atppr.timingParameterAccessType == 0x3
|
|
||||||
assert atppr.timingParameterResponseRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_SDT
|
|
||||||
|
|
||||||
sdt = UDS(b'\x84coffee')
|
|
||||||
assert sdt.service == 0x84
|
|
||||||
assert sdt.securityDataRequestRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_SDTPR
|
|
||||||
|
|
||||||
sdtpr = UDS(b'\xC4coffee')
|
|
||||||
assert sdtpr.service == 0xC4
|
|
||||||
assert sdtpr.securityDataResponseRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_CDTCS
|
|
||||||
|
|
||||||
cdtcs = UDS(b'\x85\x00coffee')
|
|
||||||
assert cdtcs.service == 0x85
|
|
||||||
assert cdtcs.DTCSettingType == 0
|
|
||||||
assert cdtcs.DTCSettingControlOptionRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_CDTCSPR
|
|
||||||
|
|
||||||
cdtcspr = UDS(b'\xC5\x00')
|
|
||||||
assert cdtcspr.service == 0xC5
|
|
||||||
assert cdtcspr.DTCSettingType == 0
|
|
||||||
|
|
||||||
= Check UDS_ROE
|
|
||||||
|
|
||||||
roe = UDS(b'\x86\x00\x10coffee')
|
|
||||||
assert roe.service == 0x86
|
|
||||||
assert roe.eventType == 0
|
|
||||||
assert roe.eventWindowTime == 16
|
|
||||||
assert roe.eventTypeRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_ROEPR
|
|
||||||
|
|
||||||
roepr = UDS(b'\xC6\x00\x01\x10coffee')
|
|
||||||
assert roepr.service == 0xC6
|
|
||||||
assert roepr.eventType == 0
|
|
||||||
assert roepr.numberOfIdentifiedEvents == 1
|
|
||||||
assert roepr.eventWindowTime == 16
|
|
||||||
assert roepr.eventTypeRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_LC
|
|
||||||
|
|
||||||
lc = UDS(b'\x87\x01\x02')
|
|
||||||
assert lc.service == 0x87
|
|
||||||
assert lc.linkControlType == 0x01
|
|
||||||
assert lc.baudrateIdentifier == 0x02
|
|
||||||
|
|
||||||
= Check UDS_LC
|
|
||||||
|
|
||||||
lc = UDS(b'\x87\x02\x02\x03\x04')
|
|
||||||
assert lc.service == 0x87
|
|
||||||
assert lc.linkControlType == 0x02
|
|
||||||
assert lc.baudrateHighByte == 0x02
|
|
||||||
assert lc.baudrateMiddleByte == 0x03
|
|
||||||
assert lc.baudrateLowByte == 0x04
|
|
||||||
|
|
||||||
= Check UDS_LCPR
|
|
||||||
|
|
||||||
lcpr = UDS(b'\xC7\x01')
|
|
||||||
assert lcpr.service == 0xC7
|
|
||||||
assert lcpr.linkControlType == 0x01
|
|
||||||
|
|
||||||
= Check UDS_RDBI
|
|
||||||
|
|
||||||
rdbi = UDS(b'\x22\x01\x02')
|
|
||||||
assert rdbi.service == 0x22
|
|
||||||
assert rdbi.identifiers[0] == 0x0102
|
|
||||||
|
|
||||||
= Build UDS_RDBI
|
|
||||||
|
|
||||||
rdbi = UDS()/UDS_RDBI(identifiers=[0x102])
|
|
||||||
assert rdbi.service == 0x22
|
|
||||||
assert rdbi.identifiers[0] == 0x0102
|
|
||||||
assert bytes(rdbi) == b'\x22\x01\x02'
|
|
||||||
|
|
||||||
= Check UDS_RDBI2
|
|
||||||
|
|
||||||
rdbi = UDS(b'\x22\x01\x02\x03\x04')
|
|
||||||
assert rdbi.service == 0x22
|
|
||||||
assert rdbi.identifiers[0] == 0x0102
|
|
||||||
assert rdbi.identifiers[1] == 0x0304
|
|
||||||
assert raw(rdbi) == b'\x22\x01\x02\x03\x04'
|
|
||||||
|
|
||||||
= Build UDS_RDBI2
|
|
||||||
|
|
||||||
rdbi = UDS()/UDS_RDBI(identifiers=[0x102, 0x304])
|
|
||||||
assert rdbi.service == 0x22
|
|
||||||
assert rdbi.identifiers[0] == 0x0102
|
|
||||||
assert rdbi.identifiers[1] == 0x0304
|
|
||||||
assert raw(rdbi) == b'\x22\x01\x02\x03\x04'
|
|
||||||
|
|
||||||
|
|
||||||
= Check UDS_RDBIPR
|
|
||||||
|
|
||||||
rdbipr = UDS(b'\x62\x01\x02dieselgate')
|
|
||||||
assert rdbipr.service == 0x62
|
|
||||||
assert rdbipr.dataIdentifier == 0x0102
|
|
||||||
assert rdbipr.load == b'dieselgate'
|
|
||||||
|
|
||||||
= Check UDS_RMBA
|
|
||||||
|
|
||||||
rmba = UDS(b'\x23\x11\x02\x02')
|
|
||||||
assert rmba.service == 0x23
|
|
||||||
assert rmba.memorySizeLen == 1
|
|
||||||
assert rmba.memoryAddressLen == 1
|
|
||||||
assert rmba.memoryAddress1 == 2
|
|
||||||
assert rmba.memorySize1 == 2
|
|
||||||
|
|
||||||
= Check UDS_RMBA
|
|
||||||
|
|
||||||
rmba = UDS(b'\x23\x22\x02\x02\x03\x03')
|
|
||||||
assert rmba.service == 0x23
|
|
||||||
assert rmba.memorySizeLen == 2
|
|
||||||
assert rmba.memoryAddressLen == 2
|
|
||||||
assert rmba.memoryAddress2 == 0x202
|
|
||||||
assert rmba.memorySize2 == 0x303
|
|
||||||
|
|
||||||
= Check UDS_RMBA
|
|
||||||
|
|
||||||
rmba = UDS(b'\x23\x33\x02\x02\x02\x03\x03\x03')
|
|
||||||
assert rmba.service == 0x23
|
|
||||||
assert rmba.memorySizeLen == 3
|
|
||||||
assert rmba.memoryAddressLen == 3
|
|
||||||
assert rmba.memoryAddress3 == 0x20202
|
|
||||||
assert rmba.memorySize3 == 0x30303
|
|
||||||
|
|
||||||
= Check UDS_RMBA
|
|
||||||
|
|
||||||
rmba = UDS(b'\x23\x44\x02\x02\x02\x02\x03\x03\x03\x03')
|
|
||||||
assert rmba.service == 0x23
|
|
||||||
assert rmba.memorySizeLen == 4
|
|
||||||
assert rmba.memoryAddressLen == 4
|
|
||||||
assert rmba.memoryAddress4 == 0x2020202
|
|
||||||
assert rmba.memorySize4 == 0x3030303
|
|
||||||
|
|
||||||
= Check UDS_RMBAPR
|
|
||||||
|
|
||||||
rmbapr = UDS(b'\x63muchData')
|
|
||||||
assert rmbapr.service == 0x63
|
|
||||||
assert rmbapr.dataRecord == b'muchData'
|
|
||||||
|
|
||||||
= Check UDS_RSDBI
|
|
||||||
|
|
||||||
rsdbi = UDS(b'\x24\x12\x34')
|
|
||||||
assert rsdbi.service == 0x24
|
|
||||||
assert rsdbi.dataIdentifier == 0x1234
|
|
||||||
|
|
||||||
= Check UDS_RSDBIPR
|
|
||||||
|
|
||||||
rsdbipr = UDS(b'\x64\x12\x34\xffmuchData')
|
|
||||||
assert rsdbipr.service == 0x64
|
|
||||||
assert rsdbipr.dataIdentifier == 0x1234
|
|
||||||
assert rsdbipr.scalingByte == 255
|
|
||||||
assert rsdbipr.dataRecord == b'muchData'
|
|
||||||
|
|
||||||
= Check UDS_RSDBPI
|
|
||||||
|
|
||||||
rsdbpi = UDS(b'\x2a\x12\x34coffee')
|
|
||||||
assert rsdbpi.service == 0x2a
|
|
||||||
assert rsdbpi.transmissionMode == 0x12
|
|
||||||
assert rsdbpi.periodicDataIdentifier == 0x34
|
|
||||||
assert rsdbpi.furtherPeriodicDataIdentifier == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_RSDBPIPR
|
|
||||||
|
|
||||||
rsdbpipr = UDS(b'\x6a\xff\x12\x34')
|
|
||||||
assert rsdbpipr.service == 0x6a
|
|
||||||
assert rsdbpipr.periodicDataIdentifier == 255
|
|
||||||
assert rsdbpipr.dataRecord == b'\x12\x34'
|
|
||||||
|
|
||||||
= Check UDS_DDDI
|
|
||||||
|
|
||||||
dddi = UDS(b'\x2c\x12coffee')
|
|
||||||
assert dddi.service == 0x2c
|
|
||||||
assert dddi.definitionMode == 0x12
|
|
||||||
assert dddi.dataRecord == b'coffee'
|
|
||||||
|
|
||||||
= Check UDS_DDDIPR
|
|
||||||
|
|
||||||
dddipr = UDS(b'\x6c\x12\x44\x55')
|
|
||||||
assert dddipr.service == 0x6c
|
|
||||||
assert dddipr.definitionMode == 0x12
|
|
||||||
assert dddipr.dynamicallyDefinedDataIdentifier == 0x4455
|
|
||||||
|
|
||||||
= Check UDS_WDBI
|
|
||||||
|
|
||||||
wdbi = UDS(b'\x2e\x01\x02dieselgate')
|
|
||||||
assert wdbi.service == 0x2e
|
|
||||||
assert wdbi.dataIdentifier == 0x0102
|
|
||||||
assert wdbi.load == b'dieselgate'
|
|
||||||
|
|
||||||
= Build UDS_WDBI
|
|
||||||
|
|
||||||
wdbi = UDS()/UDS_WDBI(dataIdentifier=0x0102)/Raw(load=b'dieselgate')
|
|
||||||
assert wdbi.service == 0x2e
|
|
||||||
assert wdbi.dataIdentifier == 0x0102
|
|
||||||
assert wdbi.load == b'dieselgate'
|
|
||||||
assert bytes(wdbi) == b'\x2e\x01\x02dieselgate'
|
|
||||||
|
|
||||||
= Check UDS_WDBIPR
|
|
||||||
|
|
||||||
wdbipr = UDS(b'\x6e\x01\x02')
|
|
||||||
assert wdbipr.service == 0x6e
|
|
||||||
assert wdbipr.dataIdentifier == 0x0102
|
|
||||||
|
|
||||||
= Check UDS_WMBA
|
|
||||||
|
|
||||||
wmba = UDS(b'\x3d\x11\x02\x02muchData')
|
|
||||||
assert wmba.service == 0x3d
|
|
||||||
assert wmba.memorySizeLen == 1
|
|
||||||
assert wmba.memoryAddressLen == 1
|
|
||||||
assert wmba.memoryAddress1 == 2
|
|
||||||
assert wmba.memorySize1 == 2
|
|
||||||
assert wmba.dataRecord == b'muchData'
|
|
||||||
|
|
||||||
= Check UDS_WMBA
|
|
||||||
|
|
||||||
wmba = UDS(b'\x3d\x22\x02\x02\x03\x03muchData')
|
|
||||||
assert wmba.service == 0x3d
|
|
||||||
assert wmba.memorySizeLen == 2
|
|
||||||
assert wmba.memoryAddressLen == 2
|
|
||||||
assert wmba.memoryAddress2 == 0x202
|
|
||||||
assert wmba.memorySize2 == 0x303
|
|
||||||
assert wmba.dataRecord == b'muchData'
|
|
||||||
|
|
||||||
= Check UDS_WMBA
|
|
||||||
|
|
||||||
wmba = UDS(b'\x3d\x33\x02\x02\x02\x03\x03\x03muchData')
|
|
||||||
assert wmba.service == 0x3d
|
|
||||||
assert wmba.memorySizeLen == 3
|
|
||||||
assert wmba.memoryAddressLen == 3
|
|
||||||
assert wmba.memoryAddress3 == 0x20202
|
|
||||||
assert wmba.memorySize3 == 0x30303
|
|
||||||
assert wmba.dataRecord == b'muchData'
|
|
||||||
|
|
||||||
= Check UDS_WMBA
|
|
||||||
|
|
||||||
wmba = UDS(b'\x3d\x44\x02\x02\x02\x02\x03\x03\x03\x03muchData')
|
|
||||||
assert wmba.service == 0x3d
|
|
||||||
assert wmba.memorySizeLen == 4
|
|
||||||
assert wmba.memoryAddressLen == 4
|
|
||||||
assert wmba.memoryAddress4 == 0x2020202
|
|
||||||
assert wmba.memorySize4 == 0x3030303
|
|
||||||
assert wmba.dataRecord == b'muchData'
|
|
||||||
|
|
||||||
= Check UDS_WMBAPR
|
|
||||||
|
|
||||||
wmbapr = UDS(b'\x7d\x11\x02\x02')
|
|
||||||
assert wmbapr.service == 0x7d
|
|
||||||
assert wmbapr.memorySizeLen == 1
|
|
||||||
assert wmbapr.memoryAddressLen == 1
|
|
||||||
assert wmbapr.memoryAddress1 == 2
|
|
||||||
assert wmbapr.memorySize1 == 2
|
|
||||||
|
|
||||||
= Check UDS_WMBAPR
|
|
||||||
|
|
||||||
wmbapr = UDS(b'\x7d\x22\x02\x02\x03\x03')
|
|
||||||
assert wmbapr.service == 0x7d
|
|
||||||
assert wmbapr.memorySizeLen == 2
|
|
||||||
assert wmbapr.memoryAddressLen == 2
|
|
||||||
assert wmbapr.memoryAddress2 == 0x202
|
|
||||||
assert wmbapr.memorySize2 == 0x303
|
|
||||||
|
|
||||||
= Check UDS_WMBAPR
|
|
||||||
|
|
||||||
wmbapr = UDS(b'\x7d\x33\x02\x02\x02\x03\x03\x03')
|
|
||||||
assert wmbapr.service == 0x7d
|
|
||||||
assert wmbapr.memorySizeLen == 3
|
|
||||||
assert wmbapr.memoryAddressLen == 3
|
|
||||||
assert wmbapr.memoryAddress3 == 0x20202
|
|
||||||
assert wmbapr.memorySize3 == 0x30303
|
|
||||||
|
|
||||||
= Check UDS_WMBAPR
|
|
||||||
|
|
||||||
wmbapr = UDS(b'\x7d\x44\x02\x02\x02\x02\x03\x03\x03\x03')
|
|
||||||
assert wmbapr.service == 0x7d
|
|
||||||
assert wmbapr.memorySizeLen == 4
|
|
||||||
assert wmbapr.memoryAddressLen == 4
|
|
||||||
assert wmbapr.memoryAddress4 == 0x2020202
|
|
||||||
assert wmbapr.memorySize4 == 0x3030303
|
|
||||||
|
|
||||||
= Check UDS_CDTCI
|
|
||||||
|
|
||||||
cdtci = UDS(b'\x14\x44\x02\x03')
|
|
||||||
assert cdtci.service == 0x14
|
|
||||||
assert cdtci.groupOfDTCHighByte == 0x44
|
|
||||||
assert cdtci.groupOfDTCMiddleByte == 0x02
|
|
||||||
assert cdtci.groupOfDTCLowByte == 0x3
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x44')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x44
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x01\xff')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x01
|
|
||||||
assert rdtci.DTCStatusMask == 0xff
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x02\xff')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x02
|
|
||||||
assert rdtci.DTCStatusMask == 0xff
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x0f\xff')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x0f
|
|
||||||
assert rdtci.DTCStatusMask == 0xff
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x11\xff')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x11
|
|
||||||
assert rdtci.DTCStatusMask == 0xff
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x12\xff')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x12
|
|
||||||
assert rdtci.DTCStatusMask == 0xff
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x13\xff')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x13
|
|
||||||
assert rdtci.DTCStatusMask == 0xff
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x03\xff\xee\xdd\xaa')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x03
|
|
||||||
assert rdtci.DTCHighByte == 0xff
|
|
||||||
assert rdtci.DTCMiddleByte == 0xee
|
|
||||||
assert rdtci.DTCLowByte == 0xdd
|
|
||||||
assert rdtci.DTCSnapshotRecordNumber == 0xaa
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x04\xff\xee\xdd\xaa')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x04
|
|
||||||
assert rdtci.DTCHighByte == 0xff
|
|
||||||
assert rdtci.DTCMiddleByte == 0xee
|
|
||||||
assert rdtci.DTCLowByte == 0xdd
|
|
||||||
assert rdtci.DTCSnapshotRecordNumber == 0xaa
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x05\xaa')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x05
|
|
||||||
assert rdtci.DTCSnapshotRecordNumber == 0xaa
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x06\xff\xee\xdd\xaa')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x06
|
|
||||||
assert rdtci.DTCHighByte == 0xff
|
|
||||||
assert rdtci.DTCMiddleByte == 0xee
|
|
||||||
assert rdtci.DTCLowByte == 0xdd
|
|
||||||
assert rdtci.DTCExtendedDataRecordNumber == 0xaa
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x07\xaa\xbb')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x07
|
|
||||||
assert rdtci.DTCSeverityMask == 0xaa
|
|
||||||
assert rdtci.DTCStatusMask == 0xbb
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x08\xaa\xbb')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x08
|
|
||||||
assert rdtci.DTCSeverityMask == 0xaa
|
|
||||||
assert rdtci.DTCStatusMask == 0xbb
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x09\xff\xee\xdd')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x09
|
|
||||||
assert rdtci.DTCHighByte == 0xff
|
|
||||||
assert rdtci.DTCMiddleByte == 0xee
|
|
||||||
assert rdtci.DTCLowByte == 0xdd
|
|
||||||
|
|
||||||
= Check UDS_RDTCI
|
|
||||||
|
|
||||||
rdtci = UDS(b'\x19\x10\xff\xee\xdd\xaa')
|
|
||||||
assert rdtci.service == 0x19
|
|
||||||
assert rdtci.reportType == 0x10
|
|
||||||
assert rdtci.DTCHighByte == 0xff
|
|
||||||
assert rdtci.DTCMiddleByte == 0xee
|
|
||||||
assert rdtci.DTCLowByte == 0xdd
|
|
||||||
assert rdtci.DTCExtendedDataRecordNumber == 0xaa
|
|
||||||
|
|
||||||
= Check UDS_RDTCIPR
|
|
||||||
|
|
||||||
rdtcipr = UDS(b'\x59\x01\xff\xee\xdd\xaa')
|
|
||||||
assert rdtcipr.service == 0x59
|
|
||||||
assert rdtcipr.reportType == 1
|
|
||||||
assert rdtcipr.DTCStatusAvailabilityMask == 0xff
|
|
||||||
assert rdtcipr.DTCFormatIdentifier == 0xee
|
|
||||||
assert rdtcipr.DTCCount == 0xddaa
|
|
||||||
|
|
||||||
= Check UDS_RDTCIPR
|
|
||||||
|
|
||||||
rdtcipr = UDS(b'\x59\x02\xff\xee\xdd\xaa')
|
|
||||||
assert rdtcipr.service == 0x59
|
|
||||||
assert rdtcipr.reportType == 2
|
|
||||||
assert rdtcipr.DTCStatusAvailabilityMask == 0xff
|
|
||||||
assert rdtcipr.DTCAndStatusRecord == b'\xee\xdd\xaa'
|
|
||||||
|
|
||||||
= Check UDS_RDTCIPR
|
|
||||||
|
|
||||||
rdtcipr = UDS(b'\x59\x03\xff\xee\xdd\xaa')
|
|
||||||
assert rdtcipr.service == 0x59
|
|
||||||
assert rdtcipr.reportType == 3
|
|
||||||
assert rdtcipr.dataRecord == b'\xff\xee\xdd\xaa'
|
|
||||||
|
|
||||||
= Check UDS_RC
|
|
||||||
|
|
||||||
rc = UDS(b'\x31\x03\xff\xee\xdd\xaa')
|
|
||||||
assert rc.service == 0x31
|
|
||||||
assert rc.routineControlType == 3
|
|
||||||
assert rc.routineIdentifier == 0xffee
|
|
||||||
assert rc.routineControlOptionRecord == b'\xdd\xaa'
|
|
||||||
|
|
||||||
= Check UDS_RCPR
|
|
||||||
|
|
||||||
rcpr = UDS(b'\x71\x03\xff\xee\xdd\xaa')
|
|
||||||
assert rcpr.service == 0x71
|
|
||||||
assert rcpr.routineControlType == 3
|
|
||||||
assert rcpr.routineIdentifier == 0xffee
|
|
||||||
assert rcpr.routineStatusRecord == b'\xdd\xaa'
|
|
||||||
|
|
||||||
= Check UDS_RD
|
|
||||||
|
|
||||||
rd = UDS(b'\x34\xaa\x11\x02\x02')
|
|
||||||
assert rd.service == 0x34
|
|
||||||
assert rd.dataFormatIdentifier == 0xaa
|
|
||||||
assert rd.memorySizeLen == 1
|
|
||||||
assert rd.memoryAddressLen == 1
|
|
||||||
assert rd.memoryAddress1 == 2
|
|
||||||
assert rd.memorySize1 == 2
|
|
||||||
|
|
||||||
= Check UDS_RD
|
|
||||||
|
|
||||||
rd = UDS(b'\x34\xaa\x22\x02\x02\x03\x03')
|
|
||||||
assert rd.service == 0x34
|
|
||||||
assert rd.dataFormatIdentifier == 0xaa
|
|
||||||
assert rd.memorySizeLen == 2
|
|
||||||
assert rd.memoryAddressLen == 2
|
|
||||||
assert rd.memoryAddress2 == 0x202
|
|
||||||
assert rd.memorySize2 == 0x303
|
|
||||||
|
|
||||||
= Check UDS_RD
|
|
||||||
|
|
||||||
rd = UDS(b'\x34\xaa\x33\x02\x02\x02\x03\x03\x03')
|
|
||||||
assert rd.service == 0x34
|
|
||||||
assert rd.dataFormatIdentifier == 0xaa
|
|
||||||
assert rd.memorySizeLen == 3
|
|
||||||
assert rd.memoryAddressLen == 3
|
|
||||||
assert rd.memoryAddress3 == 0x20202
|
|
||||||
assert rd.memorySize3 == 0x30303
|
|
||||||
|
|
||||||
= Check UDS_RD
|
|
||||||
|
|
||||||
rd = UDS(b'\x34\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
|
|
||||||
assert rd.service == 0x34
|
|
||||||
assert rd.dataFormatIdentifier == 0xaa
|
|
||||||
assert rd.memorySizeLen == 4
|
|
||||||
assert rd.memoryAddressLen == 4
|
|
||||||
assert rd.memoryAddress4 == 0x2020202
|
|
||||||
assert rd.memorySize4 == 0x3030303
|
|
||||||
|
|
||||||
= Check UDS_RDPR
|
|
||||||
|
|
||||||
rdpr = UDS(b'\x74\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
|
|
||||||
assert rdpr.service == 0x74
|
|
||||||
assert rdpr.routineControlType == 0xaa
|
|
||||||
assert rdpr.memorySizeLen == 4
|
|
||||||
assert rdpr.memoryAddressLen == 4
|
|
||||||
assert rdpr.maxNumberOfBlockLength == b'\x02\x02\x02\x02\x03\x03\x03\x03'
|
|
||||||
|
|
||||||
= Check UDS_RU
|
|
||||||
|
|
||||||
ru = UDS(b'\x35\xaa\x11\x02\x02')
|
|
||||||
assert ru.service == 0x35
|
|
||||||
assert ru.dataFormatIdentifier == 0xaa
|
|
||||||
assert ru.memorySizeLen == 1
|
|
||||||
assert ru.memoryAddressLen == 1
|
|
||||||
assert ru.memoryAddress1 == 2
|
|
||||||
assert ru.memorySize1 == 2
|
|
||||||
|
|
||||||
= Check UDS_RU
|
|
||||||
|
|
||||||
ru = UDS(b'\x35\xaa\x22\x02\x02\x03\x03')
|
|
||||||
assert ru.service == 0x35
|
|
||||||
assert ru.dataFormatIdentifier == 0xaa
|
|
||||||
assert ru.memorySizeLen == 2
|
|
||||||
assert ru.memoryAddressLen == 2
|
|
||||||
assert ru.memoryAddress2 == 0x202
|
|
||||||
assert ru.memorySize2 == 0x303
|
|
||||||
|
|
||||||
= Check UDS_RU
|
|
||||||
|
|
||||||
ru = UDS(b'\x35\xaa\x33\x02\x02\x02\x03\x03\x03')
|
|
||||||
assert ru.service == 0x35
|
|
||||||
assert ru.dataFormatIdentifier == 0xaa
|
|
||||||
assert ru.memorySizeLen == 3
|
|
||||||
assert ru.memoryAddressLen == 3
|
|
||||||
assert ru.memoryAddress3 == 0x20202
|
|
||||||
assert ru.memorySize3 == 0x30303
|
|
||||||
|
|
||||||
= Check UDS_RU
|
|
||||||
|
|
||||||
ru = UDS(b'\x35\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
|
|
||||||
assert ru.service == 0x35
|
|
||||||
assert ru.dataFormatIdentifier == 0xaa
|
|
||||||
assert ru.memorySizeLen == 4
|
|
||||||
assert ru.memoryAddressLen == 4
|
|
||||||
assert ru.memoryAddress4 == 0x2020202
|
|
||||||
assert ru.memorySize4 == 0x3030303
|
|
||||||
|
|
||||||
= Check UDS_RUPR
|
|
||||||
|
|
||||||
rupr = UDS(b'\x75\xaa\x44\x02\x02\x02\x02\x03\x03\x03\x03')
|
|
||||||
assert rupr.service == 0x75
|
|
||||||
assert rupr.routineControlType == 0xaa
|
|
||||||
assert rupr.memorySizeLen == 4
|
|
||||||
assert rupr.memoryAddressLen == 4
|
|
||||||
assert rupr.maxNumberOfBlockLength == b'\x02\x02\x02\x02\x03\x03\x03\x03'
|
|
||||||
|
|
||||||
= Check UDS_TD
|
|
||||||
|
|
||||||
td = UDS(b'\x36\xaapayload')
|
|
||||||
assert td.service == 0x36
|
|
||||||
assert td.blockSequenceCounter == 0xaa
|
|
||||||
assert td.transferRequestParameterRecord == b'payload'
|
|
||||||
|
|
||||||
= Check UDS_TDPR
|
|
||||||
|
|
||||||
tdpr = UDS(b'\x76\xaapayload')
|
|
||||||
assert tdpr.service == 0x76
|
|
||||||
assert tdpr.blockSequenceCounter == 0xaa
|
|
||||||
assert tdpr.transferResponseParameterRecord == b'payload'
|
|
||||||
|
|
||||||
= Check UDS_RTE
|
|
||||||
|
|
||||||
rte = UDS(b'\x37payload')
|
|
||||||
assert rte.service == 0x37
|
|
||||||
assert rte.transferRequestParameterRecord == b'payload'
|
|
||||||
|
|
||||||
= Check UDS_RTEPR
|
|
||||||
|
|
||||||
rtepr = UDS(b'\x77payload')
|
|
||||||
assert rtepr.service == 0x77
|
|
||||||
assert rtepr.transferResponseParameterRecord == b'payload'
|
|
||||||
|
|
||||||
= Check UDS_IOCBI
|
|
||||||
|
|
||||||
iocbi = UDS(b'\x2f\x23\x34\xffcoffee')
|
|
||||||
assert iocbi.service == 0x2f
|
|
||||||
assert iocbi.dataIdentifier == 0x2334
|
|
||||||
assert iocbi.controlOptionRecord == 255
|
|
||||||
assert iocbi.controlEnableMaskRecord == b'coffee'
|
|
||||||
|
|
||||||
|
|
||||||
= Check UDS_NRC
|
|
||||||
|
|
||||||
nrc = UDS(b'\x7f\x22\x33')
|
|
||||||
assert nrc.service == 0x7f
|
|
||||||
assert nrc.requestServiceId == 0x22
|
|
||||||
assert nrc.negativeResponseCode == 0x33
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.status = skip
|
|
||||||
|
|
||||||
"""
|
|
||||||
Package of contrib automotive bmw specific modules
|
|
||||||
that have to be loaded explicitly.
|
|
||||||
"""
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,71 +0,0 @@
|
||||||
# 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 Packet, bind_layers
|
|
||||||
from scapy.fields import IntEnumField, IntField, LongField, SignedIntField
|
|
||||||
from scapy.layers.dot11 import Dot11
|
|
||||||
from scapy.data import DLT_IEEE802_11_RADIO_AVS
|
|
||||||
from scapy.config import conf
|
|
||||||
|
|
||||||
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),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
conf.l2types.register(DLT_IEEE802_11_RADIO_AVS, AVSWLANHeader)
|
|
||||||
|
|
||||||
bind_layers(AVSWLANHeader, Dot11)
|
|
|
@ -1,19 +0,0 @@
|
||||||
% Regression tests for the avs module
|
|
||||||
|
|
||||||
+ Basic AVS test
|
|
||||||
|
|
||||||
= Default build, storage and dissection
|
|
||||||
|
|
||||||
pkt = AVSWLANHeader()/Dot11()/Dot11Auth()
|
|
||||||
_filepath = get_temp_file(autoext=".pcap")
|
|
||||||
wrpcap(_filepath, pkt)
|
|
||||||
pkt1 = rdpcap(_filepath)[0]
|
|
||||||
assert raw(pkt) == raw(pkt1)
|
|
||||||
assert AVSWLANHeader in pkt
|
|
||||||
assert Dot11 in pkt
|
|
||||||
assert Dot11Auth in pkt
|
|
||||||
|
|
||||||
try:
|
|
||||||
os.remove(_filepath)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
|
@ -1,42 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Parag Bhide
|
|
||||||
# This program is published under GPLv2 license
|
|
||||||
|
|
||||||
"""
|
|
||||||
BFD - Bidirectional Forwarding Detection - RFC 5880, 5881
|
|
||||||
"""
|
|
||||||
|
|
||||||
# scapy.contrib.description = BFD
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers, bind_bottom_up
|
|
||||||
from scapy.fields import BitField, FlagsField, XByteField
|
|
||||||
from scapy.layers.inet import UDP
|
|
||||||
|
|
||||||
|
|
||||||
class BFD(Packet):
|
|
||||||
name = "BFD"
|
|
||||||
fields_desc = [
|
|
||||||
BitField("version", 1, 3),
|
|
||||||
BitField("diag", 0, 5),
|
|
||||||
BitField("sta", 3, 2),
|
|
||||||
FlagsField("flags", 0x00, 6, ['P', 'F', 'C', 'A', 'D', 'M']),
|
|
||||||
XByteField("detect_mult", 0x03),
|
|
||||||
XByteField("len", 24),
|
|
||||||
BitField("my_discriminator", 0x11111111, 32),
|
|
||||||
BitField("your_discriminator", 0x22222222, 32),
|
|
||||||
BitField("min_tx_interval", 1000000000, 32),
|
|
||||||
BitField("min_rx_interval", 1000000000, 32),
|
|
||||||
BitField("echo_rx_interval", 1000000000, 32)]
|
|
||||||
|
|
||||||
def mysummary(self):
|
|
||||||
return self.sprintf(
|
|
||||||
"BFD (my_disc=%BFD.my_discriminator%,"
|
|
||||||
"your_disc=%BFD.my_discriminator%)"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
bind_bottom_up(UDP, BFD, dport=3784)
|
|
||||||
bind_bottom_up(UDP, BFD, sport=3784)
|
|
||||||
bind_layers(UDP, BFD, sport=3784, dport=3784)
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,740 +0,0 @@
|
||||||
#################################### 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)
|
|
||||||
|
|
||||||
= BGPCapability - sessions (1)
|
|
||||||
p = IP()/TCP()/BGPCapability()
|
|
||||||
l = PacketList(p)
|
|
||||||
s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e
|
|
||||||
assert len(s) == 1
|
|
||||||
|
|
||||||
= BGPCapability - sessions (2)
|
|
||||||
p = IP()/UDP()/BGPCapability()
|
|
||||||
l = PacketList(p)
|
|
||||||
s = l.sessions() # Crashed on commit: e42ecdc54556c4852ca06b1a6da6c1ccbf3f522e
|
|
||||||
assert len(s) == 1
|
|
||||||
|
|
||||||
|
|
||||||
############################ 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"
|
|
||||||
|
|
||||||
= BGPPathAttr - advanced
|
|
||||||
b = BGPPathAttr(type_code=0x10, attribute=BGPPAExtComms(extended_communities=[
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommTwoOctetASSpecific()),
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommIPv4AddressSpecific()),
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommFourOctetASSpecific()),
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommOpaque()),
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommTrafficMarking()),
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommRedirectIPv4()),
|
|
||||||
BGPPAExtCommunity(value=BGPPAExtCommRedirectAS4Byte()),
|
|
||||||
]))
|
|
||||||
b = BGPPathAttr(raw(b))
|
|
||||||
cls_list = [x.value.__class__ for x in b.attribute.extended_communities]
|
|
||||||
assert cls_list == [BGPPAExtCommTwoOctetASSpecific, BGPPAExtCommIPv4AddressSpecific, BGPPAExtCommFourOctetASSpecific, BGPPAExtCommOpaque,
|
|
||||||
BGPPAExtCommTrafficMarking, BGPPAExtCommRedirectIPv4, BGPPAExtCommRedirectAS4Byte]
|
|
||||||
b.show()
|
|
||||||
|
|
||||||
################################# 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")
|
|
||||||
|
|
||||||
|
|
||||||
########## BGPCapGeneric fuzz() ###################################
|
|
||||||
+ BGPCapGeneric fuzz()
|
|
||||||
|
|
||||||
= BGPCapGeneric fuzz()
|
|
||||||
for i in range(10):
|
|
||||||
assert isinstance(raw(fuzz(BGPCapGeneric())), bytes)
|
|
|
@ -1,69 +0,0 @@
|
||||||
# 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 = Bit Index Explicit Replication (BIER)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.fields import BitEnumField, BitField, BitFieldLenField, ByteField, \
|
|
||||||
ShortField, StrLenField
|
|
||||||
from scapy.layers.inet import IP, UDP
|
|
||||||
from scapy.layers.inet6 import IPv6
|
|
||||||
|
|
||||||
|
|
||||||
class BIERLength:
|
|
||||||
BIER_LEN_64 = 0
|
|
||||||
BIER_LEN_128 = 1
|
|
||||||
BIER_LEN_256 = 2
|
|
||||||
BIER_LEN_512 = 3
|
|
||||||
BIER_LEN_1024 = 4
|
|
||||||
|
|
||||||
|
|
||||||
BIERnhcls = {1: "MPLS",
|
|
||||||
2: "MPLS",
|
|
||||||
4: "IPv4",
|
|
||||||
5: "IPv6"}
|
|
||||||
|
|
||||||
|
|
||||||
class BIFT(Packet):
|
|
||||||
name = "BIFT"
|
|
||||||
fields_desc = [BitField("bsl", BIERLength.BIER_LEN_256, 4),
|
|
||||||
BitField("sd", 0, 8),
|
|
||||||
BitField("set", 0, 8),
|
|
||||||
BitField("cos", 0, 3),
|
|
||||||
BitField("s", 1, 1),
|
|
||||||
ByteField("ttl", 0)]
|
|
||||||
|
|
||||||
|
|
||||||
class BIER(Packet):
|
|
||||||
name = "BIER"
|
|
||||||
fields_desc = [BitField("id", 5, 4),
|
|
||||||
BitField("version", 0, 4),
|
|
||||||
BitFieldLenField("length", BIERLength.BIER_LEN_256, 4,
|
|
||||||
length_of=lambda x:(x.BitString >> 8)),
|
|
||||||
BitField("entropy", 0, 20),
|
|
||||||
BitField("OAM", 0, 2),
|
|
||||||
BitField("RSV", 0, 2),
|
|
||||||
BitField("DSCP", 0, 6),
|
|
||||||
BitEnumField("Proto", 2, 6, BIERnhcls),
|
|
||||||
ShortField("BFRID", 0),
|
|
||||||
StrLenField("BitString",
|
|
||||||
"",
|
|
||||||
length_from=lambda x:(8 << x.length))]
|
|
||||||
|
|
||||||
|
|
||||||
bind_layers(BIER, IP, Proto=4)
|
|
||||||
bind_layers(BIER, IPv6, Proto=5)
|
|
||||||
bind_layers(UDP, BIFT, dport=8138)
|
|
||||||
bind_layers(BIFT, BIER)
|
|
|
@ -1,22 +0,0 @@
|
||||||
# BIER unit tests
|
|
||||||
#
|
|
||||||
# Type the following command to launch start the tests:
|
|
||||||
# $ test/run_tests -P "load_contrib('bier')" -P "load_contrib('mpls')" -t scapy/contrib/bier.uts
|
|
||||||
|
|
||||||
+ BIER tests
|
|
||||||
|
|
||||||
= BIER - build/dissection
|
|
||||||
|
|
||||||
from scapy.contrib.mpls import MPLS
|
|
||||||
|
|
||||||
p1 = MPLS()/BIER(length=BIERLength.BIER_LEN_256)/IP()/UDP()
|
|
||||||
assert(p1[MPLS].s == 1)
|
|
||||||
p2 = BIFT()/BIER(length=BIERLength.BIER_LEN_64)/IP()/UDP()
|
|
||||||
assert(p2[BIFT].s == 1)
|
|
||||||
|
|
||||||
p1[MPLS]
|
|
||||||
p1[BIER]
|
|
||||||
p1[IP]
|
|
||||||
p2[BIFT]
|
|
||||||
p2[BIER]
|
|
||||||
p2[IP]
|
|
|
@ -1,128 +0,0 @@
|
||||||
# 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 2012, The MITRE Corporation::
|
|
||||||
|
|
||||||
NOTICE
|
|
||||||
This software/technical data was produced for the U.S. Government
|
|
||||||
under Prime Contract No. NASA-03001 and JPL Contract No. 1295026
|
|
||||||
and is subject to FAR 52.227-14 (6/87) Rights in Data General,
|
|
||||||
and Article GP-51, Rights in Data General, respectively.
|
|
||||||
This software is publicly released under MITRE case #12-3054
|
|
||||||
"""
|
|
||||||
|
|
||||||
# scapy.contrib.description = Bundle Protocol (BP)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.fields import ByteEnumField, ByteField, ConditionalField, \
|
|
||||||
StrLenField
|
|
||||||
from scapy.contrib.sdnv import SDNV2FieldLenField, SDNV2LenField, SDNV2
|
|
||||||
from scapy.contrib.ltp import LTP, ltp_bind_payload
|
|
||||||
|
|
||||||
|
|
||||||
class BP(Packet):
|
|
||||||
name = "BP"
|
|
||||||
fields_desc = [ByteField('version', 0x06),
|
|
||||||
SDNV2('ProcFlags', 0),
|
|
||||||
SDNV2LenField('BlockLen', None),
|
|
||||||
SDNV2('DSO', 0),
|
|
||||||
SDNV2('DSSO', 0),
|
|
||||||
SDNV2('SSO', 0),
|
|
||||||
SDNV2('SSSO', 0),
|
|
||||||
SDNV2('RTSO', 0),
|
|
||||||
SDNV2('RTSSO', 0),
|
|
||||||
SDNV2('CSO', 0),
|
|
||||||
SDNV2('CSSO', 0),
|
|
||||||
SDNV2('CT', 0),
|
|
||||||
SDNV2('CTSN', 0),
|
|
||||||
SDNV2('LT', 0),
|
|
||||||
SDNV2('DL', 0),
|
|
||||||
ConditionalField(SDNV2("FO", 0), lambda x: (
|
|
||||||
x.ProcFlags & 0x01)),
|
|
||||||
ConditionalField(SDNV2("ADUL", 0), lambda x: (
|
|
||||||
x.ProcFlags & 0x01)),
|
|
||||||
]
|
|
||||||
|
|
||||||
def mysummary(self):
|
|
||||||
tmp = "BP(%version%) flags("
|
|
||||||
if (self.ProcFlags & 0x01):
|
|
||||||
tmp += ' FR'
|
|
||||||
if (self.ProcFlags & 0x02):
|
|
||||||
tmp += ' AR'
|
|
||||||
if (self.ProcFlags & 0x04):
|
|
||||||
tmp += ' DF'
|
|
||||||
if (self.ProcFlags & 0x08):
|
|
||||||
tmp += ' CT'
|
|
||||||
if (self.ProcFlags & 0x10):
|
|
||||||
tmp += ' S'
|
|
||||||
if (self.ProcFlags & 0x20):
|
|
||||||
tmp += ' ACKME'
|
|
||||||
RAWCOS = (self.ProcFlags & 0x0180)
|
|
||||||
COS = RAWCOS >> 7
|
|
||||||
cos_tmp = ''
|
|
||||||
if COS == 0x00:
|
|
||||||
cos_tmp += 'B '
|
|
||||||
if COS == 0x01:
|
|
||||||
cos_tmp += 'N '
|
|
||||||
if COS == 0x02:
|
|
||||||
cos_tmp += 'E '
|
|
||||||
if COS & 0xFE000:
|
|
||||||
cos_tmp += 'SRR: ('
|
|
||||||
if COS & 0x02000:
|
|
||||||
cos_tmp += 'Rec '
|
|
||||||
if COS & 0x04000:
|
|
||||||
cos_tmp += 'CA '
|
|
||||||
if COS & 0x08000:
|
|
||||||
cos_tmp += 'FWD '
|
|
||||||
if COS & 0x10000:
|
|
||||||
cos_tmp += 'DLV '
|
|
||||||
if COS & 0x20000:
|
|
||||||
cos_tmp += 'DEL '
|
|
||||||
if COS & 0xFE000:
|
|
||||||
cos_tmp += ') '
|
|
||||||
|
|
||||||
if cos_tmp:
|
|
||||||
tmp += ' Pr: ' + cos_tmp
|
|
||||||
|
|
||||||
tmp += " ) len(%BlockLen%) "
|
|
||||||
if self.DL == 0:
|
|
||||||
tmp += "CBHE: d[%DSO%,%DSSO%] s[%SSO%, %SSSO%] r[%RTSO%, %RTSSO%] c[%CSO%, %CSSO%] " # noqa: E501
|
|
||||||
else:
|
|
||||||
tmp += "dl[%DL%] "
|
|
||||||
tmp += "ct[%CT%] ctsn[%CTSN%] lt[%LT%] "
|
|
||||||
if (self.ProcFlags & 0x01):
|
|
||||||
tmp += "fo[%FO%] "
|
|
||||||
tmp += "tl[%ADUL%]"
|
|
||||||
|
|
||||||
return self.sprintf(tmp), [LTP]
|
|
||||||
|
|
||||||
|
|
||||||
class BPBLOCK(Packet):
|
|
||||||
fields_desc = [ByteEnumField('Type', 1, {1: "Bundle payload block"}),
|
|
||||||
SDNV2('ProcFlags', 0),
|
|
||||||
SDNV2FieldLenField('BlockLen', None, length_of="load"),
|
|
||||||
StrLenField("load", "",
|
|
||||||
length_from=lambda pkt: pkt.BlockLen,
|
|
||||||
max_length=65535)
|
|
||||||
]
|
|
||||||
|
|
||||||
def mysummary(self):
|
|
||||||
return self.sprintf("BPBLOCK(%Type%) Flags: %ProcFlags% Len: %BlockLen%") # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
ltp_bind_payload(BP, lambda pkt: pkt.DATA_ClientServiceID == 1)
|
|
||||||
bind_layers(BP, BPBLOCK)
|
|
||||||
bind_layers(BPBLOCK, BPBLOCK)
|
|
|
@ -1,33 +0,0 @@
|
||||||
% Bundle Protocol tests
|
|
||||||
|
|
||||||
############
|
|
||||||
############
|
|
||||||
+ Bundle Protocol (BP) basic tests
|
|
||||||
|
|
||||||
#TODO: no pcap have been found on Internet. Check that scapy correctly decode those too
|
|
||||||
|
|
||||||
= Build packets & dissect
|
|
||||||
|
|
||||||
from scapy.contrib.ltp import LTPex
|
|
||||||
|
|
||||||
pkt = Ether(src="aa:aa:aa:aa:aa:aa", dst="bb:bb:bb:bb:bb:bb")/IP(src="192.168.0.1", dst="192.168.0.2")/UDP()/LTP(flags=7,\
|
|
||||||
SessionOriginator=2,
|
|
||||||
SessionNumber=113,
|
|
||||||
HeaderExtensions=[
|
|
||||||
LTPex(ExTag=1, ExData=b"\x00"),
|
|
||||||
],
|
|
||||||
DATA_ClientServiceID=1,
|
|
||||||
DATA_PayloadOffset=0,
|
|
||||||
LTP_Payload=[
|
|
||||||
BP(ProcFlags=415)/\
|
|
||||||
BPBLOCK(ProcFlags=10, load="data")
|
|
||||||
])
|
|
||||||
|
|
||||||
pkt = Ether(raw(pkt))
|
|
||||||
assert LTP in pkt
|
|
||||||
bp = pkt[LTP].LTP_Payload[0]
|
|
||||||
assert BP in bp
|
|
||||||
assert BPBLOCK in bp
|
|
||||||
assert bp.load == b"data"
|
|
||||||
|
|
||||||
bp.mysummary()
|
|
|
@ -1,47 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = CANSocket Utils
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
"""
|
|
||||||
CANSocket.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from scapy.error import log_loading
|
|
||||||
from scapy.consts import LINUX
|
|
||||||
from scapy.config import conf
|
|
||||||
import scapy.modules.six as six
|
|
||||||
|
|
||||||
PYTHON_CAN = False
|
|
||||||
|
|
||||||
try:
|
|
||||||
if conf.contribs['CANSocket']['use-python-can']:
|
|
||||||
from can import BusABC as can_BusABC # noqa: F401
|
|
||||||
PYTHON_CAN = True
|
|
||||||
else:
|
|
||||||
PYTHON_CAN = False
|
|
||||||
except ImportError:
|
|
||||||
log_loading.info("Can't import python-can.")
|
|
||||||
except KeyError:
|
|
||||||
log_loading.info("Configuration 'conf.contribs['CANSocket'] not found.")
|
|
||||||
|
|
||||||
|
|
||||||
if PYTHON_CAN:
|
|
||||||
log_loading.info("Using python-can CANSocket.")
|
|
||||||
log_loading.info("Specify 'conf.contribs['CANSocket'] = "
|
|
||||||
"{'use-python-can': False}' to enable native CANSockets.")
|
|
||||||
from scapy.contrib.cansocket_python_can import (PythonCANSocket, CANSocket, CAN_FRAME_SIZE, CAN_INV_FILTER) # noqa: E501 F401
|
|
||||||
|
|
||||||
elif LINUX and six.PY3 and not conf.use_pypy:
|
|
||||||
log_loading.info("Using native CANSocket.")
|
|
||||||
log_loading.info("Specify 'conf.contribs['CANSocket'] = "
|
|
||||||
"{'use-python-can': True}' "
|
|
||||||
"to enable python-can CANSockets.")
|
|
||||||
from scapy.contrib.cansocket_native import (NativeCANSocket, CANSocket, CAN_FRAME_SIZE, CAN_INV_FILTER) # noqa: E501 F401
|
|
||||||
|
|
||||||
else:
|
|
||||||
log_loading.info("No CAN support available. Install python-can or "
|
|
||||||
"use Linux and python3.")
|
|
|
@ -1,123 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = Native CANSocket
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
"""
|
|
||||||
NativeCANSocket.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import struct
|
|
||||||
import socket
|
|
||||||
import time
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.supersocket import SuperSocket
|
|
||||||
from scapy.error import Scapy_Exception, warning
|
|
||||||
from scapy.layers.can import CAN
|
|
||||||
from scapy.packet import Padding
|
|
||||||
from scapy.arch.linux import get_last_packet_timestamp
|
|
||||||
|
|
||||||
conf.contribs['NativeCANSocket'] = {'channel': "can0"}
|
|
||||||
|
|
||||||
CAN_FRAME_SIZE = 16
|
|
||||||
CAN_INV_FILTER = 0x20000000
|
|
||||||
|
|
||||||
|
|
||||||
class NativeCANSocket(SuperSocket):
|
|
||||||
desc = "read/write packets at a given CAN interface using PF_CAN sockets"
|
|
||||||
nonblocking_socket = True
|
|
||||||
|
|
||||||
def __init__(self, channel=None, receive_own_messages=False,
|
|
||||||
can_filters=None, remove_padding=True, basecls=CAN, **kwargs):
|
|
||||||
bustype = kwargs.pop("bustype", None)
|
|
||||||
if bustype and bustype != "socketcan":
|
|
||||||
warning("You created a NativeCANSocket. "
|
|
||||||
"If you're providing the argument 'bustype', please use "
|
|
||||||
"the correct one to achieve compatibility with python-can"
|
|
||||||
"/PythonCANSocket. \n'bustype=socketcan'")
|
|
||||||
|
|
||||||
self.basecls = basecls
|
|
||||||
self.remove_padding = remove_padding
|
|
||||||
self.channel = conf.contribs['NativeCANSocket']['channel'] if \
|
|
||||||
channel is None else channel
|
|
||||||
self.ins = socket.socket(socket.PF_CAN,
|
|
||||||
socket.SOCK_RAW,
|
|
||||||
socket.CAN_RAW)
|
|
||||||
try:
|
|
||||||
self.ins.setsockopt(socket.SOL_CAN_RAW,
|
|
||||||
socket.CAN_RAW_RECV_OWN_MSGS,
|
|
||||||
struct.pack("i", receive_own_messages))
|
|
||||||
except Exception as exception:
|
|
||||||
raise Scapy_Exception(
|
|
||||||
"Could not modify receive own messages (%s)", exception
|
|
||||||
)
|
|
||||||
|
|
||||||
if can_filters is None:
|
|
||||||
can_filters = [{
|
|
||||||
"can_id": 0,
|
|
||||||
"can_mask": 0
|
|
||||||
}]
|
|
||||||
|
|
||||||
can_filter_fmt = "={}I".format(2 * len(can_filters))
|
|
||||||
filter_data = []
|
|
||||||
for can_filter in can_filters:
|
|
||||||
filter_data.append(can_filter["can_id"])
|
|
||||||
filter_data.append(can_filter["can_mask"])
|
|
||||||
|
|
||||||
self.ins.setsockopt(socket.SOL_CAN_RAW,
|
|
||||||
socket.CAN_RAW_FILTER,
|
|
||||||
struct.pack(can_filter_fmt, *filter_data))
|
|
||||||
|
|
||||||
self.ins.bind((self.channel,))
|
|
||||||
self.outs = self.ins
|
|
||||||
|
|
||||||
def recv(self, x=CAN_FRAME_SIZE):
|
|
||||||
try:
|
|
||||||
pkt, sa_ll = self.ins.recvfrom(x)
|
|
||||||
except BlockingIOError: # noqa: F821
|
|
||||||
warning("Captured no data, socket in non-blocking mode.")
|
|
||||||
return None
|
|
||||||
except socket.timeout:
|
|
||||||
warning("Captured no data, socket read timed out.")
|
|
||||||
return None
|
|
||||||
except OSError:
|
|
||||||
# something bad happened (e.g. the interface went down)
|
|
||||||
warning("Captured no data.")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# need to change the byte order of the first four bytes,
|
|
||||||
# required by the underlying Linux SocketCAN frame format
|
|
||||||
if not conf.contribs['CAN']['swap-bytes']:
|
|
||||||
pkt = struct.pack("<I12s", *struct.unpack(">I12s", pkt))
|
|
||||||
|
|
||||||
len = pkt[4]
|
|
||||||
canpkt = self.basecls(pkt[:len + 8])
|
|
||||||
canpkt.time = get_last_packet_timestamp(self.ins)
|
|
||||||
if self.remove_padding:
|
|
||||||
return canpkt
|
|
||||||
else:
|
|
||||||
return canpkt / Padding(pkt[len + 8:])
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
try:
|
|
||||||
if hasattr(x, "sent_time"):
|
|
||||||
x.sent_time = time.time()
|
|
||||||
|
|
||||||
# need to change the byte order of the first four bytes,
|
|
||||||
# required by the underlying Linux SocketCAN frame format
|
|
||||||
bs = bytes(x)
|
|
||||||
if not conf.contribs['CAN']['swap-bytes']:
|
|
||||||
bs = bs + b'\x00' * (CAN_FRAME_SIZE - len(bs))
|
|
||||||
bs = struct.pack("<I12s", *struct.unpack(">I12s", bs))
|
|
||||||
return SuperSocket.send(self, bs)
|
|
||||||
except socket.error as msg:
|
|
||||||
raise msg
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.ins.close()
|
|
||||||
|
|
||||||
|
|
||||||
CANSocket = NativeCANSocket
|
|
|
@ -1,761 +0,0 @@
|
||||||
% Regression tests for nativecansocket
|
|
||||||
~ python3_only vcan_socket
|
|
||||||
|
|
||||||
# More information at http://www.secdev.org/projects/UTscapy/
|
|
||||||
|
|
||||||
|
|
||||||
############
|
|
||||||
############
|
|
||||||
+ Configuration of CAN virtual sockets
|
|
||||||
|
|
||||||
= Load module
|
|
||||||
~ conf command needs_root linux
|
|
||||||
|
|
||||||
load_layer("can")
|
|
||||||
conf.contribs['CANSocket'] = {'use-python-can': False}
|
|
||||||
from scapy.contrib.cansocket_native import *
|
|
||||||
conf.contribs['CAN'] = {'swap-bytes': False}
|
|
||||||
|
|
||||||
|
|
||||||
= Setup string for vcan
|
|
||||||
~ conf command
|
|
||||||
bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'"
|
|
||||||
|
|
||||||
= Load os
|
|
||||||
~ conf command needs_root linux
|
|
||||||
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
= Setup vcan0
|
|
||||||
~ conf command needs_root linux
|
|
||||||
|
|
||||||
0 == os.system(bashCommand)
|
|
||||||
|
|
||||||
+ Basic Packet Tests()
|
|
||||||
= CAN Packet init
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
canframe = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
bytes(canframe) == b'\x00\x00\x07\xff\x08\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08'
|
|
||||||
|
|
||||||
+ Basic Socket Tests()
|
|
||||||
= CAN Socket Init
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface="vcan0")
|
|
||||||
|
|
||||||
= CAN Socket send recv small packet
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sleep(0.1)
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x7ff,length=1,data=b'\x01'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx == CAN(identifier=0x7ff,length=1,data=b'\x01')
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= CAN Socket send recv
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sleep(0.1)
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= CAN Socket basecls test
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sleep(0.1)
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
sock1.basecls = Raw
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx == Raw(bytes(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')))
|
|
||||||
sock1.basecls = CAN
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
+ Advanced Socket Tests()
|
|
||||||
= CAN Socket sr1
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
|
|
||||||
= CAN Socket sr1 init time
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
tx.sent_time == None
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sleep(0.1)
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(tx)
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = None
|
|
||||||
rx = sock1.sr1(tx)
|
|
||||||
rx == tx
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= CAN Socket sr1 time check
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
tx.sent_time < rx.time and rx.time > 0
|
|
||||||
|
|
||||||
= srcan
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
|
|
||||||
= srcan check init time
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
assert tx.sent_time == None
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sleep(0.1)
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(tx)
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = None
|
|
||||||
rx = srcan(tx, "vcan0", timeout=1)
|
|
||||||
rx = rx[0][0][1]
|
|
||||||
tx == rx
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= srcan check init time basecls
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sleep(0.1)
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(tx)
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = None
|
|
||||||
rx = srcan(tx, "vcan0", timeout=1, basecls=Raw)
|
|
||||||
rx = rx[0][0][1]
|
|
||||||
Raw(bytes(tx)) == rx
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= srcan check rx and tx
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
tx.sent_time > 0 and rx.time > 0 and tx.sent_time < rx.time
|
|
||||||
|
|
||||||
= sniff with filtermask 0x7ff
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}])
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 3
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x700
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x700}])
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x212, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x2ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x2aa, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x0ff
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x0ff}])
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x301, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= sniff with multiple filters
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}, {'can_id': 0x400, 'can_mask': 0x7ff}, {'can_id': 0x600, 'can_mask': 0x7ff}, {'can_id': 0x7ff, 'can_mask': 0x7ff}])
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x400, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x500, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x600, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x7ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x7ff and inverse filter
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7ff}])
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 2
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x1FFFFFFF
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x10000000, 'can_mask': 0x1fffffff}])
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface="vcan0")
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 2
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x1FFFFFFF and inverse filter
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x10000000 | CAN_INV_FILTER, 'can_mask': 0x1fffffff}])
|
|
||||||
|
|
||||||
if six.PY3:
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
packets = sock1.sniff(timeout=0.2, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= CAN Socket sr1 with receive own messages
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface="vcan0", receive_own_messages=True)
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
rx = None
|
|
||||||
rx = sock1.sr1(tx)
|
|
||||||
tx == rx
|
|
||||||
tx.sent_time < rx.time and tx == rx and rx.time > 0
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= srcan
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
rx = None
|
|
||||||
rx = srcan(tx, iface="vcan0", receive_own_messages=True, timeout=1)
|
|
||||||
tx == rx[0][0][1]
|
|
||||||
|
|
||||||
+ bridge and sniff tests
|
|
||||||
|
|
||||||
= bridge and sniff setup vcan1 package forwarding
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
bashCommand = "/bin/bash -c 'sudo ip link add name vcan1 type vcan; sudo ip link set dev vcan1 up'"
|
|
||||||
0 == os.system(bashCommand)
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0')
|
|
||||||
sock1 = CANSocket(iface='vcan1')
|
|
||||||
|
|
||||||
def senderVCan0():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridge():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface='vcan1')
|
|
||||||
def pnr(pkt):
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridge)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan0)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.2, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
sock0.close()
|
|
||||||
|
|
||||||
= bridge and sniff setup vcan0 package forwarding
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0')
|
|
||||||
sock1 = CANSocket(iface='vcan1')
|
|
||||||
|
|
||||||
def senderVCan1():
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridge():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface='vcan1')
|
|
||||||
def pnr(pkt):
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridge)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan1)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.2, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 vcan1 package forwarding both directions
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0')
|
|
||||||
sock1 = CANSocket(iface='vcan1')
|
|
||||||
|
|
||||||
def senderBothVCans():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x30, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridge():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface='vcan1')
|
|
||||||
def pnr(pkt):
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridge)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderBothVCans)
|
|
||||||
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.1, count=6, started_callback=threadSender.start)
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.1)
|
|
||||||
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan1 package change
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0')
|
|
||||||
sock1 = CANSocket(iface='vcan1', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}])
|
|
||||||
|
|
||||||
def senderVCan0():
|
|
||||||
sleep(0.1)
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridgeWithPackageChangeVCan0ToVCan1():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface="vcan1")
|
|
||||||
def pnr(pkt):
|
|
||||||
pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01'
|
|
||||||
pkt.identifier = 0x10010000
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan0ToVCan1)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan0)
|
|
||||||
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.2, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 package change
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}])
|
|
||||||
sock1 = CANSocket(iface='vcan1')
|
|
||||||
|
|
||||||
def senderVCan1():
|
|
||||||
sleep(0.1)
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridgeWithPackageChangeVCan1ToVCan0():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface="vcan1")
|
|
||||||
def pnr(pkt):
|
|
||||||
pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01'
|
|
||||||
pkt.identifier = 0x10010000
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan1ToVCan0)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan1)
|
|
||||||
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.2, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 and vcan1 package change in both directions
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}])
|
|
||||||
sock1 = CANSocket(iface='vcan1', can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}])
|
|
||||||
|
|
||||||
def senderBothVCans():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridgeWithPackageChangeBothDirections():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface="vcan1")
|
|
||||||
def pnr(pkt):
|
|
||||||
pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01'
|
|
||||||
pkt.identifier = 0x10010000
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithPackageChangeBothDirections)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderBothVCans)
|
|
||||||
|
|
||||||
bridgeStarted.wait()
|
|
||||||
threadSender.start()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.1)
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.1)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 package remove
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0')
|
|
||||||
sock1 = CANSocket(iface='vcan1')
|
|
||||||
|
|
||||||
def senderVCan0():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridgeWithRemovePackageFromVCan0ToVCan1():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface="vcan1")
|
|
||||||
def pnr(pkt):
|
|
||||||
if(pkt.identifier == 0x10020000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan0ToVCan1)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan0)
|
|
||||||
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
threadSender.start()
|
|
||||||
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.2)
|
|
||||||
len(packetsVCan1) == 5
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan1 package remove
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface='vcan0')
|
|
||||||
sock1 = CANSocket(iface='vcan1')
|
|
||||||
|
|
||||||
def senderVCan1():
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridgeWithRemovePackageFromVCan1ToVCan0():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface="vcan1")
|
|
||||||
def pnr(pkt):
|
|
||||||
if(pkt.identifier == 0x10050000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan1ToVCan0)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan1)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
threadSender.start()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.2)
|
|
||||||
len(packetsVCan0) == 3
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 and vcan1 package remove both directions
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface="vcan0")
|
|
||||||
sock1 = CANSocket(iface="vcan1")
|
|
||||||
|
|
||||||
def senderBothVCans():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
|
|
||||||
def bridgeWithRemovePackageInBothDirections():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(iface="vcan0")
|
|
||||||
bSock1 = CANSocket(iface="vcan1")
|
|
||||||
def pnrA(pkt):
|
|
||||||
if(pkt.identifier == 0x10020000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
def pnrB(pkt):
|
|
||||||
if (pkt.identifier == 0x10050000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
bridgeStarted.set()
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnrA, xfrm21=pnrB, timeout=0.2)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithRemovePackageInBothDirections)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderBothVCans)
|
|
||||||
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.1, started_callback=threadSender.start)
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.1)
|
|
||||||
|
|
||||||
len(packetsVCan0) == 3
|
|
||||||
len(packetsVCan1) == 5
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadSender.join()
|
|
||||||
threadBridge.join()
|
|
|
@ -1,186 +0,0 @@
|
||||||
# This file is part of Scapy
|
|
||||||
# See http://www.secdev.org/projects/scapy for more information
|
|
||||||
# Copyright (C) Nils Weiss <nils@we155.de>
|
|
||||||
# This program is published under a GPLv2 license
|
|
||||||
|
|
||||||
# scapy.contrib.description = Python-Can CANSocket
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
"""
|
|
||||||
Python-CAN CANSocket Wrapper.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import time
|
|
||||||
import struct
|
|
||||||
import threading
|
|
||||||
import copy
|
|
||||||
|
|
||||||
from functools import reduce
|
|
||||||
from operator import add
|
|
||||||
|
|
||||||
from scapy.config import conf
|
|
||||||
from scapy.supersocket import SuperSocket
|
|
||||||
from scapy.layers.can import CAN
|
|
||||||
from scapy.automaton import SelectableObject
|
|
||||||
from scapy.modules.six.moves import queue
|
|
||||||
from can import Message as can_Message
|
|
||||||
from can import CanError as can_CanError
|
|
||||||
from can import BusABC as can_BusABC
|
|
||||||
from can.interface import Bus as can_Bus
|
|
||||||
|
|
||||||
|
|
||||||
CAN_FRAME_SIZE = 16
|
|
||||||
CAN_INV_FILTER = 0x20000000
|
|
||||||
|
|
||||||
|
|
||||||
class SocketMapper:
|
|
||||||
def __init__(self, bus, sockets):
|
|
||||||
self.bus = bus
|
|
||||||
self.sockets = sockets
|
|
||||||
|
|
||||||
def mux(self):
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
msg = self.bus.recv(timeout=0)
|
|
||||||
if msg is None:
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
for sock in self.sockets:
|
|
||||||
if sock._matches_filters(msg):
|
|
||||||
sock.rx_queue.put(copy.copy(msg))
|
|
||||||
|
|
||||||
|
|
||||||
class SocketsPool(object):
|
|
||||||
__instance = None
|
|
||||||
|
|
||||||
def __new__(cls):
|
|
||||||
if SocketsPool.__instance is None:
|
|
||||||
SocketsPool.__instance = object.__new__(cls)
|
|
||||||
SocketsPool.__instance.pool = dict()
|
|
||||||
SocketsPool.__instance.pool_mutex = threading.Lock()
|
|
||||||
return SocketsPool.__instance
|
|
||||||
|
|
||||||
def internal_send(self, sender, msg):
|
|
||||||
with self.pool_mutex:
|
|
||||||
try:
|
|
||||||
t = self.pool[sender.name]
|
|
||||||
except KeyError:
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
t.bus.send(msg)
|
|
||||||
for sock in t.sockets:
|
|
||||||
if sock != sender and sock._matches_filters(msg):
|
|
||||||
m = copy.copy(msg)
|
|
||||||
m.timestamp = time.time()
|
|
||||||
sock.rx_queue.put(m)
|
|
||||||
except can_CanError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def multiplex_rx_packets(self):
|
|
||||||
with self.pool_mutex:
|
|
||||||
for _, t in self.pool.items():
|
|
||||||
t.mux()
|
|
||||||
|
|
||||||
def register(self, socket, *args, **kwargs):
|
|
||||||
k = str(
|
|
||||||
str(kwargs.get("bustype", "unknown_bustype")) + "_" +
|
|
||||||
str(kwargs.get("channel", "unknown_channel"))
|
|
||||||
)
|
|
||||||
with self.pool_mutex:
|
|
||||||
if k in self.pool:
|
|
||||||
t = self.pool[k]
|
|
||||||
t.sockets.append(socket)
|
|
||||||
filters = [s.filters for s in t.sockets
|
|
||||||
if s.filters is not None]
|
|
||||||
if filters:
|
|
||||||
t.bus.set_filters(reduce(add, filters))
|
|
||||||
socket.name = k
|
|
||||||
else:
|
|
||||||
bus = can_Bus(*args, **kwargs)
|
|
||||||
socket.name = k
|
|
||||||
self.pool[k] = SocketMapper(bus, [socket])
|
|
||||||
|
|
||||||
def unregister(self, socket):
|
|
||||||
with self.pool_mutex:
|
|
||||||
t = self.pool[socket.name]
|
|
||||||
t.sockets.remove(socket)
|
|
||||||
if not t.sockets:
|
|
||||||
t.bus.shutdown()
|
|
||||||
del self.pool[socket.name]
|
|
||||||
|
|
||||||
|
|
||||||
class SocketWrapper(can_BusABC):
|
|
||||||
"""Socket for specific Bus or Interface.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super(SocketWrapper, self).__init__(*args, **kwargs)
|
|
||||||
self.rx_queue = queue.Queue() # type: queue.Queue[can_Message]
|
|
||||||
self.name = None
|
|
||||||
SocketsPool().register(self, *args, **kwargs)
|
|
||||||
|
|
||||||
def _recv_internal(self, timeout):
|
|
||||||
SocketsPool().multiplex_rx_packets()
|
|
||||||
try:
|
|
||||||
return self.rx_queue.get(block=True, timeout=timeout), True
|
|
||||||
except queue.Empty:
|
|
||||||
return None, True
|
|
||||||
|
|
||||||
def send(self, msg, timeout=None):
|
|
||||||
SocketsPool().internal_send(self, msg)
|
|
||||||
|
|
||||||
def shutdown(self):
|
|
||||||
SocketsPool().unregister(self)
|
|
||||||
|
|
||||||
|
|
||||||
class PythonCANSocket(SuperSocket, SelectableObject):
|
|
||||||
desc = "read/write packets at a given CAN interface " \
|
|
||||||
"using a python-can bus object"
|
|
||||||
nonblocking_socket = True
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self.basecls = kwargs.pop("basecls", CAN)
|
|
||||||
self.iface = SocketWrapper(**kwargs)
|
|
||||||
|
|
||||||
def recv_raw(self, x=0xffff):
|
|
||||||
msg = self.iface.recv()
|
|
||||||
|
|
||||||
hdr = msg.is_extended_id << 31 | msg.is_remote_frame << 30 | \
|
|
||||||
msg.is_error_frame << 29 | msg.arbitration_id
|
|
||||||
|
|
||||||
if conf.contribs['CAN']['swap-bytes']:
|
|
||||||
hdr = struct.unpack("<I", struct.pack(">I", hdr))[0]
|
|
||||||
|
|
||||||
dlc = msg.dlc << 24
|
|
||||||
pkt_data = struct.pack("!II", hdr, dlc) + bytes(msg.data)
|
|
||||||
return self.basecls, pkt_data, msg.timestamp
|
|
||||||
|
|
||||||
def send(self, x):
|
|
||||||
msg = can_Message(is_remote_frame=x.flags == 0x2,
|
|
||||||
is_extended_id=x.flags == 0x4,
|
|
||||||
is_error_frame=x.flags == 0x1,
|
|
||||||
arbitration_id=x.identifier,
|
|
||||||
dlc=x.length,
|
|
||||||
data=bytes(x)[8:])
|
|
||||||
try:
|
|
||||||
x.sent_time = time.time()
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
self.iface.send(msg)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def select(sockets, *args, **kwargs):
|
|
||||||
SocketsPool().multiplex_rx_packets()
|
|
||||||
return [s for s in sockets if isinstance(s, PythonCANSocket) and
|
|
||||||
not s.iface.rx_queue.empty()], PythonCANSocket.recv
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
if self.closed:
|
|
||||||
return
|
|
||||||
super(PythonCANSocket, self).close()
|
|
||||||
self.iface.shutdown()
|
|
||||||
|
|
||||||
|
|
||||||
CANSocket = PythonCANSocket
|
|
|
@ -1,806 +0,0 @@
|
||||||
% Regression tests for the CANSocket
|
|
||||||
~ vcan_socket
|
|
||||||
|
|
||||||
# More information at http://www.secdev.org/projects/UTscapy/
|
|
||||||
|
|
||||||
|
|
||||||
############
|
|
||||||
############
|
|
||||||
+ Configuration of CAN virtual sockets
|
|
||||||
|
|
||||||
= Load module
|
|
||||||
~ conf command
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
conf.contribs['CAN'] = {'swap-bytes': False}
|
|
||||||
load_layer("can")
|
|
||||||
conf.contribs['CANSocket'] = {'use-python-can': True}
|
|
||||||
from scapy.contrib.cansocket_python_can import *
|
|
||||||
import can
|
|
||||||
|
|
||||||
= Setup string for vcan
|
|
||||||
~ conf command
|
|
||||||
bashCommand = "/bin/bash -c 'sudo modprobe vcan; sudo ip link add name vcan0 type vcan; sudo ip link set dev vcan0 up'"
|
|
||||||
|
|
||||||
= Load os
|
|
||||||
~ conf command
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
= Setup vcan0
|
|
||||||
~ conf command
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
0 == os.system(bashCommand)
|
|
||||||
|
|
||||||
+ Basic Packet Tests()
|
|
||||||
= CAN Packet init
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
canframe = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
bytes(canframe) == b'\x00\x00\x07\xff\x08\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08'
|
|
||||||
|
|
||||||
+ Basic Socket Tests()
|
|
||||||
= CAN Socket Init
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
|
|
||||||
= CAN Socket send recv small packet
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x7ff,length=1,data=b'\x01'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx.show()
|
|
||||||
bytes(rx)
|
|
||||||
rx == CAN(identifier=0x7ff,length=1,data=b'\x01')
|
|
||||||
|
|
||||||
|
|
||||||
= CAN Socket send recv ISOTP_Packet
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
from scapy.contrib.isotp import ISOTPHeader, ISOTP_FF
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
pkt = ISOTPHeader(identifier=0x7ff)/ISOTP_FF(message_size=100, data=b'abcdef')
|
|
||||||
pkt.show()
|
|
||||||
sock2.send(pkt)
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx.show()
|
|
||||||
bytes(rx)
|
|
||||||
rx == CAN(identifier=0x7ff,length=8,data=b'\x10\x64abcdef')
|
|
||||||
|
|
||||||
|
|
||||||
= CAN Socket basecls test
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
sock1.basecls = Raw
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx == Raw(bytes(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')))
|
|
||||||
sock1.basecls = CAN
|
|
||||||
|
|
||||||
= CAN Socket send recv
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
thread.start()
|
|
||||||
|
|
||||||
rx = sock1.recv()
|
|
||||||
rx == CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x7ff
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 3
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x700
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, can_filters=[{'can_id': 0x200, 'can_mask': 0x700}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x212, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x2ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x2aa, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x0ff
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, can_filters=[{'can_id': 0x200, 'can_mask': 0xff}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x301, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x1ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with multiple filters
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000,
|
|
||||||
can_filters=[{'can_id': 0x200, 'can_mask': 0x7ff},
|
|
||||||
{'can_id': 0x400, 'can_mask': 0x7ff},
|
|
||||||
{'can_id': 0x600, 'can_mask': 0x7ff},
|
|
||||||
{'can_id': 0x7ff, 'can_mask': 0x7ff}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x400, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x500, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x600, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x700, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x7ff, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x7ff and inverse filter
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000,
|
|
||||||
can_filters=[{'can_id': 0x200 | CAN_INV_FILTER, 'can_mask': 0x7ff}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x300, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x100, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(identifier=0x200, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 2
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x1FFFFFFF
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000,
|
|
||||||
can_filters=[{'can_id': 0x10000000, 'can_mask': 0x1fffffff}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 2
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= sniff with filtermask 0x1FFFFFFF and inverse filter
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000,
|
|
||||||
can_filters=[{'can_id': 0x10000000 | CAN_INV_FILTER, 'can_mask': 0x1fffffff}]))
|
|
||||||
|
|
||||||
def sender():
|
|
||||||
sock2 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock2.close()
|
|
||||||
|
|
||||||
thread = threading.Thread(target=sender)
|
|
||||||
|
|
||||||
packets = sock1.sniff(timeout=0.1, started_callback=thread.start)
|
|
||||||
len(packets) == 4
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= CAN Socket sr1 with receive own messages
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, receive_own_messages=True))
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
rx = None
|
|
||||||
rx = sock1.sr1(tx)
|
|
||||||
tx == rx
|
|
||||||
#this test can be enabled after issue #1199 is fixed
|
|
||||||
#tx.sent_time < rx.time and tx == rx and rx.time > 0
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
= srcan
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
rx = None
|
|
||||||
rx = srcan(tx, iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, receive_own_messages=True), timeout=1)
|
|
||||||
tx == rx[0][0][1]
|
|
||||||
|
|
||||||
= srcan basecls
|
|
||||||
~ needs_root
|
|
||||||
~ linux
|
|
||||||
|
|
||||||
tx = CAN(identifier=0x7ff,length=8,data=b'\x01\x02\x03\x04\x05\x06\x07\x08')
|
|
||||||
rx = None
|
|
||||||
rx = srcan(tx, iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000, receive_own_messages=True), timeout=1, basecls=Raw)
|
|
||||||
Raw(bytes(tx)) == rx[0][0][1]
|
|
||||||
|
|
||||||
|
|
||||||
+ bridge and sniff tests
|
|
||||||
|
|
||||||
= bridge and sniff setup vcan1 package forwarding
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
bashCommand = "/bin/bash -c 'sudo ip link add name vcan1 type vcan; sudo ip link set dev vcan1 up'"
|
|
||||||
0 == os.system(bashCommand)
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan1', bitrate=250000))
|
|
||||||
|
|
||||||
def senderVCan0():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridge():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridge)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan0)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock1.close()
|
|
||||||
sock0.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
= bridge and sniff setup vcan0 package forwarding
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000))
|
|
||||||
sock1 = CANSocket(iface=can.interface.Bus(bustype='socketcan', channel='vcan1', bitrate=250000))
|
|
||||||
|
|
||||||
def senderVCan1():
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridge():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridge)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan1)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 vcan1 package forwarding both directions
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
|
|
||||||
|
|
||||||
def senderBothVCans():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x25, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x20, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x30, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x80, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x40, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridge():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridge)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderBothVCans)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.3)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan1 package change
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000, can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]))
|
|
||||||
|
|
||||||
def senderVCan0():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridgeWithPackageChangeVCan0ToVCan1():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01'
|
|
||||||
pkt.identifier = 0x10010000
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan0ToVCan1)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan0)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 package change
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000, can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]))
|
|
||||||
|
|
||||||
def senderVCan1():
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridgeWithPackageChangeVCan1ToVCan0():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01'
|
|
||||||
pkt.identifier = 0x10010000
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithPackageChangeVCan1ToVCan0)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan1)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 and vcan1 package change in both directions
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000, can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]))
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000, can_filters=[{'can_id': 0x10010000, 'can_mask': 0x1fffffff}]))
|
|
||||||
|
|
||||||
def senderBothVCans():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridgeWithPackageChangeBothDirections():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
pkt.data = b'\x08\x07\x06\x05\x04\x03\x02\x01'
|
|
||||||
pkt.identifier = 0x10010000
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithPackageChangeBothDirections)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderBothVCans)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.3)
|
|
||||||
len(packetsVCan0) == 4
|
|
||||||
len(packetsVCan1) == 6
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 package remove
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
|
|
||||||
def senderVCan0():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridgeWithRemovePackageFromVCan0ToVCan1():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
if(pkt.identifier == 0x10020000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan0ToVCan1)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan0)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
|
|
||||||
len(packetsVCan1) == 5
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan1 package remove
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
|
|
||||||
def senderVCan1():
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridgeWithRemovePackageFromVCan1ToVCan0():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnr(pkt):
|
|
||||||
if(pkt.identifier == 0x10050000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm21=pnr, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithRemovePackageFromVCan1ToVCan0)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderVCan1)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
|
|
||||||
len(packetsVCan0) == 3
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
||||||
threadBridge.join()
|
|
||||||
threadSender.join()
|
|
||||||
|
|
||||||
|
|
||||||
=bridge and sniff setup vcan0 and vcan1 package remove both directions
|
|
||||||
~ needs_root linux
|
|
||||||
|
|
||||||
sock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
sock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
|
|
||||||
def senderBothVCans():
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10020000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10030000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10040000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock0.send(CAN(flags='extended', identifier=0x10000000, length=8, data=b'\x01\x02\x03\x04\x05\x06\x07\x08'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10050000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
sock1.send(CAN(flags='extended', identifier=0x10010000, length=8, data=b'\x01\x02\x03\x04\x05\x04\x05\x06'))
|
|
||||||
|
|
||||||
bridgeStarted = threading.Event()
|
|
||||||
def bridgeWithRemovePackageInBothDirections():
|
|
||||||
global bridgeStarted
|
|
||||||
bSock0 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan0',
|
|
||||||
bitrate=250000))
|
|
||||||
bSock1 = CANSocket(
|
|
||||||
iface=can.interface.Bus(bustype='socketcan', channel='vcan1',
|
|
||||||
bitrate=250000))
|
|
||||||
def pnrA(pkt):
|
|
||||||
if(pkt.identifier == 0x10020000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
def pnrB(pkt):
|
|
||||||
if (pkt.identifier == 0x10050000):
|
|
||||||
pkt = None
|
|
||||||
else:
|
|
||||||
pkt = pkt
|
|
||||||
return pkt
|
|
||||||
bSock0.timeout = 0.01
|
|
||||||
bSock1.timeout = 0.01
|
|
||||||
bridge_and_sniff(if1=bSock0, if2=bSock1, xfrm12=pnrA, xfrm21=pnrB, timeout=0.5, started_callback=bridgeStarted.set)
|
|
||||||
bSock0.close()
|
|
||||||
bSock1.close()
|
|
||||||
|
|
||||||
threadBridge = threading.Thread(target=bridgeWithRemovePackageInBothDirections)
|
|
||||||
threadBridge.start()
|
|
||||||
threadSender = threading.Thread(target=senderBothVCans)
|
|
||||||
bridgeStarted.wait()
|
|
||||||
|
|
||||||
packetsVCan0 = sock0.sniff(timeout=0.3, started_callback=threadSender.start)
|
|
||||||
packetsVCan1 = sock1.sniff(timeout=0.3)
|
|
||||||
|
|
||||||
len(packetsVCan0) == 3
|
|
||||||
len(packetsVCan1) == 5
|
|
||||||
|
|
||||||
sock0.close()
|
|
||||||
sock1.close()
|
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
# 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 = Common Address Redundancy Protocol (CARP)
|
|
||||||
# scapy.contrib.status = loads
|
|
||||||
|
|
||||||
import struct
|
|
||||||
import hmac
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
from scapy.packet import Packet, split_layers, bind_layers
|
|
||||||
from scapy.layers.inet import IP
|
|
||||||
from scapy.fields import BitField, ByteField, XShortField, XIntField
|
|
||||||
from scapy.layers.vrrp import IPPROTO_VRRP, VRRP, VRRPv3
|
|
||||||
from scapy.utils import checksum, inet_aton
|
|
||||||
from scapy.error import warning
|
|
||||||
|
|
||||||
|
|
||||||
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", None),
|
|
||||||
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 is None:
|
|
||||||
pkt = pkt[:6] + struct.pack("!H", checksum(pkt)) + pkt[8:]
|
|
||||||
|
|
||||||
return pkt
|
|
||||||
|
|
||||||
def build_hmac_sha1(self, pw=b'\x00' * 20, ip4l=[], ip6l=[]):
|
|
||||||
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 # noqa: E501
|
|
||||||
h.update(b'\x21')
|
|
||||||
# XXX: mac addy if different from special link layer. comes before vhid
|
|
||||||
h.update(struct.pack('!B', self.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()
|
|
||||||
|
|
||||||
|
|
||||||
warning("CARP overwrites VRRP !")
|
|
||||||
# This cancel the bindings done in vrrp.py
|
|
||||||
split_layers(IP, VRRP, proto=IPPROTO_VRRP)
|
|
||||||
split_layers(IP, VRRPv3, proto=IPPROTO_VRRP)
|
|
||||||
# CARP bindings
|
|
||||||
bind_layers(IP, CARP, proto=112, dst='224.0.0.18')
|
|
|
@ -1,11 +0,0 @@
|
||||||
% Regression tests for the avs module
|
|
||||||
|
|
||||||
+ Basic CARP test
|
|
||||||
|
|
||||||
= Build
|
|
||||||
|
|
||||||
pkt = Ether()/IP()/CARP()
|
|
||||||
pkt = Ether(raw(pkt))
|
|
||||||
assert CARP in pkt
|
|
||||||
assert pkt[CARP].chksum
|
|
||||||
assert pkt[CARP].build_hmac_sha1(ip4l=['192.168.0.111']) == b'\xbd\x82\xc7\x8f6\x1a\x0e\xff\xcfl\x14\xa2v\xedW;>ic\xa3'
|
|
|
@ -1,398 +0,0 @@
|
||||||
# scapy.contrib.description = Cisco Discovery Protocol (CDP)
|
|
||||||
# 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
|
|
||||||
import struct
|
|
||||||
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.fields import ByteEnumField, ByteField, FieldLenField, FlagsField, \
|
|
||||||
IP6Field, IPField, PacketListField, ShortField, StrLenField, \
|
|
||||||
X3BytesField, XByteField, XShortEnumField, XShortField
|
|
||||||
from scapy.layers.inet import checksum
|
|
||||||
from scapy.layers.l2 import SNAP
|
|
||||||
from scapy.compat import orb, chb
|
|
||||||
from scapy.modules.six.moves import range
|
|
||||||
from scapy.config import conf
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################################
|
|
||||||
# Helpers and constants
|
|
||||||
#####################################################################
|
|
||||||
|
|
||||||
# CDP TLV classes keyed by type
|
|
||||||
_cdp_tlv_cls = {0x0001: "CDPMsgDeviceID",
|
|
||||||
0x0002: "CDPMsgAddr",
|
|
||||||
0x0003: "CDPMsgPortID",
|
|
||||||
0x0004: "CDPMsgCapabilities",
|
|
||||||
0x0005: "CDPMsgSoftwareVersion",
|
|
||||||
0x0006: "CDPMsgPlatform",
|
|
||||||
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]
|
|
||||||
if t == 0x0007 and len(p) > 4:
|
|
||||||
tmp_len = struct.unpack("!H", p[2:4])[0]
|
|
||||||
if tmp_len == 8:
|
|
||||||
clsname = "CDPMsgIPGateway"
|
|
||||||
else:
|
|
||||||
clsname = "CDPMsgIPPrefix"
|
|
||||||
else:
|
|
||||||
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",
|
|
||||||
adjust=lambda pkt, x: x + 4),
|
|
||||||
StrLenField("val", "", length_from=lambda x:x.len - 4,
|
|
||||||
max_length=65531)]
|
|
||||||
|
|
||||||
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,
|
|
||||||
max_length=255),
|
|
||||||
FieldLenField("addrlen", None, length_of=lambda x:x.addr),
|
|
||||||
StrLenField("addr", None, length_from=lambda x:x.addrlen,
|
|
||||||
max_length=65535)]
|
|
||||||
|
|
||||||
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, max_length=255),
|
|
||||||
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, max_length=255),
|
|
||||||
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,
|
|
||||||
length_from=lambda x:x.len - 8)]
|
|
||||||
|
|
||||||
def post_build(self, pkt, pay):
|
|
||||||
if self.len is None:
|
|
||||||
tmp_len = 8 + len(self.addr) * 9
|
|
||||||
pkt = pkt[:2] + struct.pack("!H", tmp_len) + 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",
|
|
||||||
adjust=lambda pkt, x: x + 4),
|
|
||||||
StrLenField("iface", "Port 1", length_from=lambda x:x.len - 4)] # noqa: E501
|
|
||||||
|
|
||||||
|
|
||||||
_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 CDPMsgIPGateway(CDPMsgGeneric):
|
|
||||||
name = "IP Gateway"
|
|
||||||
type = 0x0007
|
|
||||||
fields_desc = [XShortEnumField("type", 0x0007, _cdp_tlv_types),
|
|
||||||
ShortField("len", 8),
|
|
||||||
IPField("defaultgw", "192.168.0.1")]
|
|
||||||
|
|
||||||
|
|
||||||
class CDPMsgIPPrefix(CDPMsgGeneric):
|
|
||||||
name = "IP Prefix"
|
|
||||||
type = 0x0007
|
|
||||||
fields_desc = [XShortEnumField("type", 0x0007, _cdp_tlv_types),
|
|
||||||
ShortField("len", 9),
|
|
||||||
IPField("prefix", "192.168.0.1"),
|
|
||||||
ByteField("plen", 24)]
|
|
||||||
|
|
||||||
|
|
||||||
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),
|
|
||||||
FieldLenField("len", None, "unknown2", fmt="!H",
|
|
||||||
adjust=lambda pkt, x: x + 7),
|
|
||||||
XByteField("unknown1", 0),
|
|
||||||
ShortField("vlan", 1),
|
|
||||||
# TLV length (len) - 2 (type) - 2 (len) - 1 (unknown1) - 2 (vlan) # noqa: E501
|
|
||||||
StrLenField("unknown2", "", length_from=lambda p: p.len - 7,
|
|
||||||
max_length=65528)]
|
|
||||||
|
|
||||||
|
|
||||||
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", fmt="!H",
|
|
||||||
adjust=lambda pkt, x: x + 4),
|
|
||||||
StrLenField("val", "", length_from=lambda x:x.len - 4,
|
|
||||||
max_length=65531)]
|
|
||||||
|
|
||||||
|
|
||||||
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 = orb(pkt[-1])
|
|
||||||
if last_chr <= 0x80:
|
|
||||||
return pkt[:-1] + b'\x00' + chb(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})
|
|
|
@ -1,71 +0,0 @@
|
||||||
#################################### 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 - Rebuild (1)
|
|
||||||
|
|
||||||
cdpv2.cksum = None
|
|
||||||
assert raw(cdpv2) == s
|
|
||||||
|
|
||||||
= 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)
|
|
||||||
|
|
||||||
assert cdpv2[CDPMsgPower].sprintf("%power%") == '6300 mW'
|
|
||||||
|
|
||||||
= CDPv2 - Rebuild (2)
|
|
||||||
|
|
||||||
cdpv2.cksum = None
|
|
||||||
s2 = s[:2] + b"\xf3\xf1" + s[4:]
|
|
||||||
assert raw(cdpv2) == s2
|
|
|
@ -1,60 +0,0 @@
|
||||||
# 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 # noqa: E501
|
|
||||||
|
|
||||||
from scapy.data import DLT_C_HDLC
|
|
||||||
from scapy.packet import Packet, bind_layers
|
|
||||||
from scapy.fields import ByteEnumField, ByteField, ConditionalField, \
|
|
||||||
IntEnumField, IntField, IPField, XShortField
|
|
||||||
from scapy.layers.l2 import Dot3, STP
|
|
||||||
from scapy.layers.inet import IP
|
|
||||||
from scapy.layers.inet6 import IPv6
|
|
||||||
from scapy.config import conf
|
|
||||||
|
|
||||||
|
|
||||||
class CHDLC(Packet):
|
|
||||||
name = "Cisco HDLC"
|
|
||||||
fields_desc = [ByteEnumField("address", 0x0f, {0x0f: "unicast", 0x8f: "multicast"}), # noqa: E501
|
|
||||||
ByteField("control", 0),
|
|
||||||
XShortField("proto", 0x0800)]
|
|
||||||
|
|
||||||
|
|
||||||
class SLARP(Packet):
|
|
||||||
name = "SLARP"
|
|
||||||
fields_desc = [IntEnumField("type", 2, {0: "request", 1: "reply", 2: "line keepalive"}), # noqa: E501
|
|
||||||
ConditionalField(IPField("address", "192.168.0.1"),
|
|
||||||
lambda pkt: pkt.type == 0 or pkt.type == 1), # noqa: E501
|
|
||||||
ConditionalField(IPField("mask", "255.255.255.0"),
|
|
||||||
lambda pkt: pkt.type == 0 or pkt.type == 1), # noqa: E501
|
|
||||||
ConditionalField(XShortField("unused", 0),
|
|
||||||
lambda pkt: pkt.type == 0 or pkt.type == 1), # noqa: E501
|
|
||||||
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)
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue