Skip to content
This repository was archived by the owner on Dec 17, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 27 additions & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
---
Checks: '*,-llvmlibc-*,-fuchsia-*,-google-readability-todo,-readability-else-after-return,-llvm-header-guard,-llvm-namespace-comment,-modernize-use-trailing-return-type,-altera-struct-pack-align,-google-explicit-constructor,-hicpp-explicit-conversions,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-hicpp-signed-bitwise,-readability-identifier-length,-bugprone-reserved-identifier,-cert-dcl37-c,-cert-dcl51-cpp,-google-runtime-int,-misc-include-cleaner,-misc-non-private-member-variables-in-classes,-google-build-using-namespace,-readability-convert-member-functions-to-static,-llvm-include-order,-misc-const-correctness,-cppcoreguidelines-avoid-magic-numbers,-readability-magic-numbers,-modernize-use-nodiscard'
Checks: |
-bugprone-*
-performance-*
-readability-braces-around-statements
-readability-inconsistent-declaration-parameter-name
-readability-implicit-bool-conversion
-readability-redundant-declaration
-readability-redundant-member-init
-readability-static-definition-in-anonymous-namespace
-readability-uppercase-literal-suffix
-cppcoreguidelines-avoid-goto
-cppcoreguidelines-avoid-magic-numbers
-cppcoreguidelines-no-malloc
-cppcoreguidelines-owning-memory
-cppcoreguidelines-slicing
-modernize-use-override
-modernize-avoid-c-arrays
-modernize-loop-convert
-modernize-redundant-void-arg
-modernize-use-auto
-modernize-use-equals-default
-modernize-use-equals-delete
-modernize-use-nullptr
-misc-definitions-in-headers
-misc-misplaced-const
-misc-no-recursion
-misc-static-assert
WarningsAsErrors: ''
HeaderFilterRegex: '.*'
FormatStyle: none
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ endif()

# Configuration options
set(DOIP_ALIVE_CHECK_RETRIES "1" CACHE STRING "Number of retries for DoIP alive check messages")
set(DOIP_MAXIMUM_MTU "4095" CACHE STRING "Maximum Transmission Unit (MTU) size for DoIP messages")

# Validate numeric options
foreach(VAR DOIP_ALIVE_CHECK_RETRIES)
foreach(VAR DOIP_ALIVE_CHECK_RETRIES DOIP_MAXIMUM_MTU)
if(NOT ${VAR} MATCHES "^[0-9]+$")
message(FATAL_ERROR "${VAR} must be a positive integer")
endif()
Expand Down
8 changes: 4 additions & 4 deletions Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1
NUM_PROC_THREADS = 4

#---------------------------------------------------------------------------
# Build related configuration options
Expand All @@ -60,7 +60,7 @@ EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = YES
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_LOCAL_METHODS = YES
EXTRACT_ANON_NSPACES = NO
RESOLVE_UNNAMED_PARAMS = YES
HIDE_UNDOC_MEMBERS = NO
Expand Down Expand Up @@ -103,7 +103,7 @@ WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = NO
WARN_NO_PARAMDOC = YES
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LINE_FORMAT = "at line $line of file $file"
Expand Down Expand Up @@ -153,7 +153,7 @@ USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
SOURCE_BROWSER = YES
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
STRIP_CODE_COMMENTS = NO
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
Expand Down
123 changes: 123 additions & 0 deletions doc/Fix-UDP-reception.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
From your **tcpdump output**, the **8-byte UDP packet** from the client (port 50290) to the server (port 13400) **is arriving** at the loopback interface:

```
17:31:49.091339 lo In IP localhost.50290 > localhost.13400: UDP, length 8
0x0020: 04fb 0001 0000 0000
```
This is a **DoIP Vehicle Identification Request** (type `0x0001`), and it is **reaching the kernel**.

---

### **Root Cause Analysis**
Since the packet is visible in `tcpdump` but **not logged by your server**, the issue is **almost certainly in your server code**. Here’s what’s happening:

