HSFZ and DoIP#
Protocol Basics of HSFZ#
HSFZ (High Speed Fahrzeug Zugang)
Transport layer protocol for UDS messages
Only used by BMW
First usages 2008
10Base-T in 2008
100Base-T appeared in 2015
Supports (logical) addressing of ECUs
OBD-Pin 8 is EthernetActivationLine
Usually on port 6801
HSFZ Bus overview#
HSFZ Packet#
rfc(HSFZ)
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LENGTH |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TYPE | SRC | DST |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
LENGTH
is payload length +SRC
andDST
TYPE
: 1 is message, 2 is echo/ack, 64 is errorSRC
: Address of TesterDST
: Address of target ECU
HSFZ in Scapy#
Load contribution layer
load_contrib("automotive.bmw.hsfz")
Build HSFZ packet
pkt = HSFZ(src=0xf4, dst=0x10)/UDS()/UDS_RDBI(identifiers=[0x172a])
Open HSFZSocket
sock = HSFZSocket('192.168.17.151')
Send and receive
res = sock.sr1(pkt, timeout=1)
res.show()
###[ HSFZ ]###
length = 18
type = message
src = 0x10
dst = 0xf4
###[ UDS ]###
service = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]###
dataIdentifier= IPConfiguration
###[ IP_CONFIG_RESP ]###
ADDRESS_FORMAT_ID= 0
IP = 192.168.17.151
SUBNETMASK= 255.255.255.0
DEFAULT_GATEWAY= 192.168.17.1
Create convenience socket object
sock = UDS_HSFZSocket(0xf4, 0x10, '192.168.17.151')
Send and receive UDS packet
pkt = UDS() / UDS_RDBI(identifiers=[0x172a])
res = sock.sr1(pkt, timeout=1)
res.show()
###[ UDS ]###
service = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]###
dataIdentifier= IPConfiguration
###[ IP_CONFIG_RESP ]###
ADDRESS_FORMAT_ID= 0
IP = 192.168.17.151
SUBNETMASK= 255.255.255.0
DEFAULT_GATEWAY= 192.168.17.1
HSFZ scanning#
Identification with nmap#
sudo nmap -sT 192.168.17.151 -Pn -p 6801
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-05 13:18 CEST
Nmap scan report for 192.168.17.151
Host is up (0.00024s latency).
PORT STATE SERVICE
6801/tcp open acnet
Nmap done: 1 IP address (1 host up) scanned in 0.15 seconds
HSFZ Vehicle announcement message#
Iterate through all ECUs behind a gateway#
load_contrib("automotive.bmw.hsfz")
load_contrib("automotive.uds")
pkts = list()
for i in range(0x100):
with UDS_HSFZSocket(0xf4, i, "192.168.17.151") as sock:
pkts += [(i, sock.sr1(UDS()/UDS_DSC(b"\x03"), timeout=0.05))]
Show results
print([x for x in pkts if x[1] is not None])
[(16,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(64,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(223,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(224,
<UDS service=DiagnosticSessionControlPositiveResponse ...>),
(240,
<UDS service=NegativeResponse |<UDS_NR ...|>>)]
Protocol Basics of DoIP#
Dagnostic over IP
Transport layer protocol for UDS messages
Specified in 2012
Features:
vehicle announcement and vehicle discovery
vehicle basic status information retrieval
connection establishment, connection maintenance and vehicle gateway control
data routing to and from the vehicle’s sub-components
error handling
Supports (logical) addressing of ECUs
Usually on port 13400
Communicates over Ethernet 100 Base-TX
Uses TCP and UDP dependent on the payload type
Pinout on OBD-Connector is usually identical to HSFZ
OBD-Pin 8 is EthernetActivationLine
Fully supported by Wireshark
Connection Kind vs. Payload Type#
Payload Type |
Payload Type Name |
Connection Kind |
---|---|---|
0x0000 |
Generic DoIP header negative acknowledge |
UDP / TCP |
0x0001 |
Vehicle Identification request message |
UDP |
0x0002 |
Vehicle identification request message with EID |
UDP |
0x0003 |
Vehicle identification request message with VIN |
UDP |
0x0004 |
Vehicle announcement message/vehicle identification response |
UDP |
0x0005 |
Routing activation request |
TCP |
0x0006 |
Routing activation response |
TCP |
0x0007 |
Alive Check request |
TCP |
0x0008 |
Alive Check response |
TCP |
0x4001 |
IP entity status request |
UDP |
0x4002 |
DoIP entity status response |
UDP |
0x4003 |
Diagnostic power mode information request |
UDP |
0x4004 |
Diagnostic power mode information response |
UDP |
0x8001 |
Diagnostic message |
TCP |
0x8002 |
Diagnostic message positive acknowledgement |
TCP |
0x8003 |
Diagnostic message negative acknowledgement |
TCP |
DoIP Vehicle announcement message#
DoIP in Scapy#
DoIP in Scapy
Load contribution layer
load_contrib("automotive.doip")
Send UDP message
socket = L3RawSocket6(iface="eth0")
resp = socket.sr1(IPv6(dst="fe80::21a:37ff:febf:eee4")/UDP()/DoIP(payload_type=1))
resp.show()
###[ IPv6 ]###
version = 6
tc = 0
fl = 0
plen = 48
nh = UDP
hlim = 255
src = fe80::21a:37ff:febf:eee4
dst = fe80::dea6:32ff:fe60:77a0
###[ UDP ]###
sport = 13400
dport = 13400
len = 48
chksum = 0xf8a8
###[ DoIP ]###
protocol_version= 0x2
inverse_version= 0xfd
payload_type= Vehicle announcement message/vehicle identification response message
payload_length= 32
vin = 'WAUZZZGE7KB000486'
logical_address= 0x4010
eid = '\x00\x1a7\\xbf\\xee\\xe4'
gid = '\x00\x1a7\\xbf\\xee\\xe4'
further_action= No further action required
vin_gid_status= VIN and/or GID are synchronized
Another UDP example
resp = socket.sr1(IPv6(dst="fe80::21a:37ff:febf:eee4")/UDP()/DoIP(payload_type=0x4001))
resp.show()
###[ IPv6 ]###
version = 6
tc = 0
fl = 0
plen = 23
nh = UDP
hlim = 255
src = fe80::21a:37ff:febf:eee4
dst = fe80::dea6:32ff:fe60:77a0
###[ UDP ]###
sport = 13400
dport = 13400
len = 23
chksum = 0xa891
###[ DoIP ]###
protocol_version= 0x2
inverse_version= 0xfd
payload_type= DoIP entity status response
payload_length= 7
node_type = DoIP gateway
max_open_sockets= 0x1
cur_open_sockets= 0x0
max_data_size= 4095
Send UDS message over TCP (StreamSocket)
sock = DoIPSocket("192.168.17.75")
Routing activation successful! Target address set to: 0x4010
pkt = DoIP(source_address=0xe80, target_address=0x4010)/UDS()/UDS_RDBI(identifiers=[0x1000])
resp = sock.sr1(pkt, timeout=1)
resp.show()
###[ DoIP ]###
protocol_version= 0x2
inverse_version= 0xfd
payload_type= Diagnostic message
payload_length= 15
source_address= 0x4010
target_address= 0xe80
###[ UDS ]###
service = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]###
dataIdentifier= 0x1000
###[ Raw ]###
load = '@\x00\x00\x00\x00\x00\x00\x00'
Send UDS message with convenience socket object
sock = UDS_DoIPSocket("192.168.17.75")
Routing activation successful! Target address set to: 0x4010
pkt = UDS() / UDS_RDBI(identifiers=[0x1000])
resp = sock.sr1(pkt, timeout=1)
resp.show()
###[ UDS ]###
service = ReadDataByIdentifierPositiveResponse
###[ ReadDataByIdentifierPositiveResponse ]###
dataIdentifier= 0x1000
###[ Raw ]###
load = '@\x00\x00\x00\x00\x00\x00\x00'
Important note: Immediately after opening a TCP connection, a routing activation message has to be sent.
Identification with nmap#
sudo nmap -sS 192.168.17.75 -Pn -p 13400
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-05 15:06 CEST
Nmap scan report for audi_cgw3_ecu (192.168.17.75)
Host is up (0.00013s latency).
PORT STATE SERVICE
13400/tcp open doip-data
MAC Address: 00:1A:37:BF:EE:E4 (Lear)
Nmap done: 1 IP address (1 host up) scanned in 0.96 seconds
Finding EthernetActivationLine#
EthernetActivationLine is an important pin for HSFZ and DoIP ECUs
If ECU is connected to OBD, pin 8 needs to be pulled up with 510 Ohm
This pin usually enables the entire Ethernet PHY on an ECU.
Without this pin being pulled, not IP communication.
If this pin is unknown, monitor the current consumption of an ECU and pull up various pins. If EthernetActivationLine is hit, current consumption will increase a lot.