Skip to content
This repository was archived by the owner on Dec 17, 2025. It is now read-only.
Merged

Dev #11

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
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![CMake](https://img.shields.io/badge/CMake-3.15+-blue.svg)](https://cmake.org/)
[![doctest](https://img.shields.io/badge/Tests-doctest-green.svg)](https://github.com/doctest/doctest)

C/C++ library for Diagnostics over IP (DoIP) (fork from https://github.com/AVL-DiTEST-DiagDev/libdoip)
C/C++ library for Diagnostics over IP (DoIP) (fork from [https://github.com/AVL-DiTEST-DiagDev/libdoip](https://github.com/AVL-DiTEST-DiagDev/libdoip))

**CAUTION** The current API is under construction any may change at any time.

Expand All @@ -26,9 +26,9 @@ See [Logging](./doc/LOGGING.md) for details.

Quick start — read the generated tutorial for the example server:

- Online (published): https://magolves.github.io/libdoip/index.html
- Online (published): [https://magolves.github.io/libdoip/index.html](https://magolves.github.io/libdoip/index.html)
- Local example page: see `doc/ExampleDoIPServer.md` (included in the Doxygen HTML under "Example DoIP Server Tutorial").
- Example tutorial (direct): https://magolves.github.io/libdoip/ExampleDoIPServer.html
- Example tutorial (direct): [https://magolves.github.io/libdoip/md_doc_ExampleDoIPServer.html](https://magolves.github.io/libdoip/md_doc_ExampleDoIPServer.html)

If you want to generate the docs locally, install Doxygen and Graphviz and
run:
Expand All @@ -47,7 +47,7 @@ xdg-open docs/html/index.html
git clone https://github.com/Magolves/libdoip.git
```

2. Enter the directory 'libdoip' and build the library with:
1. Enter the directory 'libdoip' and build the library with:

```bash
cmake . -Bbuild
Expand Down Expand Up @@ -83,14 +83,13 @@ handlers.

- Example source files: `examples/exampleDoIPServer.cpp`,
`examples/ExampleDoIPServerModel.h`
- Example tutorial (published): https://magolves.github.io/libdoip/ExampleDoIPServer.html
- Example tutorial: [https://magolves.github.io/libdoip/ExampleDoIPServer.html](https://magolves.github.io/libdoip/ExampleDoIPServer.html)

See the "Examples" section in the generated Doxygen main page for
additional annotated links to these files.


## References

- [ISO 13400-2:2019(en) Road vehicles — Diagnostic communication over Internet Protocol (DoIP) — Part 2: Transport protocol and network layer services](<https://www.iso.org/obp/ui/#iso:std:iso:13400:-2:ed-2:v1:en>)
- [Specification of Diagnostic over IP](<https://www.autosar.org/fileadmin/standards/R20-11/CP/AUTOSAR_SWS_DiagnosticOverIP.pdf>)
- [Diagnostics over Internet Protocol (DoIP)](<https://cdn.vector.com/cms/content/know-how/_application-notes/AN-IND-1-026_DoIP_in_CANoe.pdf>)
- [Diagnostics over Internet Protocol (DoIP)](<https://cdn.vector.com/cms/content/know-how/_application-notes/AN-IND-1-026_DoIP_in_CANoe.pdf>)
5 changes: 0 additions & 5 deletions doc/ExampleDoIPServer.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,6 @@ m_model.onDownstreamRequest = [this](IConnectionContext &ctx, const DoIPMessage
Below is a PlantUML diagram illustrating `DoIPServer`, `DoIPConnection`,
`DoIPServerModel` and a downstream UDS/CAN backend interaction.

[\image html ]ServerModel.svg

For GitHub or other markdown viewers that don't process the Doxygen
directive, the SVG is also referenced directly:

![ServerModel](diagrams/ServerModel.svg)

## Logging and debugging tips
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).
101 changes: 50 additions & 51 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,55 +1,54 @@
# Examples CMakeLists.txt

# DoIP Server Example
add_executable(exampleDoIPServer exampleDoIPServer.cpp)
target_link_libraries(exampleDoIPServer
PRIVATE
${DOIP_NAME}
Threads::Threads
set (EXAMPLE_SOURCES
exampleDoIPServer.cpp
exampleDoIPClient.cpp
exampleDoIPDiscover.cpp
)

# Set properties for the example
set_target_properties(exampleDoIPServer PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

# DoIP Client Example
add_executable(exampleDoIPClient exampleDoIPClient.cpp)
target_link_libraries(exampleDoIPClient
PRIVATE
${DOIP_NAME}
)

# Set properties for the client example
set_target_properties(exampleDoIPClient PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

# Simple DoIP Server Example (using high-level API)
add_executable(exampleDoIPServerSimple exampleDoIPServerSimple.cpp)
target_link_libraries(exampleDoIPServerSimple
PRIVATE
${DOIP_NAME}
Threads::Threads
)

# Set properties for the simple server example
set_target_properties(exampleDoIPServerSimple PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

# Disable switch-default warning for examples using spdlog
target_compile_options(exampleDoIPServer PRIVATE -Wno-switch-default)
target_compile_options(exampleDoIPClient PRIVATE -Wno-switch-default)
target_compile_options(exampleDoIPServerSimple PRIVATE -Wno-switch-default)

# Install examples (optional)
install(TARGETS exampleDoIPServer exampleDoIPClient exampleDoIPServerSimple
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/examples
)
foreach(example_source ${EXAMPLE_SOURCES})
get_filename_component(example_name ${example_source} NAME_WE)
add_executable(${example_name} ${example_source})
target_link_libraries(${example_name}
PRIVATE
${DOIP_NAME}
Threads::Threads
)

# Set properties for the example
set_target_properties(${example_name} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON
CXX_EXTENSIONS OFF
)

# Disable switch-default warning for examples using spdlog
target_compile_options(${example_name} PRIVATE -Wno-switch-default)

# Install examples (optional)
install(TARGETS ${example_name}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/examples
)

endforeach()

if (WITH_UNIT_TEST)
enable_testing()
# Integration fixture: start exampleDoIPServer as a daemon
add_test(NAME Integration_StartExampleServer
COMMAND $<TARGET_FILE:exampleDoIPServer> --daemonize --loopback
)
set_tests_properties(Integration_StartExampleServer PROPERTIES FIXTURES_SETUP example_server TIMEOUT 20)

# Integration test: run discover against the running server
add_test(NAME Integration_DiscoverRuns
COMMAND $<TARGET_FILE:exampleDoIPDiscover> --loopback
)
set_tests_properties(Integration_DiscoverRuns PROPERTIES FIXTURES_REQUIRED example_server TIMEOUT 20)

# Teardown fixture: stop exampleDoIPServer
add_test(NAME Integration_StopExampleServer
COMMAND pkill -f exampleDoIPServer
)
set_tests_properties(Integration_StopExampleServer PROPERTIES FIXTURES_CLEANUP example_server TIMEOUT 10)
endif()
Loading