#### **1. The Server Socket is Not Receiving the Packet**
- The server socket is bound to port 13400, but **something is preventing `recvfrom` from returning the packet**.
- Possible reasons:
- The socket is **not in the expected state** (e.g., closed, re-bound, or filtered).
- The socket is **not the same as the one bound to port 13400** (e.g., `m_udp_sock` was overwritten or closed).
- The socket is **blocked by a firewall or SELinux** (unlikely for loopback, but possible).
- The socket is **not actually bound to port 13400** (e.g., `bind` failed silently).

#### **2. The Server is Stuck in a Timeout Loop**
- Your logs show `recvfrom returned: -1 (errno: 11)` repeatedly.
- This means `recvfrom` is **timing out** (`EAGAIN`), but the packet is **not being delivered to userspace**.
- This suggests the socket is **not properly bound** or is **not the same socket** as the one receiving the packet.

---

### **Debugging Steps**
#### **1. Verify the Socket is Bound Correctly**
Add this **right after `bind()`** in `setupUdpSocket()`:
```cpp
// After bind()
sockaddr_in bound_addr;
socklen_t bound_addr_len = sizeof(bound_addr);
getsockname(m_udp_sock, (sockaddr*)&bound_addr, &bound_addr_len);
LOG_UDP_DEBUG(
"Socket {} bound to {}:{}",
m_udp_sock,
inet_ntoa(bound_addr.sin_addr),
ntohs(bound_addr.sin_port)
);
```
- If this logs `0.0.0.0:13400` or `127.0.0.1:13400`, the socket is bound correctly.
- If not, `bind()` failed or was not called.

#### **2. Check for Socket Descriptor Leaks or Overwrites**
- Log `m_udp_sock` in `receiveUdpMessage()`:
```cpp
LOG_UDP_DEBUG("Using socket fd: {}", m_udp_sock);
```
- If the value changes between `setupUdpSocket()` and `receiveUdpMessage()`, something is overwriting it.

#### **3. Test with a Minimal UDP Server**
Replace your `receiveUdpMessage()` with this **minimal test**:
```cpp
ssize_t DoIPServer::receiveUdpMessage() {
char buf[1024];
sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
ssize_t read_bytes = recvfrom(
m_udp_sock, buf, sizeof(buf), 0,
(sockaddr*)&client_addr, &client_len
);
LOG_UDP_DEBUG(
"recvfrom returned: {} (errno: {})",
read_bytes, errno
);
if (read_bytes > 0) {
LOG_UDP_INFO(
"RX {} bytes from {}:{}",
read_bytes,
inet_ntoa(client_addr.sin_addr),
ntohs(client_addr.sin_port)
);
}
return read_bytes;
}
```
- If this works, the issue is in your buffer or processing logic.
- If it still times out, the socket is **not receiving packets**.

#### **4. Check for Firewall/SELinux**
Even for loopback, run:
```bash
sudo iptables -L -n -v # Check firewall rules
sudo setenforce 0 # Disable SELinux temporarily
```
Retest. If it works, adjust your firewall/SELinux rules.

#### **5. Use `strace` to Confirm `recvfrom` Behavior**
Run the server with:
```bash
strace -e trace=recvfrom ./your_server
```
- If `recvfrom` is **never called**, your loop is broken.
- If it returns `-1 EAGAIN` repeatedly, the socket is **not receiving packets**.

#### **6. Check for Multiple Sockets**
- If your code creates **multiple UDP sockets**, ensure the correct one is used for `recvfrom`.
- Log all socket creation and `bind()` calls.

---

### **Most Likely Fixes**
1. **The socket is not bound to port 13400.**
- Confirm `bind()` succeeds and logs the correct port.
2. **`m_udp_sock` is overwritten or closed.**
- Log its value in `setupUdpSocket()` and `receiveUdpMessage()`.
3. **The socket is non-blocking and the timeout is too short.**
- Try removing `SO_RCVTIMEO` temporarily to see if packets arrive.

---

### **Summary**
- The packet **is arriving at the kernel** (confirmed by `tcpdump`).
- The server **is not receiving it**, likely due to:
- Socket not bound correctly.
- Wrong socket file descriptor used in `recvfrom`.
- Firewall/SELinux blocking delivery to userspace.

