86890704fd
todo: add documentation & wireshark dissector
2053 lines
59 KiB
Text
Executable file
2053 lines
59 KiB
Text
Executable file
% ISOTP Tests
|
|
~ vcan_socket
|
|
* Tests for ISOTP
|
|
|
|
|
|
+ Configuration
|
|
~ conf
|
|
|
|
= Imports
|
|
~ conf
|
|
|
|
load_layer("can")
|
|
import threading, time, six, subprocess
|
|
from six.moves.queue import Queue
|
|
from subprocess import call
|
|
|
|
|
|
= Definition of constants, utility functions and mock classes
|
|
~ conf
|
|
|
|
iface0 = "vcan0"
|
|
iface1 = "vcan1"
|
|
|
|
|
|
class MockCANSocket(SuperSocket):
|
|
def __init__(self, rcvd_queue=None):
|
|
self.rcvd_queue = Queue()
|
|
self.sent_queue = Queue()
|
|
if rcvd_queue is not None:
|
|
for c in rcvd_queue:
|
|
self.rcvd_queue.put(c)
|
|
def recv_raw(self, x=MTU):
|
|
pkt = bytes(self.rcvd_queue.get(True, 2))
|
|
return CAN, pkt, None
|
|
def send(self, p):
|
|
self.sent_queue.put(p)
|
|
@staticmethod
|
|
def select(sockets, remain=None):
|
|
return sockets, None
|
|
|
|
|
|
# utility function that waits on list l for n elements, timing out if nothing is added for 1 second
|
|
def list_wait(l, n):
|
|
old_len = 0
|
|
c = 0
|
|
while len(l) < n:
|
|
if c > 100:
|
|
return False
|
|
if len(l) == old_len:
|
|
time.sleep(0.01)
|
|
c += 1
|
|
else:
|
|
old_len = len(l)
|
|
c = 0
|
|
|
|
# hexadecimal to bytes convenience function
|
|
if six.PY2:
|
|
dhex = lambda s: "".join(s.split()).decode('hex')
|
|
else:
|
|
dhex = bytes.fromhex
|
|
|
|
|
|
# function to exit when the can-isotp kernel module is not available
|
|
ISOTP_KERNEL_MODULE_AVAILABLE = False
|
|
def exit_if_no_isotp_module():
|
|
if not ISOTP_KERNEL_MODULE_AVAILABLE:
|
|
err = "TEST SKIPPED: can-isotp not available"
|
|
subprocess.call("printf \"%s\r\n\" > /dev/stderr" % err, shell=True)
|
|
warning("Can't test ISOTP native socket because kernel module is not loaded")
|
|
exit(0)
|
|
|
|
|
|
= 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")
|
|
|
|
if 0 != call("cansend %s 000#" % iface1, shell=True):
|
|
# vcan1 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" % iface1, shell=True):
|
|
print("add %s failed: Maybe it was already up?" % iface1)
|
|
if 0 != call("sudo ip link set dev %s up" % iface1, shell=True):
|
|
raise Exception("could not bring up %s" % iface1)
|
|
|
|
if 0 != call("cansend %s 000#" % iface1, shell=True):
|
|
raise Exception("cansend doesn't work")
|
|
|
|
print("CAN should work now")
|
|
|
|
|
|
if six.PY3:
|
|
from scapy.contrib.cansocket_native import *
|
|
else:
|
|
from scapy.contrib.cansocket_python_can import *
|
|
|
|
|
|
if "python_can" in CANSocket.__module__:
|
|
import can as python_can
|
|
new_can_socket = lambda iface: CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel=iface, bitrate=250000))
|
|
new_can_socket0 = lambda: CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel=iface0, bitrate=250000), timeout=0.01)
|
|
new_can_socket1 = lambda: CANSocket(iface=python_can.interface.Bus(bustype='socketcan', channel=iface1, bitrate=250000), timeout=0.01)
|
|
else:
|
|
new_can_socket = lambda iface: CANSocket(iface)
|
|
new_can_socket0 = lambda: CANSocket(iface0)
|
|
new_can_socket1 = lambda: CANSocket(iface1)
|
|
|
|
# utility function for draining a can interface, asserting that no packets are there
|
|
def drain_bus(iface=iface0, assert_empty=True):
|
|
s = new_can_socket(iface)
|
|
pkts = s.sniff(timeout=0.1)
|
|
if assert_empty:
|
|
assert len(pkts) == 0
|
|
s.close()
|
|
|
|
print("CAN sockets should work now")
|
|
|
|
|
|
# Verify that a CAN socket can be created and closed
|
|
~ conf linux needs_root
|
|
s = new_can_socket(iface0)
|
|
s.close()
|
|
|
|
|
|
= Check if can-isotp and can-utils are installed on this system
|
|
~ linux
|
|
p = subprocess.Popen('lsmod | grep "^can_isotp"', stdout = subprocess.PIPE, shell=True)
|
|
if p.wait() == 0:
|
|
if b"can_isotp" in p.stdout.read():
|
|
p = subprocess.Popen("isotpsend -s1 -d0 %s" % iface0, stdin = subprocess.PIPE, shell=True)
|
|
p.stdin.write(b"01")
|
|
p.stdin.close()
|
|
r = p.wait()
|
|
if r == 0:
|
|
ISOTP_KERNEL_MODULE_AVAILABLE = True
|
|
|
|
|
|
+ Syntax check
|
|
|
|
= Import isotp
|
|
conf.contribs['ISOTP'] = {'use-can-isotp-kernel-module': False}
|
|
load_contrib("isotp")
|
|
|
|
|
|
+ ISOTP packet check
|
|
|
|
= Creation of an empty ISOTP packet
|
|
p = ISOTP()
|
|
assert(p.data == b"")
|
|
assert(p.src is None and p.dst is None and p.exsrc is None and p.exdst is None)
|
|
assert(bytes(p) == b"")
|
|
|
|
= Creation of a simple ISOTP packet with source
|
|
p = ISOTP(b"eee", src=0x241)
|
|
assert(p.src == 0x241)
|
|
assert(p.data == b"eee")
|
|
assert(bytes(p) == b"eee")
|
|
|
|
+ ISOTPFrame related checks
|
|
|
|
= Build a packet with extended addressing
|
|
|
|
pkt = CAN(identifier=0x123, data=b'\x42\x10\xff\xde\xea\xdd\xaa\xaa')
|
|
isotpex = ISOTPHeaderEA(bytes(pkt))
|
|
assert(isotpex.type == 1)
|
|
assert(isotpex.message_size == 0xff)
|
|
assert(isotpex.extended_address == 0x42)
|
|
assert(isotpex.identifier == 0x123)
|
|
assert(isotpex.length == 8)
|
|
|
|
|
|
= Build a packet with normal addressing
|
|
|
|
pkt = CAN(identifier=0x123, data=b'\x10\xff\xde\xea\xdd\xaa\xaa')
|
|
isotpno = ISOTPHeader(bytes(pkt))
|
|
assert(isotpno.type == 1)
|
|
assert(isotpno.message_size == 0xff)
|
|
assert(isotpno.identifier == 0x123)
|
|
assert(isotpno.length == 7)
|
|
|
|
|
|
= Compare both isotp payloads
|
|
|
|
assert(isotpno.data == isotpex.data)
|
|
assert(isotpno.message_size == isotpex.message_size)
|
|
|
|
|
|
= Dissect multiple packets
|
|
|
|
frames = \
|
|
[b'\x00\x00\x00\x00\x08\x00\x00\x00\x10(\xde\xad\xbe\xef\xde\xad',
|
|
b'\x00\x00\x00\x00\x08\x00\x00\x00!\xbe\xef\xde\xad\xbe\xef\xde',
|
|
b'\x00\x00\x00\x00\x08\x00\x00\x00"\xad\xbe\xef\xde\xad\xbe\xef',
|
|
b'\x00\x00\x00\x00\x08\x00\x00\x00#\xde\xad\xbe\xef\xde\xad\xbe',
|
|
b'\x00\x00\x00\x00\x08\x00\x00\x00$\xef\xde\xad\xbe\xef\xde\xad',
|
|
b'\x00\x00\x00\x00\x07\x00\x00\x00%\xbe\xef\xde\xad\xbe\xef']
|
|
|
|
isotpframes = [ISOTPHeader(x) for x in frames]
|
|
|
|
assert(isotpframes[0].type == 1)
|
|
assert(isotpframes[0].message_size == 40)
|
|
assert(isotpframes[0].length == 8)
|
|
assert(isotpframes[1].type == 2)
|
|
assert(isotpframes[1].index == 1)
|
|
assert(isotpframes[1].length == 8)
|
|
assert(isotpframes[2].type == 2)
|
|
assert(isotpframes[2].index == 2)
|
|
assert(isotpframes[2].length == 8)
|
|
assert(isotpframes[3].type == 2)
|
|
assert(isotpframes[3].index == 3)
|
|
assert(isotpframes[3].length == 8)
|
|
assert(isotpframes[4].type == 2)
|
|
assert(isotpframes[4].index == 4)
|
|
assert(isotpframes[4].length == 8)
|
|
assert(isotpframes[5].type == 2)
|
|
assert(isotpframes[5].index == 5)
|
|
assert(isotpframes[5].length == 7)
|
|
|
|
= Build SF frame with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_SF(data=b'\xad\xbe\xad\xff')))
|
|
assert(p.length == 5)
|
|
assert(p.message_size == 4)
|
|
assert(len(p.data) == 4)
|
|
assert(p.data == b'\xad\xbe\xad\xff')
|
|
assert(p.type == 0)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build SF frame EA with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_SF(data=b'\xad\xbe\xad\xff')))
|
|
assert(p.extended_address == 0)
|
|
assert(p.length == 6)
|
|
assert(p.message_size == 4)
|
|
assert(len(p.data) == 4)
|
|
assert(p.data == b'\xad\xbe\xad\xff')
|
|
assert(p.type == 0)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build FF frame with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_FF(message_size=10, data=b'\xad\xbe\xad\xff')))
|
|
assert(p.length == 6)
|
|
assert(p.message_size == 10)
|
|
assert(len(p.data) == 4)
|
|
assert(p.data == b'\xad\xbe\xad\xff')
|
|
assert(p.type == 1)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build FF frame EA with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_FF(message_size=10, data=b'\xad\xbe\xad\xff')))
|
|
assert(p.extended_address == 0)
|
|
assert(p.length == 7)
|
|
assert(p.message_size == 10)
|
|
assert(len(p.data) == 4)
|
|
assert(p.data == b'\xad\xbe\xad\xff')
|
|
assert(p.type == 1)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build FF frame EA, extended size, with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_FF(message_size=0,
|
|
extended_message_size=2000,
|
|
data=b'\xad')))
|
|
assert(p.extended_address == 0)
|
|
assert(p.length == 8)
|
|
assert(p.message_size == 0)
|
|
assert(p.extended_message_size == 2000)
|
|
assert(len(p.data) == 1)
|
|
assert(p.data == b'\xad')
|
|
assert(p.type == 1)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build FF frame, extended size, with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_FF(message_size=0,
|
|
extended_message_size=2000,
|
|
data=b'\xad')))
|
|
assert(p.length == 7)
|
|
assert(p.message_size == 0)
|
|
assert(p.extended_message_size == 2000)
|
|
assert(len(p.data) == 1)
|
|
assert(p.data == b'\xad')
|
|
assert(p.type == 1)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build CF frame with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_CF(data=b'\xad')))
|
|
assert(p.length == 2)
|
|
assert(p.index == 0)
|
|
assert(len(p.data) == 1)
|
|
assert(p.data == b'\xad')
|
|
assert(p.type == 2)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build CF frame EA with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_CF(data=b'\xad')))
|
|
assert(p.length == 3)
|
|
assert(p.index == 0)
|
|
assert(len(p.data) == 1)
|
|
assert(p.data == b'\xad')
|
|
assert(p.type == 2)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build FC frame EA with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeaderEA(bytes(ISOTPHeaderEA()/ISOTP_FC()))
|
|
assert(p.length == 4)
|
|
assert(p.block_size == 0)
|
|
assert(p.separation_time == 0)
|
|
assert(p.type == 3)
|
|
assert(p.identifier == 0)
|
|
|
|
= Build FC frame with constructor, check for correct length assignments
|
|
|
|
p = ISOTPHeader(bytes(ISOTPHeader()/ISOTP_FC()))
|
|
assert(p.length == 3)
|
|
assert(p.block_size == 0)
|
|
assert(p.separation_time == 0)
|
|
assert(p.type == 3)
|
|
assert(p.identifier == 0)
|
|
|
|
= Construct some single frames
|
|
|
|
p = ISOTPHeader(identifier=0x123, length=5)/ISOTP_SF(message_size=4, data=b'abcd')
|
|
assert(p.length == 5)
|
|
assert(p.identifier == 0x123)
|
|
assert(p.type == 0)
|
|
assert(p.message_size == 4)
|
|
assert(p.data == b'abcd')
|
|
|
|
= Construct some single frames EA
|
|
|
|
p = ISOTPHeaderEA(identifier=0x123, length=6, extended_address=42)/ISOTP_SF(message_size=4, data=b'abcd')
|
|
assert(p.length == 6)
|
|
assert(p.extended_address == 42)
|
|
assert(p.identifier == 0x123)
|
|
assert(p.type == 0)
|
|
assert(p.message_size == 4)
|
|
assert(p.data == b'abcd')
|
|
|
|
|
|
+ ISOTP fragment and defragment checks
|
|
|
|
= Fragment an empty ISOTP message
|
|
fragments = ISOTP().fragment()
|
|
assert(len(fragments) == 1)
|
|
assert(fragments[0].data == b"\0")
|
|
|
|
= Fragment another empty ISOTP message
|
|
fragments = ISOTP("").fragment()
|
|
assert(len(fragments) == 1)
|
|
assert(fragments[0].data == b"\0")
|
|
|
|
= Fragment a 4 bytes long ISOTP message
|
|
fragments = ISOTP("data", src=0x241).fragment()
|
|
assert(len(fragments) == 1)
|
|
assert(isinstance(fragments[0], CAN))
|
|
fragment = CAN(bytes(fragments[0]))
|
|
assert(fragment.data == b"\x04data")
|
|
assert(fragment.flags == 0)
|
|
assert(fragment.length == 5)
|
|
assert(fragment.reserved == 0)
|
|
|
|
= Fragment a 7 bytes long ISOTP message
|
|
fragments = ISOTP("abcdefg").fragment()
|
|
assert(len(fragments) == 1)
|
|
assert(fragments[0].data == b"\x07abcdefg")
|
|
|
|
= Fragment a 8 bytes long ISOTP message
|
|
fragments = ISOTP("abcdefgh").fragment()
|
|
assert(len(fragments) == 2)
|
|
assert(fragments[0].data == b"\x10\x08abcdef")
|
|
assert(fragments[1].data == b"\x21gh")
|
|
|
|
= Fragment an ISOTP message with extended addressing
|
|
isotp = ISOTP("abcdef", exdst=ord('A'))
|
|
fragments = isotp.fragment()
|
|
assert(len(fragments) == 1)
|
|
assert(fragments[0].data == b"A\x06abcdef")
|
|
|
|
= Fragment a 7 bytes ISOTP message with destination identifier
|
|
isotp = ISOTP("abcdefg", dst=0x64f)
|
|
fragments = isotp.fragment()
|
|
assert(len(fragments) == 1)
|
|
assert(fragments[0].data == b"\x07abcdefg")
|
|
assert(fragments[0].identifier == 0x64f)
|
|
|
|
= Fragment a 16 bytes ISOTP message with extended addressing
|
|
isotp = ISOTP("abcdefghijklmnop", dst=0x64f, exdst=ord('A'))
|
|
fragments = isotp.fragment()
|
|
assert(len(fragments) == 3)
|
|
assert(fragments[0].data == b"A\x10\x10abcde")
|
|
assert(fragments[1].data == b"A\x21fghijk")
|
|
assert(fragments[2].data == b"A\x22lmnop")
|
|
assert(fragments[0].identifier == 0x64f)
|
|
assert(fragments[1].identifier == 0x64f)
|
|
assert(fragments[2].identifier == 0x64f)
|
|
|
|
= Fragment a huge ISOTP message, 4997 bytes long
|
|
data = b"T" * 4997
|
|
isotp = ISOTP(b"T" * 4997, dst=0x345)
|
|
fragments = isotp.fragment()
|
|
assert(len(fragments) == 715)
|
|
assert(fragments[0].data == dhex("10 00 00 00 13 85") + b"TT")
|
|
assert(fragments[1].data == b"\x21TTTTTTT")
|
|
assert(fragments[-2].data == b"\x29TTTTTTT")
|
|
assert(fragments[-1].data == b"\x2ATTTT")
|
|
|
|
= Defragment a single-frame ISOTP message
|
|
fragments = [CAN(identifier=0x641, data=b"\x04test")]
|
|
isotp = ISOTP.defragment(fragments)
|
|
isotp.show()
|
|
assert(isotp.data == b"test")
|
|
assert(isotp.dst == 0x641)
|
|
|
|
= Defragment an ISOTP message composed of multiple CAN frames
|
|
fragments = [
|
|
CAN(identifier=0x641, data=dhex("41 10 10 61 62 63 64 65")),
|
|
CAN(identifier=0x641, data=dhex("41 21 66 67 68 69 6A 6B")),
|
|
CAN(identifier=0x641, data=dhex("41 22 6C 6D 6E 6F 70 00"))
|
|
]
|
|
isotp = ISOTP.defragment(fragments)
|
|
isotp.show()
|
|
assert(isotp.data == dhex("61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70"))
|
|
assert(isotp.dst == 0x641)
|
|
assert(isotp.exdst == 0x41)
|
|
|
|
= Check if fragmenting a message and defragmenting it back yields the original message
|
|
isotp1 = ISOTP("abcdef", exdst=ord('A'))
|
|
fragments = isotp1.fragment()
|
|
isotp2 = ISOTP.defragment(fragments)
|
|
isotp2.show()
|
|
assert(isotp1 == isotp2)
|
|
|
|
isotp1 = ISOTP("abcdefghijklmnop")
|
|
fragments = isotp1.fragment()
|
|
isotp2 = ISOTP.defragment(fragments)
|
|
isotp2.show()
|
|
assert(isotp1 == isotp2)
|
|
|
|
isotp1 = ISOTP("abcdefghijklmnop", exdst=ord('A'))
|
|
fragments = isotp1.fragment()
|
|
isotp2 = ISOTP.defragment(fragments)
|
|
isotp2.show()
|
|
assert(isotp1 == isotp2)
|
|
|
|
isotp1 = ISOTP("T"*5000, exdst=ord('A'))
|
|
fragments = isotp1.fragment()
|
|
isotp2 = ISOTP.defragment(fragments)
|
|
isotp2.show()
|
|
assert(isotp1 == isotp2)
|
|
|
|
= Defragment an ambiguous CAN frame
|
|
fragments = [CAN(identifier=0x641, data=dhex("02 01 AA"))]
|
|
isotp = ISOTP.defragment(fragments, False)
|
|
isotp.show()
|
|
assert(isotp.data == dhex("01 AA"))
|
|
assert(isotp.exdst == None)
|
|
isotpex = ISOTP.defragment(fragments, True)
|
|
isotpex.show()
|
|
assert(isotpex.data == dhex("AA"))
|
|
assert(isotpex.exdst == 0x02)
|
|
|
|
|
|
|
|
+ Testing ISOTPMessageBuilder
|
|
|
|
= Create ISOTPMessageBuilder
|
|
m = ISOTPMessageBuilder()
|
|
|
|
= Feed packets to machine
|
|
m.feed(CAN(identifier=0x241, data=dhex("10 28 01 02 03 04 05 06")))
|
|
m.feed(CAN(identifier=0x641, data=dhex("30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("21 07 08 09 0A 0B 0C 0D")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("22 0E 0F 10 11 12 13 14")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("23 15 16 17 18 19 1A 1B")))
|
|
m.feed(CAN(identifier=0x641, data=dhex("30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("24 1C 1D 1E 1F 20 21 22")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("25 23 24 25 26 27 28" )))
|
|
|
|
= Verify there is a ready message in the machine
|
|
assert(m.count() == 1)
|
|
|
|
= Extract the message from the machine
|
|
msg = m.pop()
|
|
assert(m.count() == 0)
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is None)
|
|
expected = dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28")
|
|
assert(msg.data == expected)
|
|
|
|
= Verify that no error happens when there is not enough data
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("04 AB CD EF")))
|
|
msg = m.pop()
|
|
assert(msg is None)
|
|
|
|
= Verify that no error happens when there is no data
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("")))
|
|
msg = m.pop()
|
|
assert(msg is None)
|
|
|
|
= Verify a single frame without EA
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("04 AB CD EF 04")))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is None)
|
|
assert(msg.data == dhex("AB CD EF 04"))
|
|
|
|
= Single frame without EA, with excessive bytes in CAN frame
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("03 AB CD EF AB CD EF AB")))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is None)
|
|
assert(msg.data == dhex("AB CD EF"))
|
|
|
|
= Verify a single frame with EA
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("E2 04 01 02 03 04")))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0xE2)
|
|
assert(msg.data == dhex("01 02 03 04"))
|
|
|
|
= Single CAN frame that has 2 valid interpretations
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("04 01 02 03 04")))
|
|
msg = m.pop(0x241, None)
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is None)
|
|
assert(msg.data == dhex("01 02 03 04"))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst == 0x04)
|
|
assert(msg.data == dhex("02"))
|
|
|
|
= Verify multiple frames with EA
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05")))
|
|
m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17")))
|
|
m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" )))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0xEA)
|
|
assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28"))
|
|
|
|
= Verify that an EA starting with 1 will still work
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("1A 10 14 01 02 03 04 05")))
|
|
m.feed(CAN(identifier=0x641, data=dhex("1A 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("1A 21 06 07 08 09 0A 0B")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("1A 22 0C 0D 0E 0F 10 11")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("1A 23 12 13 14 15 16 17")))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0x1A)
|
|
assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14"))
|
|
|
|
= Verify that an EA of 07 will still work
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("07 10 0A 01 02 03 04 05")))
|
|
m.feed(CAN(identifier=0x641, data=dhex("07 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("07 21 06 07 08 09 0A 0B")))
|
|
msg = m.pop(0x241, 0x07)
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0x07)
|
|
assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A"))
|
|
|
|
= Verify that three interleaved messages can be sniffed simultaneously on the same identifier and extended address (very unrealistic)
|
|
m = ISOTPMessageBuilder()
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 10 28 01 02 03 04 05"))) # start of message A
|
|
m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 21 06 07 08 09 0A 0B")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 22 0C 0D 0E 0F 10 11")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 23 12 13 14 15 16 17")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 10 10 31 32 33 34 35"))) # start of message B
|
|
m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 03 A6 A7 A8" ))) # single-frame message C
|
|
m.feed(CAN(identifier=0x641, data=dhex("EA 30 03 00" )))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 24 18 19 1A 1B 1C 1D")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 21 36 37 38 39 3A 3B")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 22 3C 3D 3E 3F 40" ))) # end of message B
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 25 1E 1F 20 21 22 23")))
|
|
m.feed(CAN(identifier=0x241, data=dhex("EA 26 24 25 26 27 28" ))) # end of message A
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0xEA)
|
|
assert(msg.data == dhex("A6 A7 A8"))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0xEA)
|
|
assert(msg.data == dhex("31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40"))
|
|
msg = m.pop()
|
|
assert(msg.dst == 0x241)
|
|
assert(msg.exdst is 0xEA)
|
|
assert(msg.data == dhex("01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28"))
|
|
|
|
|
|
+ Test sniffer
|
|
= Test sniffer with multiple frames
|
|
~ linux needs_root
|
|
|
|
|
|
test_frames = [
|
|
(0x241, "EA 10 28 01 02 03 04 05"),
|
|
(0x641, "EA 30 03 00" ),
|
|
(0x241, "EA 21 06 07 08 09 0A 0B"),
|
|
(0x241, "EA 22 0C 0D 0E 0F 10 11"),
|
|
(0x241, "EA 23 12 13 14 15 16 17"),
|
|
(0x641, "EA 30 03 00" ),
|
|
(0x241, "EA 24 18 19 1A 1B 1C 1D"),
|
|
(0x241, "EA 25 1E 1F 20 21 22 23"),
|
|
(0x241, "EA 26 24 25 26 27 28" ),
|
|
]
|
|
|
|
succ = False
|
|
def sender(args=None):
|
|
for f in test_frames:
|
|
call("cansend %s %3x#%s" % (iface0, f[0], "".join(f[1].split())), shell=True)
|
|
global succ
|
|
succ = True
|
|
|
|
s = new_can_socket(iface0)
|
|
thread = threading.Thread(target=sender)
|
|
sniffed = ISOTPSniffer.sniff(s, timeout=1, count=1, prn=lambda x: x.show2(), started_callback=thread.start)
|
|
sniffed[0]['ISOTP'].data == bytearray(range(1, 0x29))
|
|
thread.join()
|
|
assert(succ)
|
|
|
|
|
|
+ ISOTPSocket tests
|
|
|
|
|
|
= Single-frame receive
|
|
|
|
cans = MockCANSocket()
|
|
cans.rcvd_queue.put(CAN(identifier=0x241, data=dhex("05 01 02 03 04 05")))
|
|
with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s:
|
|
msg = s.recv()
|
|
|
|
assert(msg.data == dhex("01 02 03 04 05"))
|
|
assert(cans.sent_queue.empty())
|
|
|
|
|
|
= Single-frame send
|
|
|
|
cans = MockCANSocket()
|
|
with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s:
|
|
s.send(ISOTP(dhex("01 02 03 04 05")))
|
|
|
|
msg = cans.sent_queue.get(True, 1)
|
|
assert(msg.data == dhex("05 01 02 03 04 05"))
|
|
assert(cans.sent_queue.empty())
|
|
assert(cans.rcvd_queue.empty())
|
|
|
|
|
|
= Two frame receive
|
|
|
|
cans = MockCANSocket()
|
|
with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s:
|
|
ready = threading.Event()
|
|
exception = None
|
|
succ = False
|
|
def sender():
|
|
global exception, succ
|
|
try:
|
|
cans.rcvd_queue.put(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06")))
|
|
ready.set()
|
|
c = cans.sent_queue.get(True, 2)
|
|
assert(c.data == dhex("30 00 00"))
|
|
cans.rcvd_queue.put(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00")))
|
|
succ = True
|
|
except Exception as ex:
|
|
exception = ex
|
|
raise ex
|
|
thread = threading.Thread(target=sender, name="sender")
|
|
thread.start()
|
|
ready.wait()
|
|
msg = s.recv()
|
|
thread.join()
|
|
|
|
if exception is not None:
|
|
raise exception
|
|
|
|
assert(succ)
|
|
assert(msg.data == dhex("01 02 03 04 05 06 07 08 09"))
|
|
assert(cans.sent_queue.empty())
|
|
assert(cans.rcvd_queue.empty())
|
|
|
|
|
|
= 20000 bytes receive
|
|
|
|
data = dhex("01 02 03 04 05")*4000
|
|
cans = MockCANSocket()
|
|
|
|
with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s:
|
|
ready = threading.Event()
|
|
exception = None
|
|
succ = False
|
|
def sender():
|
|
global exception, succ
|
|
try:
|
|
cf = ISOTP(data, dst=0x241).fragment()
|
|
ff = cf.pop(0)
|
|
cans.rcvd_queue.put(ff)
|
|
ready.set()
|
|
c = cans.sent_queue.get(True, 2)
|
|
assert(c.data == dhex("30 00 00"))
|
|
for f in cf:
|
|
cans.rcvd_queue.put(f)
|
|
succ = True
|
|
except Exception as ex:
|
|
exception = ex
|
|
raise ex
|
|
thread = threading.Thread(target=sender, name="sender")
|
|
thread.start()
|
|
ready.wait()
|
|
msg = s.recv()
|
|
thread.join()
|
|
|
|
if exception is not None:
|
|
raise exception
|
|
|
|
assert(succ)
|
|
assert(msg.data == data)
|
|
assert(cans.sent_queue.empty())
|
|
assert(cans.rcvd_queue.empty())
|
|
|
|
cans = MockCANSocket()
|
|
with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s:
|
|
s.send(ISOTP(dhex("01 02 03 04 05")))
|
|
|
|
|
|
= 20000 bytes send
|
|
|
|
data = dhex("01 02 03 04 05")*4000
|
|
cans = MockCANSocket()
|
|
msg = ISOTP(data, dst=0x641)
|
|
succ = threading.Event()
|
|
ready = threading.Event()
|
|
fragments = msg.fragment()
|
|
ack = CAN(identifier=0x241, data=dhex("30 00 00"))
|
|
|
|
def acker():
|
|
ready.set()
|
|
ff = cans.sent_queue.get(True, 2)
|
|
assert(ff == fragments[0])
|
|
cans.rcvd_queue.put(ack)
|
|
for fragment in fragments[1:]:
|
|
cf = cans.sent_queue.get(True, 2)
|
|
assert(fragment == cf)
|
|
succ.set()
|
|
|
|
thread = threading.Thread(target=acker, name="acker")
|
|
thread.start()
|
|
ready.wait()
|
|
|
|
with ISOTPSoftSocket(cans, sid=0x641, did=0x241) as s:
|
|
s.send(msg)
|
|
|
|
thread.join()
|
|
succ.wait(2)
|
|
assert(succ.is_set())
|
|
|
|
|
|
= Create and close ISOTP soft socket
|
|
with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) as s:
|
|
assert(s.impl.rx_thread.isAlive())
|
|
s.close()
|
|
s.impl.rx_thread.join(5)
|
|
assert(not s.impl.rx_thread.isAlive())
|
|
assert(not s.impl.rx_timer.isAlive())
|
|
assert(not s.impl.tx_timer.isAlive())
|
|
|
|
|
|
= Verify that all threads will die when GC collects the socket
|
|
import gc
|
|
s = ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241)
|
|
assert(s.impl.rx_thread.isAlive())
|
|
impl = s.impl
|
|
s = None
|
|
r = gc.collect()
|
|
impl.rx_thread.join(10) # hope that the GC has made a pass
|
|
assert(not impl.rx_thread.isAlive())
|
|
assert(not impl.rx_timer.isAlive())
|
|
assert(not impl.tx_timer.isAlive())
|
|
|
|
|
|
= Test on_recv function with single frame
|
|
with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) as s:
|
|
s.ins.on_recv(CAN(identifier=0x241, data=dhex("05 01 02 03 04 05")))
|
|
msg = s.ins.rx_queue.get(True, 1)
|
|
assert(msg == dhex("01 02 03 04 05"))
|
|
|
|
|
|
= Test on_recv function with empty frame
|
|
with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241) as s:
|
|
s.ins.on_recv(CAN(identifier=0x241, data=b""))
|
|
assert(s.ins.rx_queue.empty())
|
|
|
|
|
|
= Test on_recv function with single frame and extended addressing
|
|
with ISOTPSocket(MockCANSocket(), sid=0x641, did=0x241, extended_rx_addr=0xea) as s:
|
|
s.ins.on_recv(CAN(identifier=0x241, data=dhex("EA 05 01 02 03 04 05")))
|
|
msg = s.ins.rx_queue.get(True, 1)
|
|
assert(msg == dhex("01 02 03 04 05"))
|
|
|
|
|
|
= CF is sent when first frame is received
|
|
cans = MockCANSocket()
|
|
with ISOTPSocket(cans, sid=0x641, did=0x241) as s:
|
|
s.ins.on_recv(CAN(identifier=0x241, data=dhex("10 20 01 02 03 04 05 06")))
|
|
can = cans.sent_queue.get(True, 1)
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("30 00 00"))
|
|
|
|
|
|
+ Testing ISOTPSocket with an actual CAN socket
|
|
|
|
= Verify that packets are not lost if they arrive before the sniff() is called
|
|
~ linux needs_root
|
|
|
|
ss = new_can_socket(iface0)
|
|
sr = new_can_socket(iface0)
|
|
print("socket open")
|
|
ss.send(CAN(identifier=0x111, data=b"\x01\x23\x45\x67"))
|
|
time.sleep(0.02)
|
|
p = sr.sniff(count=1, timeout=0.2)
|
|
assert(len(p)==1)
|
|
ss.send(CAN(identifier=0x111, data=b"\x89\xab\xcd\xef"))
|
|
time.sleep(0.02)
|
|
p = sr.sniff(count=1, timeout=0.2)
|
|
assert(len(p)==1)
|
|
del ss
|
|
del sr
|
|
|
|
|
|
= Send single frame ISOTP message, using begin_send
|
|
~ linux needs_root
|
|
cans = new_can_socket(iface0)
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
s.begin_send(ISOTP(data=dhex("01 02 03 04 05")))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("05 01 02 03 04 05"))
|
|
|
|
|
|
= Send many single frame ISOTP messages, using begin_send
|
|
~ linux needs_root
|
|
cans = new_can_socket(iface0)
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
for i in range(100):
|
|
data = dhex("01 02 03 04 05") + struct.pack("B", i)
|
|
expected = struct.pack("B", len(data)) + data
|
|
s.begin_send(ISOTP(data=data))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
print(can.data, data)
|
|
assert(can.data == expected)
|
|
|
|
|
|
= Send two-frame ISOTP message, using begin_send
|
|
~ linux needs_root
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
cans = new_can_socket(iface0)
|
|
s.begin_send(ISOTP(data=dhex("01 02 03 04 05 06 07 08")))
|
|
can = cans.recv()
|
|
assert can.identifier == 0x641
|
|
assert can.data == dhex("10 08 01 02 03 04 05 06")
|
|
cans.send(CAN(identifier = 0x241, data=dhex("30 00 00")))
|
|
can = cans.recv()
|
|
assert can.identifier == 0x641
|
|
assert can.data == dhex("21 07 08")
|
|
|
|
|
|
= Send single frame ISOTP message
|
|
~ linux needs_root
|
|
|
|
cans = new_can_socket(iface0)
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
s.send(ISOTP(data=dhex("01 02 03 04 05")))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("05 01 02 03 04 05"))
|
|
|
|
|
|
= Send two-frame ISOTP message
|
|
~ linux needs_root
|
|
|
|
cans = new_can_socket(iface0)
|
|
acker_ready = threading.Event()
|
|
def acker():
|
|
acks = new_can_socket(iface0)
|
|
acker_ready.set()
|
|
can = acks.recv()
|
|
acks.send(CAN(identifier = 0x241, data=dhex("30 00 00")))
|
|
|
|
Thread(target=acker).start()
|
|
acker_ready.wait()
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08")))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("10 08 01 02 03 04 05 06"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x241)
|
|
assert(can.data == dhex("30 00 00"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("21 07 08"))
|
|
|
|
|
|
= Receive a single frame ISOTP message
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("05 01 02 03 04 05")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05"))
|
|
assert(isotp.src == 0x641)
|
|
assert(isotp.dst == 0x241)
|
|
assert(isotp.exsrc == None)
|
|
assert(isotp.exdst == None)
|
|
|
|
|
|
= Receive a single frame ISOTP message, with extended addressing
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241, extended_addr=0xc0, extended_rx_addr=0xea) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("EA 05 01 02 03 04 05")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05"))
|
|
assert(isotp.src == 0x641)
|
|
assert(isotp.dst == 0x241)
|
|
assert(isotp.exsrc == 0xc0)
|
|
assert(isotp.exdst == 0xea)
|
|
|
|
|
|
= Receive a two-frame ISOTP message
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("10 0B 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 10 11")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05 06 07 08 09 10 11"))
|
|
|
|
|
|
= Check what happens when a CAN frame with wrong identifier gets received
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier = 0x141, data = dhex("05 01 02 03 04 05")))
|
|
assert(s.ins.rx_queue.empty())
|
|
|
|
|
|
+ Testing ISOTPSocket timeouts
|
|
|
|
|
|
= Check if not sending the last CF will make the socket timeout
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("10 11 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 0A 0B 0C 0D")))
|
|
isotp = s.sniff(timeout=1)
|
|
|
|
assert(len(isotp) == 0)
|
|
|
|
|
|
|
|
= Check if not sending the first CF will make the socket timeout
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("10 11 01 02 03 04 05 06")))
|
|
isotp = s.sniff(timeout=1)
|
|
|
|
assert(len(isotp) == 0)
|
|
|
|
|
|
= Check if not sending the first FC will make the socket timeout
|
|
~ linux needs_root
|
|
|
|
exception = None
|
|
isotp = ISOTP(data=dhex("01 02 03 04 05 06 07 08 09 0A"))
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
try:
|
|
s.send(isotp)
|
|
assert(False)
|
|
except Scapy_Exception as ex:
|
|
exception = ex
|
|
|
|
print(exception)
|
|
assert(str(exception) == "TX state was reset due to timeout")
|
|
|
|
|
|
= Check if not sending the second FC will make the socket timeout
|
|
~ linux needs_root
|
|
|
|
exception = None
|
|
isotp = ISOTP(data=b"\xa5" * 120)
|
|
test_sem = threading.Semaphore(0)
|
|
evt = threading.Event()
|
|
|
|
def acker():
|
|
cans = new_can_socket(iface0)
|
|
evt.set()
|
|
can = cans.recv()
|
|
cans.send(CAN(identifier = 0x241, data=dhex("30 04 00")))
|
|
|
|
thread = Thread(target=acker)
|
|
thread.start()
|
|
evt.wait()
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
try:
|
|
s.send(isotp)
|
|
except Scapy_Exception as ex:
|
|
exception = ex
|
|
|
|
thread.join()
|
|
|
|
assert(exception is not None)
|
|
print(exception)
|
|
assert(str(exception) == "TX state was reset due to timeout")
|
|
|
|
|
|
= Check if reception of an overflow FC will make a send fail
|
|
~ linux needs_root
|
|
|
|
exception = None
|
|
isotp = ISOTP(data=b"\xa5" * 120)
|
|
test_sem = threading.Semaphore(0)
|
|
evt = threading.Event()
|
|
|
|
def acker():
|
|
cans = new_can_socket(iface0)
|
|
evt.set()
|
|
can = cans.recv()
|
|
cans.send(CAN(identifier = 0x241, data=dhex("32 00 00")))
|
|
|
|
thread = Thread(target=acker)
|
|
thread.start()
|
|
evt.wait()
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
try:
|
|
s.send(isotp)
|
|
except Scapy_Exception as ex:
|
|
exception = ex
|
|
|
|
thread.join()
|
|
|
|
assert(exception is not None)
|
|
print(exception)
|
|
assert(str(exception) == "Overflow happened at the receiver side")
|
|
|
|
|
|
= Close the Socket
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket(iface0), sid=0x641, did=0x241) as s:
|
|
s.close()
|
|
|
|
+ More complex operations
|
|
|
|
= ISOTPSoftSocket sr1
|
|
~ needs_root linux
|
|
|
|
evt = threading.Event()
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
rx2 = None
|
|
|
|
def sender():
|
|
global evt, rx2, msg
|
|
with ISOTPSoftSocket(new_can_socket(iface0), 0x123, 0x321) as sock:
|
|
evt.wait()
|
|
rx2 = sock.sr1(msg, timeout=1, verbose=True)
|
|
|
|
txThread = threading.Thread(target=sender)
|
|
txThread.start()
|
|
|
|
with ISOTPSoftSocket(new_can_socket(iface0), 0x321, 0x123) as sock:
|
|
evt.set()
|
|
rx = sock.recv()
|
|
sock.send(msg)
|
|
sent = True
|
|
|
|
txThread.join()
|
|
|
|
assert(rx == msg)
|
|
assert(sent)
|
|
assert(rx2 is not None)
|
|
assert(rx2 == msg)
|
|
|
|
= ISOTPSoftSocket sr1 and ISOTP test vice versa
|
|
~ needs_root linux
|
|
|
|
rx2 = None
|
|
sent = False
|
|
evt = threading.Event()
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
|
|
with ISOTPSoftSocket(new_can_socket0(), 0x321, 0x123) as rxSock, \
|
|
ISOTPSoftSocket(new_can_socket0(), 0x123, 0x321) as txSock:
|
|
def receiver():
|
|
global rx2, sent
|
|
evt.set()
|
|
rx2 = rxSock.sniff(count=1)
|
|
rxSock.send(msg)
|
|
sent = True
|
|
rxThread = threading.Thread(target=receiver, name="receiver")
|
|
rxThread.start()
|
|
evt.wait()
|
|
rx = txSock.sr1(msg, timeout=1,verbose=True)
|
|
rxThread.join()
|
|
|
|
assert(rx is not None)
|
|
assert(rx == msg)
|
|
assert(len(rx2) == 1)
|
|
assert(rx2[0] == msg)
|
|
assert(sent)
|
|
|
|
= ISOTPSoftSocket sniff
|
|
~ needs_root linux
|
|
|
|
evt = threading.Event()
|
|
succ = False
|
|
|
|
def receiver():
|
|
global evt, succ, rx
|
|
with ISOTPSoftSocket(new_can_socket0(), 0x321, 0x123) as sock:
|
|
evt.set()
|
|
rx = sock.sniff(count=5, timeout=1)
|
|
succ = True
|
|
|
|
rxThread = threading.Thread(target=receiver)
|
|
rxThread.start()
|
|
evt.wait()
|
|
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
with ISOTPSoftSocket(new_can_socket0(), 0x123, 0x321) as sock:
|
|
msg.data += b'0'
|
|
sock.send(msg)
|
|
msg.data += b'1'
|
|
sock.send(msg)
|
|
msg.data += b'2'
|
|
sock.send(msg)
|
|
msg.data += b'3'
|
|
sock.send(msg)
|
|
msg.data += b'4'
|
|
sock.send(msg)
|
|
|
|
rxThread.join()
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
msg.data += b'0'
|
|
assert(rx[0] == msg)
|
|
msg.data += b'1'
|
|
assert(rx[1] == msg)
|
|
msg.data += b'2'
|
|
assert(rx[2] == msg)
|
|
msg.data += b'3'
|
|
assert(rx[3] == msg)
|
|
msg.data += b'4'
|
|
assert(rx[4] == msg)
|
|
assert(succ)
|
|
|
|
+ ISOTPSoftSocket MITM attack tests
|
|
|
|
= bridge and sniff with isotp soft sockets set up vcan0 and vcan1 for package forwarding vcan1
|
|
~ linux needs_root
|
|
|
|
drain_bus(iface0)
|
|
drain_bus(iface1)
|
|
|
|
packet = ISOTP('Request')
|
|
succ = False
|
|
|
|
with ISOTPSoftSocket(new_can_socket0(), sid=0x241, did=0x641) as isoTpSocket0, \
|
|
ISOTPSoftSocket(new_can_socket1(), sid=0x641, did=0x241) as isoTpSocket1, \
|
|
ISOTPSoftSocket(new_can_socket0(), sid=0x641, did=0x241) as bSocket0, \
|
|
ISOTPSoftSocket(new_can_socket1(), sid=0x241, did=0x641) as bSocket1:
|
|
evt = threading.Event()
|
|
def forwarding(pkt):
|
|
global forwarded
|
|
forwarded += 1
|
|
return pkt
|
|
def bridge():
|
|
global forwarded, succ
|
|
forwarded = 0
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5,
|
|
started_callback=evt.set)
|
|
succ = True
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
evt.wait()
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=lambda: isoTpSocket0.send(packet))
|
|
threadBridge.join()
|
|
|
|
assert forwarded == 1
|
|
assert len(packetsVCan1) == 1
|
|
assert succ
|
|
|
|
drain_bus(iface0)
|
|
drain_bus(iface1)
|
|
|
|
= bridge and sniff with isotp soft sockets and multiple long packets
|
|
~ linux needs_root
|
|
|
|
drain_bus(iface0)
|
|
drain_bus(iface1)
|
|
|
|
packet = ISOTP('RequestASDF1234567890')
|
|
N = 3
|
|
T = 3
|
|
|
|
succ = False
|
|
with ISOTPSoftSocket(new_can_socket0(), sid=0x241, did=0x641) as isoTpSocket0, \
|
|
ISOTPSoftSocket(new_can_socket1(), sid=0x641, did=0x241) as isoTpSocket1, \
|
|
ISOTPSoftSocket(new_can_socket0(), sid=0x641, did=0x241) as bSocket0, \
|
|
ISOTPSoftSocket(new_can_socket1(), sid=0x241, did=0x641) as bSocket1:
|
|
evt = threading.Event()
|
|
def forwarding(pkt):
|
|
global forwarded
|
|
forwarded += 1
|
|
return pkt
|
|
def bridge():
|
|
global forwarded, succ
|
|
forwarded = 0
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding,
|
|
timeout=T, count=N, started_callback=evt.set)
|
|
succ = True
|
|
def sendpkts():
|
|
for i in range(N):
|
|
isoTpSocket0.send(packet)
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
evt.wait()
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=T, count=N, started_callback=sendpkts)
|
|
print("forwarded: %d" % forwarded)
|
|
print("len(packetsVCan1): %d" % len(packetsVCan1))
|
|
threadBridge.join()
|
|
|
|
assert forwarded == N
|
|
assert len(packetsVCan1) == N
|
|
assert succ
|
|
|
|
drain_bus(iface0)
|
|
drain_bus(iface1)
|
|
|
|
= bridge and sniff with isotp soft sockets set up vcan0 and vcan1 for package change vcan1
|
|
~ linux needs_root
|
|
|
|
|
|
drain_bus(iface0)
|
|
drain_bus(iface1)
|
|
|
|
packet = ISOTP('Request')
|
|
succ = False
|
|
with ISOTPSoftSocket(new_can_socket0(), sid=0x241, did=0x641) as isoTpSocket0, \
|
|
ISOTPSoftSocket(new_can_socket1(), sid=0x641, did=0x241) as isoTpSocket1, \
|
|
ISOTPSoftSocket(new_can_socket0(), sid=0x641, did=0x241) as bSocket0, \
|
|
ISOTPSoftSocket(new_can_socket1(), sid=0x241, did=0x641) as bSocket1:
|
|
evt = threading.Event()
|
|
def forwarding(pkt):
|
|
pkt.data = 'changed'
|
|
return pkt
|
|
def bridge():
|
|
global succ
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=1,
|
|
started_callback=evt.set)
|
|
succ = True
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
evt.wait()
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=lambda: isoTpSocket0.send(packet))
|
|
threadBridge.join()
|
|
|
|
assert len(packetsVCan1) == 1
|
|
assert packetsVCan1[0].data == b'changed'
|
|
assert succ
|
|
|
|
drain_bus(iface0)
|
|
drain_bus(iface1)
|
|
|
|
|
|
= Two ISOTPSockets at the same time, sending and receiving
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241) as s1, \
|
|
ISOTPSocket(new_can_socket0(), sid=0x241, did=0x641) as s2:
|
|
isotp = ISOTP(data=b"\x10\x25" * 43)
|
|
def sender():
|
|
s2.send(isotp)
|
|
Thread(target=sender).start()
|
|
result = s1.recv()
|
|
|
|
assert(result is not None)
|
|
result.show()
|
|
assert(result.data == isotp.data)
|
|
|
|
|
|
= Two ISOTPSockets at the same time, multiple sends/receives
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241) as s1, \
|
|
ISOTPSocket(new_can_socket0(), sid=0x241, did=0x641) as s2:
|
|
def sender(p):
|
|
s2.send(p)
|
|
for i in range(1, 40, 5):
|
|
isotp = ISOTP(data=bytearray(range(i, i * 2)))
|
|
Thread(target=sender, args=(isotp,)).start()
|
|
result = s1.recv()
|
|
assert (result is not None)
|
|
result.show()
|
|
assert (result.data == isotp.data)
|
|
|
|
|
|
= Send a single frame ISOTP message with padding
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=True) as s:
|
|
cans = new_can_socket(iface0)
|
|
s.send(ISOTP(data=dhex("01")))
|
|
res = cans.recv()
|
|
assert(res.length == 8)
|
|
|
|
|
|
= Send a two-frame ISOTP message with padding
|
|
~ linux needs_root
|
|
|
|
cans = new_can_socket(iface0)
|
|
acker_ready = threading.Event()
|
|
def acker():
|
|
acks = new_can_socket(iface0)
|
|
acker_ready.set()
|
|
can = acks.recv()
|
|
acks.send(CAN(identifier = 0x241, data=dhex("30 00 00")))
|
|
|
|
Thread(target=acker).start()
|
|
acker_ready.wait()
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=True) as s:
|
|
s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08")))
|
|
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("10 08 01 02 03 04 05 06"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x241)
|
|
assert(can.data == dhex("30 00 00"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("21 07 08 00 00 00 00 00"))
|
|
|
|
|
|
= Receive a padded single frame ISOTP message with padding disabled
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=False) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("05 06"))
|
|
|
|
|
|
= Receive a padded single frame ISOTP message with padding enabled
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=True) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("05 06"))
|
|
|
|
|
|
= Receive a non-padded single frame ISOTP message with padding enabled
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=True) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("02 05 06")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("05 06"))
|
|
|
|
|
|
= Receive a padded two-frame ISOTP message with padding enabled
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=True) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("01 02 03 04 05 06 07 08 09"))
|
|
|
|
|
|
= Receive a padded two-frame ISOTP message with padding disabled
|
|
~ linux needs_root
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x641, did=0x241, padding=False) as s:
|
|
cans = new_can_socket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00")))
|
|
res = s.recv()
|
|
res.show()
|
|
print(res.data)
|
|
print(raw(res))
|
|
assert(res.data == dhex("01 02 03 04 05 06 07 08 09"))
|
|
|
|
|
|
+ Compatibility with can-isotp linux kernel modules
|
|
~ linux needs_root
|
|
|
|
= Compatibility with isotpsend
|
|
exit_if_no_isotp_module()
|
|
|
|
message = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14"
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x642, did=0x242) as s:
|
|
cmd = "echo \"%s\" | isotpsend -s 242 -d 642 %s" % (message, iface0)
|
|
print(cmd)
|
|
r = subprocess.call(cmd, shell=True)
|
|
print("returncode is %d" % r)
|
|
assert(r == 0)
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex(message))
|
|
|
|
|
|
= Compatibility with isotpsend - extended addresses
|
|
exit_if_no_isotp_module()
|
|
message = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14"
|
|
|
|
with ISOTPSocket(new_can_socket0(), sid=0x644, did=0x244, extended_addr=0xaa, extended_rx_addr=0xee) as s:
|
|
cmd = "echo \"%s\" | isotpsend -s 244 -d 644 %s -x ee:aa" % (message, iface0)
|
|
print(cmd)
|
|
r = subprocess.call(cmd, shell=True)
|
|
print("returncode is %d" % r)
|
|
assert(r == 0)
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex(message))
|
|
|
|
|
|
= Compatibility with isotprecv
|
|
exit_if_no_isotp_module()
|
|
|
|
isotp = ISOTP(data=bytearray(range(1,20)))
|
|
cmd = "isotprecv -s 243 -d 643 -b 3 %s" % iface0
|
|
print(cmd)
|
|
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
|
time.sleep(0.1)
|
|
with ISOTPSocket(new_can_socket0(), sid=0x643, did=0x243) as s:
|
|
s.send(isotp)
|
|
|
|
threading.Timer(1, lambda: p.terminate() if p.poll() else p.wait()).start() # Timeout the receiver after 1 second
|
|
r = p.wait()
|
|
print("returncode is %d" % r)
|
|
assert(0 == r)
|
|
|
|
result = None
|
|
for i in range(10):
|
|
time.sleep(0.1)
|
|
if p.poll() is not None:
|
|
result = p.stdout.readline().decode().strip()
|
|
break
|
|
|
|
assert(result is not None)
|
|
print(result)
|
|
result_data = dhex(result)
|
|
assert(result_data == isotp.data)
|
|
|
|
|
|
= Compatibility with isotprecv - extended addresses
|
|
exit_if_no_isotp_module()
|
|
isotp = ISOTP(data=bytearray(range(1,20)))
|
|
cmd = "isotprecv -s 245 -d 645 -b 3 %s -x ee:aa" % iface0
|
|
print(cmd)
|
|
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
|
time.sleep(0.1) # Give some time for starting reception
|
|
with ISOTPSocket(new_can_socket0(), sid=0x645, did=0x245, extended_addr=0xaa, extended_rx_addr=0xee) as s:
|
|
s.send(isotp)
|
|
|
|
threading.Timer(1, lambda: p.terminate() if p.poll() else p.wait()).start() # Timeout the receiver after 1 second
|
|
r = p.wait()
|
|
print("returncode is %d" % r)
|
|
assert(0 == r)
|
|
|
|
result = None
|
|
for i in range(10):
|
|
time.sleep(0.1)
|
|
if p.poll() is not None:
|
|
result = p.stdout.readline().decode().strip()
|
|
break
|
|
|
|
assert(result is not None)
|
|
print(result)
|
|
result_data = dhex(result)
|
|
assert(result_data == isotp.data)
|
|
|
|
|
|
+ ISOTPNativeSocket tests
|
|
~ python3_only linux needs_root
|
|
|
|
|
|
= Configuration
|
|
~ conf python3_only linux
|
|
|
|
conf.contribs['CANSocket'] = {'use-python-can': False}
|
|
from scapy.contrib.cansocket_native import *
|
|
|
|
|
|
= Create ISOTP socket
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
|
|
|
|
= Send single frame ISOTP message
|
|
exit_if_no_isotp_module()
|
|
cans = CANSocket(iface0)
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
s.send(ISOTP(data=dhex("01 02 03 04 05")))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("05 01 02 03 04 05"))
|
|
|
|
|
|
= Send two-frame ISOTP message
|
|
exit_if_no_isotp_module()
|
|
cans = CANSocket(iface0)
|
|
evt = threading.Event()
|
|
def acker():
|
|
s = CANSocket(iface0)
|
|
evt.set()
|
|
can = s.recv()
|
|
s.send(CAN(identifier = 0x241, data=dhex("30 00 00")))
|
|
|
|
Thread(target=acker).start()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
evt.wait()
|
|
s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08")))
|
|
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("10 08 01 02 03 04 05 06"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x241)
|
|
assert(can.data == dhex("30 00 00"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("21 07 08"))
|
|
|
|
|
|
= Send a single frame ISOTP message with padding
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True)
|
|
cans = CANSocket(iface0)
|
|
s.send(ISOTP(data=dhex("01")))
|
|
res = cans.recv()
|
|
assert(res.length == 8)
|
|
|
|
|
|
= Send a two-frame ISOTP message with padding
|
|
exit_if_no_isotp_module()
|
|
cans = CANSocket(iface0)
|
|
acker_ready = threading.Event()
|
|
def acker():
|
|
acks = new_can_socket(iface0)
|
|
acker_ready.set()
|
|
can = acks.recv()
|
|
acks.send(CAN(identifier = 0x241, data=dhex("30 00 00")))
|
|
|
|
Thread(target=acker).start()
|
|
acker_ready.wait()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True)
|
|
s.send(ISOTP(data=dhex("01 02 03 04 05 06 07 08")))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("10 08 01 02 03 04 05 06"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x241)
|
|
assert(can.data == dhex("30 00 00"))
|
|
can = cans.recv()
|
|
assert(can.identifier == 0x641)
|
|
assert(can.data == dhex("21 07 08 00 00 00 00 00"))
|
|
|
|
|
|
= Receive a padded single frame ISOTP message with padding disabled
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=False)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("05 06"))
|
|
|
|
|
|
= Receive a padded single frame ISOTP message with padding enabled
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("02 05 06 00 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("05 06"))
|
|
|
|
|
|
= Receive a non-padded single frame ISOTP message with padding enabled
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("02 05 06")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("05 06"))
|
|
|
|
|
|
= Receive a padded two-frame ISOTP message with padding enabled
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=True)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("01 02 03 04 05 06 07 08 09"))
|
|
|
|
|
|
= Receive a padded two-frame ISOTP message with padding disabled
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, padding=False)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier=0x241, data=dhex("10 09 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier=0x241, data=dhex("21 07 08 09 00 00 00 00")))
|
|
res = s.recv()
|
|
assert(res.data == dhex("01 02 03 04 05 06 07 08 09"))
|
|
|
|
|
|
= Receive a single frame ISOTP message
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("05 01 02 03 04 05")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05"))
|
|
assert(isotp.src == 0x641)
|
|
assert(isotp.dst == 0x241)
|
|
assert(isotp.exsrc == None)
|
|
assert(isotp.exdst == None)
|
|
|
|
|
|
= Receive a single frame ISOTP message, with extended addressing
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241, extended_addr=0xc0, extended_rx_addr=0xea)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("EA 05 01 02 03 04 05")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05"))
|
|
assert(isotp.src == 0x641)
|
|
assert(isotp.dst == 0x241)
|
|
assert(isotp.exsrc == 0xc0)
|
|
assert(isotp.exdst == 0xea)
|
|
|
|
|
|
= Receive a two-frame ISOTP message
|
|
exit_if_no_isotp_module()
|
|
s = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("10 0B 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 10 11")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05 06 07 08 09 10 11"))
|
|
|
|
= Receive a two-frame ISOTP message and test python with statement
|
|
exit_if_no_isotp_module()
|
|
with ISOTPNativeSocket(iface0, sid=0x641, did=0x241) as s:
|
|
cans = CANSocket(iface0)
|
|
cans.send(CAN(identifier = 0x241, data = dhex("10 0B 01 02 03 04 05 06")))
|
|
cans.send(CAN(identifier = 0x241, data = dhex("21 07 08 09 10 11")))
|
|
isotp = s.recv()
|
|
assert(isotp.data == dhex("01 02 03 04 05 06 07 08 09 10 11"))
|
|
|
|
|
|
= ISOTP Socket sr1 test
|
|
~ needs_root linux
|
|
exit_if_no_isotp_module()
|
|
|
|
txSock = ISOTPNativeSocket(iface0, sid=0x123, did=0x321)
|
|
rxSock = CANSocket(iface0)
|
|
txmsg = ISOTP(b'\x11\x22\x33')
|
|
rx2 = None
|
|
|
|
def sender():
|
|
time.sleep(0.1)
|
|
global txmsg
|
|
global rx2
|
|
rx2 = txSock.sr1(txmsg, timeout=1, verbose=True)
|
|
|
|
def receiver():
|
|
rx = rxSock.recv()
|
|
rxSock.send(CAN(identifier=0x321, length=4, data=b'\x03\x7f\x22\x33'))
|
|
expectedrx = CAN(identifier=0x123, length=4, data=b'\x03\x11\x22\x33')
|
|
assert(rx.length == expectedrx.length)
|
|
assert(rx.data == expectedrx.data)
|
|
assert(rx.identifier == expectedrx.identifier)
|
|
|
|
txThread = threading.Thread(target=sender)
|
|
txThread.start()
|
|
receiver()
|
|
txThread.join()
|
|
|
|
assert(rx2 is not None)
|
|
assert(rx2 == ISOTP(b'\x7f\x22\x33'))
|
|
assert(rx2.answers(txmsg))
|
|
|
|
= ISOTP Socket sr1 and ISOTP test
|
|
~ needs_root linux
|
|
exit_if_no_isotp_module()
|
|
|
|
txSock = ISOTPNativeSocket(iface0, 0x123, 0x321)
|
|
rxSock = ISOTPNativeSocket(iface0, 0x321, 0x123)
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
rx2 = None
|
|
|
|
def sender():
|
|
time.sleep(0.1)
|
|
global rx2
|
|
rx2 = txSock.sr1(msg, timeout=1, verbose=True)
|
|
|
|
def receiver():
|
|
global rx
|
|
rx = rxSock.recv()
|
|
rxSock.send(msg)
|
|
|
|
txThread = threading.Thread(target=sender)
|
|
txThread.start()
|
|
receiver()
|
|
txThread.join()
|
|
|
|
assert(rx == msg)
|
|
assert(rxSock.send(msg))
|
|
assert(rx2 is not None)
|
|
assert(rx2 == msg)
|
|
|
|
= ISOTP Socket sr1 and ISOTP test vice versa
|
|
~ needs_root linux
|
|
exit_if_no_isotp_module()
|
|
|
|
rxSock = ISOTPNativeSocket(iface0, 0x321, 0x123)
|
|
txSock = ISOTPNativeSocket(iface0, 0x123, 0x321)
|
|
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
def receiver():
|
|
global rx2, sent
|
|
rx2 = rxSock.sniff(count=1, timeout=1)
|
|
sent = rxSock.send(msg)
|
|
|
|
def sender():
|
|
global rx
|
|
time.sleep(0.1)
|
|
rx = txSock.sr1(msg, timeout=1,verbose=True)
|
|
|
|
rx2 = None
|
|
sent = False
|
|
rxThread = threading.Thread(target=receiver)
|
|
rxThread.start()
|
|
sender()
|
|
rxThread.join()
|
|
|
|
assert(rx == msg)
|
|
assert(rx2[0] == msg)
|
|
assert(sent)
|
|
|
|
= ISOTP Socket sniff
|
|
~ needs_root linux
|
|
exit_if_no_isotp_module()
|
|
|
|
rxSock = ISOTPNativeSocket(iface0, 0x321, 0x123)
|
|
txSock = ISOTPNativeSocket(iface0, 0x123, 0x321)
|
|
succ = False
|
|
|
|
def receiver():
|
|
rx = rxSock.sniff(count=5, timeout=1)
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
msg.data += b'0'
|
|
assert(rx[0] == msg)
|
|
msg.data += b'1'
|
|
assert(rx[1] == msg)
|
|
msg.data += b'2'
|
|
assert(rx[2] == msg)
|
|
msg.data += b'3'
|
|
assert(rx[3] == msg)
|
|
msg.data += b'4'
|
|
assert(rx[4] == msg)
|
|
global succ
|
|
succ = True
|
|
|
|
def sender():
|
|
time.sleep(0.1)
|
|
msg = ISOTP(b'\x11\x22\x33\x11\x22\x33\x11\x22\x33\x11\x22\x33')
|
|
msg.data += b'0'
|
|
assert(txSock.send(msg))
|
|
msg.data += b'1'
|
|
assert(txSock.send(msg))
|
|
msg.data += b'2'
|
|
assert(txSock.send(msg))
|
|
msg.data += b'3'
|
|
assert(txSock.send(msg))
|
|
msg.data += b'4'
|
|
assert(txSock.send(msg))
|
|
|
|
rxThread = threading.Thread(target=receiver)
|
|
rxThread.start()
|
|
sender()
|
|
rxThread.join()
|
|
|
|
assert(succ)
|
|
|
|
+ ISOTPNativeSocket MITM attack tests
|
|
|
|
= bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package forwarding vcan1
|
|
~ python3_only linux needs_root
|
|
exit_if_no_isotp_module()
|
|
|
|
isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641)
|
|
isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241)
|
|
bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641)
|
|
|
|
bridgeStarted = threading.Event()
|
|
def bridge():
|
|
global bridgeStarted
|
|
def forwarding(pkt):
|
|
return pkt
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=2, count=1, started_callback=bridgeStarted.set)
|
|
bSocket0.close()
|
|
bSocket1.close()
|
|
global bSucc
|
|
bSucc = True
|
|
|
|
def RequestOnBus0():
|
|
global rSucc
|
|
time.sleep(0.2)
|
|
packet = ISOTP('Request')
|
|
isoTpSocket0.send(packet)
|
|
rSucc = True
|
|
|
|
bSucc = False
|
|
rSucc = False
|
|
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
threadSender = threading.Thread(target=RequestOnBus0)
|
|
bridgeStarted.wait()
|
|
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=threadSender.start)
|
|
|
|
len(packetsVCan1) == 1
|
|
|
|
isoTpSocket0.close()
|
|
isoTpSocket1.close()
|
|
|
|
threadSender.join()
|
|
threadBridge.join()
|
|
|
|
assert(bSucc)
|
|
assert(rSucc)
|
|
|
|
= bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package change to vcan1
|
|
~ python3_only linux needs_root
|
|
exit_if_no_isotp_module()
|
|
|
|
isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641)
|
|
isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241)
|
|
bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641)
|
|
|
|
bSucc = False
|
|
rSucc = False
|
|
|
|
bridgeStarted = threading.Event()
|
|
def bridge():
|
|
global bridgeStarted
|
|
global bSucc
|
|
def forwarding(pkt):
|
|
pkt.data = 'changed'
|
|
return pkt
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=bridgeStarted.set)
|
|
bSocket0.close()
|
|
bSocket1.close()
|
|
bSucc = True
|
|
|
|
def RequestOnBus0():
|
|
global rSucc
|
|
time.sleep(0.2)
|
|
packet = ISOTP('Request')
|
|
isoTpSocket0.send(packet)
|
|
rSucc = True
|
|
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
threadSender = threading.Thread(target=RequestOnBus0)
|
|
bridgeStarted.wait()
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=0.5, started_callback=threadSender.start)
|
|
|
|
packetsVCan1[0].data = b'changed'
|
|
len(packetsVCan1) == 1
|
|
|
|
isoTpSocket0.close()
|
|
isoTpSocket1.close()
|
|
|
|
threadSender.join()
|
|
threadBridge.join()
|
|
|
|
assert(bSucc)
|
|
assert(rSucc)
|
|
|
|
= bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package forwarding in both directions
|
|
~ python3_only linux needs_root
|
|
exit_if_no_isotp_module()
|
|
|
|
bSucc = False
|
|
rSucc = False
|
|
|
|
isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641)
|
|
isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241)
|
|
bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641)
|
|
|
|
bridgeStarted = threading.Event()
|
|
def bridge():
|
|
global bridgeStarted
|
|
global bSucc
|
|
def forwarding(pkt):
|
|
return pkt
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=bridgeStarted.set)
|
|
bSocket0.close()
|
|
bSocket1.close()
|
|
bSucc = True
|
|
|
|
def RequestBothVCans():
|
|
global rSucc
|
|
time.sleep(0.2)
|
|
packetVcan0 = ISOTP('RequestVcan0')
|
|
packetVcan1 = ISOTP('RequestVcan1')
|
|
isoTpSocket0.send(packetVcan0)
|
|
isoTpSocket1.send(packetVcan1)
|
|
rSucc = True
|
|
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
threadSender = threading.Thread(target=RequestOnBus0)
|
|
bridgeStarted.wait()
|
|
|
|
packetsVCan0 = isoTpSocket0.sniff(timeout=0.5, started_callback=threadSender.start)
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=0.5)
|
|
|
|
len(packetsVCan0) == 1
|
|
len(packetsVCan1) == 1
|
|
|
|
isoTpSocket0.close()
|
|
isoTpSocket1.close()
|
|
|
|
threadSender.join()
|
|
threadBridge.join()
|
|
|
|
assert(bSucc)
|
|
assert(rSucc)
|
|
|
|
= bridge and sniff with isotp native sockets set up vcan0 and vcan1 for package change in both directions
|
|
~ python3_only linux needs_root
|
|
exit_if_no_isotp_module()
|
|
|
|
bSucc = False
|
|
rSucc = False
|
|
|
|
isoTpSocket0 = ISOTPNativeSocket(iface0, sid=0x241, did=0x641)
|
|
isoTpSocket1 = ISOTPNativeSocket(iface1, sid=0x641, did=0x241)
|
|
bSocket0 = ISOTPNativeSocket(iface0, sid=0x641, did=0x241)
|
|
bSocket1 = ISOTPNativeSocket(iface1, sid=0x241, did=0x641)
|
|
|
|
bridgeStarted = threading.Event()
|
|
def bridge():
|
|
global bridgeStarted
|
|
global bSucc
|
|
def forwarding(pkt):
|
|
pkt.data = 'changed'
|
|
return pkt
|
|
bridge_and_sniff(if1=bSocket0, if2=bSocket1, xfrm12=forwarding, xfrm21=forwarding, timeout=0.5, started_callback=bridgeStarted.set)
|
|
bSocket0.close()
|
|
bSocket1.close()
|
|
bSucc = True
|
|
|
|
def RequestBothVCans():
|
|
global rSucc
|
|
time.sleep(0.2)
|
|
packetVcan0 = ISOTP('RequestVcan0')
|
|
packetVcan1 = ISOTP('RequestVcan1')
|
|
isoTpSocket0.send(packetVcan0)
|
|
isoTpSocket1.send(packetVcan1)
|
|
rSucc = True
|
|
|
|
threadBridge = threading.Thread(target=bridge)
|
|
threadBridge.start()
|
|
threadSender = threading.Thread(target=RequestBothVCans)
|
|
bridgeStarted.wait()
|
|
|
|
packetsVCan0 = isoTpSocket0.sniff(timeout=0.5, started_callback=threadSender.start)
|
|
packetsVCan1 = isoTpSocket1.sniff(timeout=0.5)
|
|
|
|
packetsVCan0[0].data = b'changed'
|
|
len(packetsVCan0) == 1
|
|
packetsVCan1[0].data = b'changed'
|
|
len(packetsVCan1) == 1
|
|
|
|
isoTpSocket0.close()
|
|
isoTpSocket1.close()
|
|
|
|
threadSender.join()
|
|
threadBridge.join()
|
|
|
|
assert(bSucc)
|
|
assert(rSucc)
|
|
|
|
+ Cleanup
|
|
|
|
= Cleanup reference to ISOTPSoftSocket to let the thread end
|
|
s = None
|