Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
66cb860
feat: Add DoIP definitions
Magolves Dec 9, 2025
d907973
chore: Add CMakeList.txt
Magolves Dec 9, 2025
0d96015
doc: Add notes on setting up build env
Magolves Dec 9, 2025
f388223
chore: Add option UDS_TP_DOIP
Magolves Dec 9, 2025
061cc53
feat: Add DoIP header
Magolves Dec 9, 2025
6da59ae
chore: Add source files; integrate files into amalgate.py
Magolves Dec 10, 2025
724b994
fix: Begin impl of DoIP transport
Magolves Dec 10, 2025
0044be0
fix: Pass tp pointer to all functions, remove global instance
Magolves Dec 10, 2025
219311d
feat: Add test code
Magolves Dec 10, 2025
f1a9cfc
fix: Update target address in DoIP client initialization
Magolves Dec 10, 2025
dbd84ee
chore: Ignore example binaries
Magolves Dec 10, 2025
1c4e5b1
WIP
Magolves Dec 11, 2025
4304de0
feat: Add DoIP definitions
Magolves Dec 9, 2025
5ce6d25
feat: Enhance DoIP client state handling and update function return t…
Magolves Dec 15, 2025
da8fa89
style: Code cleanup, add missing doc
Magolves Dec 16, 2025
b1a3bd6
fix: Add safety belt for strncpy (sonar finding) and test case for in…
Magolves Dec 16, 2025
01b6073
test: Handle also WDBI messages
Magolves Dec 16, 2025
6c24ca6
fix: Use snprintf instead of strncpy to make SonarCube happy
Magolves Dec 16, 2025
c83b911
fix: Add missing const/sonarcube note
Magolves Dec 16, 2025
63efd7c
fix: Manual merge of diverted branches
Magolves Dec 17, 2025
d54d59c
Merge branch 'main' of https://github.com/Magolves/iso14229
Magolves Dec 17, 2025
7c83ad7
fix: Remove __attribute(packed) (review comment)
Magolves Jan 19, 2026
cf6fd0f
feat: Begin TP abstraction (tcp and udp)
Magolves Jan 19, 2026
9f3b05e
feat: Add DoIP UDP transport discovery and selection callback support
Magolves Jan 19, 2026
73eb411
feat: Implement DoIP discovery example with VIN selection callback
Magolves Jan 19, 2026
762d7f2
doc: Update ReadMe
Magolves Jan 19, 2026
9e9c9e7
feat: Add DoIP discovery example with VIN selection callback to README
Magolves Jan 19, 2026
e42f779
feat: Add Makefile
Magolves Jan 19, 2026
af322d8
fix: Make sockets non-blocking, define timeout via config, handle MSG…
Magolves Jan 20, 2026
62d5a41
fix: Fix & improve UDP discovery
Magolves Jan 20, 2026
f29ff42
doc: Add ReadMe on DoIP client
Magolves Jan 20, 2026
6768389
WIP: Add marker for TP layer
Magolves Jan 20, 2026
bdd70d1
Merge branch 'main' of https://github.com/Magolves/iso14229
Magolves Jan 20, 2026
617c628
Refactor DoIP transport layer to support mock implementations
Magolves Jan 20, 2026
bf2cf75
fix: Type, time overflow
Magolves Jan 20, 2026
38efbb8
fix: Use shared RX buffer, relief stack
Magolves Jan 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ jobs:
cd examples/linux_server_0x27
./test.sh

- run:
name: Build and test DoIP client example
command: |
cd examples/doip_client
./test.sh

workflows:
version: 2
build:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ cppcheck_reports
.venv
tools/cppcheck/misra_c_2023__headlines_for_cppcheck.txt
latex/
# Ignore binaries of doip example
examples/doip_client/doip_server
examples/doip_client/doip_client
55 changes: 55 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
cmake_minimum_required(VERSION 3.15)
project(iso14229)