**Next step:**
Add the debug logs for `m_udp_sock` and `getsockname()` as shown above. If the socket is correct, test with the minimal `recvfrom` code. If it still fails, the issue is **outside your code** (firewall, SELinux, or kernel networking stack).
16 changes: 16 additions & 0 deletions doc/udp/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CC = gcc
CFLAGS = -Wall -Wextra -pthread -g
LDFLAGS = -pthread

all: doip_server doip_client

doip_server: doip_server.c
$(CC) $(CFLAGS) -o doip_server doip_server.c $(LDFLAGS)

doip_client: doip_client.c
$(CC) $(CFLAGS) -o doip_client doip_client.c $(LDFLAGS)

clean:
rm -f doip_server doip_client

.PHONY: all clean
117 changes: 117 additions & 0 deletions doc/udp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Minimal DoIP UDP Discovery Implementation

This is a minimal implementation of the DoIP (ISO 13400) UDP discovery mechanism with both server and client.

## Features

- **Server**: Sends periodic Vehicle Announcements and responds to Vehicle Identification Requests
- **Client**: Listens for Vehicle Announcements, stores server IP, and sends Vehicle Identification Request
- **Loopback mode**: For testing client and server on the same host (avoids multicast issues)

## Building

```bash
make
```

This will create two executables:
- `doip_server` - The DoIP server
- `doip_client` - The DoIP client

## Usage

### Running in Loopback Mode (Recommended for Testing)

**Terminal 1 - Start the server:**
```bash
./doip_server --loopback
```

**Terminal 2 - Start the client:**
```bash
./doip_client --loopback
```

### Running in Broadcast Mode (For Network Testing)

**Terminal 1 - Start the server:**
```bash
./doip_server
```

**Terminal 2 - Start the client:**
```bash
./doip_client
```

## How It Works

### Server (Port 13400)
1. Creates two UDP sockets (both bound to port 13400 with SO_REUSEADDR):
- Receive socket: Listens for incoming Vehicle Identification Requests
- Send socket: Sends Vehicle Announcements and responses
2. Starts two threads:
- Listener thread: Continuously listens for requests
- Announcement thread: Sends 5 Vehicle Announcements (every 2 seconds) to port 13401
3. When a Vehicle Identification Request is received:
- Parses the request
- Sends a Vehicle Identification Response back to the client

### Client (Port 13401)
1. Creates two UDP sockets:
- Request socket: Sends Vehicle Identification Requests (unbound, OS assigns port)
- Announcement socket: Bound to port 13401 to receive Vehicle Announcements
2. Workflow:
- Listens for Vehicle Announcement from server
- Extracts vehicle information (VIN, Logical Address, EID, GID, IP address)
- Sends Vehicle Identification Request to the discovered server IP on port 13400
- Waits for and displays the response

## Ports

- **13400**: DoIP UDP Discovery Port (server listens here, client sends requests here)
- **13401**: DoIP Test Equipment Port (client listens here for announcements)

## Protocol Details

### DoIP Header (8 bytes)
- Protocol Version: 0x04
- Inverse Protocol Version: 0xFB
- Payload Type: 2 bytes
- Payload Length: 4 bytes

### Payload Types
- `0x0001`: Vehicle Identification Request (no payload)
- `0x0004`: Vehicle Identification Response (33 bytes minimum)

### Vehicle Identification Response Payload
- VIN: 17 bytes (ASCII)
- Logical Address: 2 bytes
- EID: 6 bytes
- GID: 6 bytes
- Further Action Required: 1 byte
- VIN/GID sync status: 1 byte

## Key Design Decisions

1. **Two sockets on server**: Avoids race conditions between sending announcements and receiving requests
2. **SO_REUSEADDR**: Allows both server sockets to bind to port 13400
3. **Loopback mode**: Uses unicast (127.0.0.1) instead of broadcast for local testing
4. **Non-blocking receive**: Server uses timeout to allow clean shutdown
5. **Separate announcement socket on client**: Dedicated socket for receiving broadcasts on port 13401

## Troubleshooting

If the client doesn't receive announcements:
- Check firewall rules
- Verify both programs are running
- Use `tcpdump` to monitor UDP traffic:
```bash
sudo tcpdump -i any udp port 13400 or udp port 13401 -X
```

## Cleanup

```bash
make clean
```
Loading