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

../../_images/enet-cable.jpg

Fig. 50 DoIP / HSFZ cable#

HSFZ Bus overview#

../../_images/Screenshot_rotated.png

Fig. 51 From BMW Technical training, G05 General Vehicle Electronics#

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 and DST

  • TYPE: 1 is message, 2 is echo/ack, 64 is error

  • SRC: Address of Tester

  • DST: Address of target ECU

../../_images/hsfz.png

Fig. 52 Full HSFZ packet#

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#

../../_images/2021-10-05-133210_751x778_scrot.png

Fig. 53 UDP vehicle announcement message in Wireshark#

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#

Table 4 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#

../../_images/2021-10-05-141724_846x721_scrot.png

Fig. 54 Vehicle announcement message in Wireshark#

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.