# Options for each transport layer variant
option(BUILD_UDS_TP_ISOTP_C "Build ISO-TP C variant" OFF)
option(BUILD_UDS_TP_ISOTP_C_SOCKETCAN "Build ISO-TP C SocketCAN variant" OFF)
option(BUILD_UDS_TP_ISOTP_SOCK "Build ISO-TP socket variant" ON)
option(BUILD_UDS_TP_ISOTP_MOCK "Build ISO-TP mock variant" OFF)
option(BUILD_UDS_TP_DOIP "Build DoIP (ISO 13400) variant" OFF)

# Populate VARIANT_LIST based on enabled options
set(VARIANT_LIST)
if(BUILD_UDS_TP_ISOTP_C)
list(APPEND VARIANT_LIST UDS_TP_ISOTP_C)
endif()
if(BUILD_UDS_TP_ISOTP_C_SOCKETCAN)
list(APPEND VARIANT_LIST UDS_TP_ISOTP_C_SOCKETCAN)
endif()
if(BUILD_UDS_TP_ISOTP_SOCK)
list(APPEND VARIANT_LIST UDS_TP_ISOTP_SOCK)
endif()
if(BUILD_UDS_TP_ISOTP_MOCK)
list(APPEND VARIANT_LIST UDS_TP_ISOTP_MOCK)
endif()
if(BUILD_UDS_TP_DOIP)
list(APPEND VARIANT_LIST UDS_TP_DOIP)
endif()

# Create a library target for each enabled variant
foreach(VARIANT IN LISTS VARIANT_LIST)
# Remove UDS_TP_ prefix and convert to lowercase
string(REPLACE "UDS_TP_" "" VARIANT_SUFFIX ${VARIANT})
string(TOLOWER ${VARIANT_SUFFIX} VARIANT_SUFFIX_LOWER)

# Define target name
set(TARGET_NAME "iso14229_${VARIANT_SUFFIX_LOWER}")

# Create library target
add_library(${TARGET_NAME} iso14229.c)

# Set compile definition for this variant
target_compile_definitions(${TARGET_NAME} PRIVATE ${VARIANT})

# When building DoIP, also compile the transport instances (TCP/UDP)
if (VARIANT STREQUAL "UDS_TP_DOIP")
target_sources(${TARGET_NAME} PRIVATE
src/tp/doip/doip_tp_tcp.c
src/tp/doip/doip_tp_udp.c
)
endif()

message(STATUS "Configured target: ${TARGET_NAME} with variant: ${VARIANT}")
endforeach()


155 changes: 155 additions & 0 deletions docs/client_doip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# DoIP Client {#client_doip}

This document describes the DoIP (ISO 13400) client and transport usage in this codebase, focusing on TCP diagnostics, UDP vehicle discovery, and the DoIP transport layer.

## Basic Usage

### Discovery and Selection

Use UDP discovery to locate vehicles and select one based on VIN or first responder.

```c
#include "tp/doip/doip_client.h"

DoIPClient_t tp;
UDSDoIPSetDiscoveryOptions(/*request_only=*/true, /*dump_raw=*/false);
UDSDoIPSetSelectionCallback(&tp, /*optional*/ NULL, NULL);

// Loopback discovery (binds 13401, sends VI request to 13400)
int found = UDSDoIPDiscoverVehicles(&tp, /*timeout_ms=*/2000, /*loopback=*/true);
if (found <= 0 || tp.server_ip[0] == '\0') {
// handle no selection
}

// Selected server is available in tp.server_ip and tp.server_port
```

To filter by VIN prefix:

```c
static bool select_by_vin(const DoIPDiscoveryInfo *info, void *user) {
const char *prefix = (const char*)user;
if (!prefix || !*prefix || info->vin[0] == '\0') return false;
return strncmp(info->vin, prefix, strlen(prefix)) == 0;
}

UDSDoIPSetSelectionCallback(&tp, select_by_vin, (void*)"WVWZZZ");
int found = UDSDoIPDiscoverVehicles(&tp, 2000, true);
```

### Initialization and Routing Activation

After discovery, initialize the TCP connection and activate routing:

```c
// Provide source/target logical addresses
uint16_t SA = 0x0E00; // example tester address
uint16_t TA = 0x0E80; // example server address

UDSErr_t rc = UDSDoIPInitClient(&tp, tp.server_ip, tp.server_port, SA, TA);
if (rc != UDS_OK) {
// handle error
}

// If needed later:
rc = UDSDoIPActivateRouting(&tp);
```

### Sending and Receiving Diagnostic Messages

Once routing is active, the higher-level UDS client can use the DoIP transport (via `UDSTp_t` inside `DoIPClient_t`) to send UDS requests and receive responses. See the generic client flow in [docs/client.md](client.md) and the DoIP transport integration in [src/tp/doip/doip_client.c](src/tp/doip/doip_client.c).

## DoIP Client Structure

`DoIPClient_t` holds TCP/UDP transports, state, addresses, buffers, and flags. Relevant fields:

- `tcp`: DoIP TCP transport for diagnostics
- `udp`: DoIP UDP transport for discovery
- `source_address`, `target_address`: logical addresses
- `server_ip`, `server_port`: selected server endpoint
- `uds_response`/`uds_response_len`: buffered diagnostic response

See [src/tp/doip/doip_client.h](src/tp/doip/doip_client.h).

## Transport Layer (DoIP)

The DoIP transport abstraction is defined in [src/tp/doip/doip_transport.h](src/tp/doip/doip_transport.h).

- `DoIPTransport`:
- `fd`, `port`, `ip`, `is_udp`, `loopback`
- `connect_timeout_ms`, `send_timeout_ms`
- TCP helpers:
- `doip_tp_tcp_init()`, `doip_tp_tcp_connect()`, `doip_tp_tcp_send()`, `doip_tp_tcp_recv()`, `doip_tp_tcp_close()`
- UDP helpers:
- `doip_tp_udp_init()`, `doip_tp_udp_join_default_multicast()`,
`doip_tp_udp_recv()`, `doip_tp_udp_recvfrom()`, `doip_tp_udp_sendto()`, `doip_tp_udp_close()`

### Non-Blocking Behavior

- Sockets are set to non-blocking (`O_NONBLOCK`).
- `select()` is used for readiness and timeouts.
- TCP connect: non-blocking with `EINPROGRESS` handling (select on writable + `SO_ERROR`). See [src/tp/doip/doip_tp_tcp.c](src/tp/doip/doip_tp_tcp.c).
- TCP send: send-all loop with timeout (`MSG_NOSIGNAL` used when available). Partial writes are handled until the buffer is sent or timeout. See [src/tp/doip/doip_tp_tcp.c](src/tp/doip/doip_tp_tcp.c).
- TCP/UDP recv: `select()` for readability then a single `recv()`/`recvfrom()`.

### Ports and Constants

Defined in [src/tp/doip/doip_defines.h](src/tp/doip/doip_defines.h):

- `DOIP_TCP_PORT` = 13400
- `DOIP_UDP_DISCOVERY_PORT` = 13400 (vehicle side)
- `DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT` = 13401 (tester side)
- `DOIP_DEFAULT_TIMEOUT_MS` = 5000

UDP discovery binds to 13401 by default (tester request port). Active VI requests are sent to 13400.

### Configuring Timeouts

```c
DoIPTransport *tcp = &tp.tcp;
// Override defaults per transport
doip_tp_set_timeouts(tcp, /*connect_timeout_ms=*/2000, /*send_timeout_ms=*/1000);
```

## UDP Discovery Details

Implemented in [src/tp/doip/doip_client.c](src/tp/doip/doip_client.c) and [src/tp/doip/doip_tp_udp.c](src/tp/doip/doip_tp_udp.c):

- Bind (loopback): `127.0.0.1:13401` to receive announcements/responses.
- Bind (non-loopback): `INADDR_ANY:13401` with `SO_BROADCAST` enabled.
- Active VI Request: `doip_tp_udp_sendto()` sends a header-only VI request (payload type 0x0001, length 0) to `13400`.
- Optional multicast join (non-loopback): `224.224.224.224` when not in request-only mode.
- VIN/EID/GID parsing: best-effort extraction from vehicle identification payloads.

## Example: Discovery CLI

See the example in [examples/doip_discovery_example/doip_discover.c](examples/doip_discovery_example/doip_discover.c):

- Loopback discover first responder:
```bash
(cd examples/doip_discovery_example && ./doip_discovery loopback)
```
- Filter by VIN prefix:
```bash
(cd examples/doip_discovery_example && ./doip_discovery WVWZZZ loopback)
```
- Options:
- `--request-only`: send VI requests, skip multicast
- `--raw`: dump raw frames via logger

## Configuration {#doip_configuration}

Compile-time and runtime:

- `DOIP_DEFAULT_TIMEOUT_MS` (compile-time): select timeouts default
- `doip_tp_set_timeouts()` (runtime): per-transport connect/send timeouts (TCP)
- `UDSDoIPSetDiscoveryOptions(request_only, dump_raw)` (runtime): discovery behavior

## See Also

- DoIP client implementation: [src/tp/doip/doip_client.c](src/tp/doip/doip_client.c)
- DoIP transport API: [src/tp/doip/doip_transport.h](src/tp/doip/doip_transport.h)
- TCP transport: [src/tp/doip/doip_tp_tcp.c](src/tp/doip/doip_tp_tcp.c)
- UDP transport: [src/tp/doip/doip_tp_udp.c](src/tp/doip/doip_tp_udp.c)
- DoIP constants: [src/tp/doip/doip_defines.h](src/tp/doip/doip_defines.h)
- Generic client guide: [docs/client.md](client.md)
4 changes: 3 additions & 1 deletion docs/mainpage.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# iso14229 {#mainpage}

iso14229 is a UDS (ISO14229) library for writing servers and clients.
iso14229 is a UDS (ISO14229) library for writing servers and clients.

**Source Code:** https://github.com/driftregion/iso14229

Expand All @@ -23,6 +23,7 @@ To access the examples, clone or download the repository from https://github.com
| \ref examples/arduino_server/README.md "arduino_server" | Arduino server |
| \ref examples/esp32_server/README.md "esp32_server" | ESP32 server |
| \ref examples/s32k144_server/README.md "s32k144_server" | NXP S32K144 server |
| \ref examples/doip_client/README.md "doip_client" | DoIP client |

---

Expand All @@ -44,6 +45,7 @@ Configure the library at compilation time with preprocessor defines:
| **isotp_c_socketcan** | `-DUDS_TP_ISOTP_C_SOCKETCAN` | isotp-c over SocketCAN | Linux newer than 2.6.25 | \ref examples/linux_server_0x27/README.md "linux_server_0x27" |
| **isotp_c** | `-DUDS_TP_ISOTP_C` | Software ISO-TP | Everything else | \ref examples/arduino_server/README.md "arduino_server" \ref examples/esp32_server/README.md "esp32_server" \ref examples/s32k144_server/README.md "s32k144_server" |
| **isotp_mock** | `-DUDS_TP_ISOTP_MOCK` | In-memory transport for testing | platform-independent unit tests | see unit tests |
| **doip** | `-DUDS_TP_DOIP` | DoIP (iso 13400) client | platform-independent unit tests | \ref examples/linux_rdbi_wdbi/README.md |

### System Selection Override

Expand Down
21 changes: 21 additions & 0 deletions examples/doip_client/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CLI_SRCS += iso14229.c doip_test_client.c
CLI_HDRS += iso14229.h
CLI_TARGET = doip_client

SVR_SRCS = doip_server.c doip_test_server.c
SVR_TARGET = doip_server

# Use the verbose log level for debugging
# CFLAGS = -DUDS_TP_DOIP=1 -DUDS_CONFIG_LOG_COLORS=1 -DUDS_LOG_LEVEL=UDS_LOG_VERBOSE -g -Wall -Wpedantic -Wextra
CFLAGS = -DUDS_TP_DOIP=1 -DUDS_CONFIG_LOG_COLORS=1 -DUDS_LOG_LEVEL=UDS_LOG_INFO -g -Wall -Wpedantic -Wextra

all: client server

client: $(CLI_SRCS) $(CLI_HDRS)
$(CC) $(CFLAGS) $(CLI_SRCS) -o $(CLI_TARGET)

server: $(SVR_SRCS)
$(CC) $(CFLAGS) $(SVR_SRCS) -o $(SVR_TARGET)

clean:
rm -f $(CLI_TARGET) $(SVR_TARGET)
Loading