From 7b37fec0c0d8a4fd27f6aedaba55b5e4d23a3b91 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 7 Dec 2025 16:17:19 +0100 Subject: [PATCH 01/20] feat: Add server config, introduce daemonize --- examples/CMakeLists.txt | 57 ++++++-------- examples/exampleDoIPClient.cpp | 4 +- examples/exampleDoIPDiscover.cpp | 65 ++++++++++++++++ examples/exampleDoIPServer.cpp | 94 ++++++++++++++++------- inc/DoIPServer.h | 127 ++++++++----------------------- src/DoIPServer.cpp | 93 +++++++++++++++++++++- 6 files changed, 282 insertions(+), 158 deletions(-) create mode 100644 examples/exampleDoIPDiscover.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4545639..79b2dbe 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,40 +1,33 @@ # 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} -) +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 client example -set_target_properties(exampleDoIPClient PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF -) + # 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(exampleDoIPServer PRIVATE -Wno-switch-default) -target_compile_options(exampleDoIPClient PRIVATE -Wno-switch-default) + # 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 + ) -# Install examples (optional) -install(TARGETS exampleDoIPServer exampleDoIPClient - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/examples -) \ No newline at end of file +endforeach() diff --git a/examples/exampleDoIPClient.cpp b/examples/exampleDoIPClient.cpp index 60103e7..7ab6c83 100644 --- a/examples/exampleDoIPClient.cpp +++ b/examples/exampleDoIPClient.cpp @@ -59,6 +59,8 @@ int main(int argc, char *argv[]) { client.receiveUdpMessage(); } + client.closeUdpConnection(); + // Now start TCP connection for diagnostic communication LOG_DOIP_INFO("Starting TCP connection for diagnostic messages"); client.startTcpConnection(); @@ -84,5 +86,5 @@ int main(int argc, char *argv[]) { std::this_thread::sleep_for(2s); client.closeTcpConnection(); - client.closeUdpConnection(); + //client.closeUdpConnection(); } diff --git a/examples/exampleDoIPDiscover.cpp b/examples/exampleDoIPDiscover.cpp new file mode 100644 index 0000000..cc77986 --- /dev/null +++ b/examples/exampleDoIPDiscover.cpp @@ -0,0 +1,65 @@ +#include "DoIPClient.h" +#include "DoIPMessage.h" +#include "Logger.h" + +#include +#include +#include + +using namespace doip; +using namespace std; + +DoIPClient client; + +static void printUsage(const char *progName) { + cout << "Usage: " << progName << " [OPTIONS]\n"; + cout << "Options:\n"; + cout << " --loopback Use loopback (127.0.0.1) instead of multicast\n"; + cout << " --server Connect to specific server IP\n"; + cout << " --help Show this help message\n"; +} + +int main(int argc, char *argv[]) { + std::cerr << "The client code does not work currently - use at your own risk!\n"; + + string serverAddress = "224.0.0.2"; // Default multicast address + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + string arg = argv[i]; + if (arg == "--loopback") { + serverAddress = "127.0.0.1"; + LOG_DOIP_INFO("Loopback mode enabled - using 127.0.0.1"); + } else if (arg == "--server" && i + 1 < argc) { + serverAddress = argv[++i]; + LOG_DOIP_INFO("Using custom server address: {}", serverAddress); + } else if (arg == "--help") { + printUsage(argv[0]); + return 0; + } else { + cout << "Unknown argument: " << arg << endl; + printUsage(argv[0]); + return 1; + } + } + + LOG_DOIP_INFO("Starting DoIP Client"); + + // Start UDP connections (don't start TCP yet) + client.startUdpConnection(); + client.startAnnouncementListener(); // Listen for Vehicle Announcements on port 13401 + + // Listen for Vehicle Announcements first + LOG_DOIP_INFO("Listening for Vehicle Announcements..."); + client.receiveVehicleAnnouncement(); + + // Send Vehicle Identification Request to configured address + if (client.sendVehicleIdentificationRequest(serverAddress.c_str()) > 0) { + LOG_DOIP_INFO("Vehicle Identification Request sent successfully"); + client.receiveUdpMessage(); + } + + // Now start TCP connection for diagnostic communication + LOG_DOIP_INFO("Discovery complete, closing UDP connections"); + client.closeUdpConnection(); +} diff --git a/examples/exampleDoIPServer.cpp b/examples/exampleDoIPServer.cpp index 24c8a8a..f82184b 100644 --- a/examples/exampleDoIPServer.cpp +++ b/examples/exampleDoIPServer.cpp @@ -16,7 +16,7 @@ using namespace std; static const DoIPAddress LOGICAL_ADDRESS(0x0028); -DoIPServer server; +std::unique_ptr server; std::vector doipReceiver; bool serverActive = false; std::unique_ptr tcpConnection(nullptr); @@ -30,7 +30,7 @@ void listenTcp(); void listenUdp() { LOG_UDP_INFO("UDP listener thread started"); while (serverActive) { - ssize_t result = server.receiveUdpMessage(); + ssize_t result = server->receiveUdpMessage(); // If timeout (result == 0), sleep briefly to prevent CPU spinning if (result == 0) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); @@ -45,7 +45,7 @@ void listenTcp() { LOG_UDP_INFO("TCP listener thread started"); while (true) { - tcpConnection = server.waitForTcpConnection(); + tcpConnection = server->waitForTcpConnection(); while (tcpConnection->isSocketActive()) { tcpConnection->receiveTcpMessage(); @@ -53,39 +53,48 @@ void listenTcp() { } } -static void ConfigureDoipServer() { - // VIN needs to have a fixed length of 17 bytes. - // Shorter VINs will be padded with '0' - server.setVIN("EXAMPLESERVER"); - server.setLogicalGatewayAddress(LOGICAL_ADDRESS.toUint16()); - server.setGID(0); - server.setFAR(DoIPFurtherAction::NoFurtherAction); - server.setEID(0); - - // be more relaxed for testing purposes - server.setAnnounceInterval(2000); - server.setAnnounceNum(10); - - // doipserver->setAnnounceNum(tempNum); - // doipserver->setAnnounceInterval(tempInterval); -} +// Default example settings are applied in main when building ServerConfig static void printUsage(const char *progName) { cout << "Usage: " << progName << " [OPTIONS]\n"; cout << "Options:\n"; cout << " --loopback Use loopback (127.0.0.1) for announcements instead of broadcast\n"; + cout << " --daemonize / --no-daemonize Enable or disable daemon mode (default: enabled)\n"; + cout << " --eid <6chars> Set EID (6 ASCII chars)\n"; + cout << " --gid <6chars> Set GID (6 ASCII chars)\n"; + cout << " --vin <17chars> Set VIN (17 ASCII chars)\n"; + cout << " --logical-address Set logical gateway address (default: 0x0E00)\n"; cout << " --help Show this help message\n"; } int main(int argc, char *argv[]) { bool useLoopback = false; + bool daemonize = true; + std::string eid_str; + std::string gid_str; + std::string vin_str = "EXAMPLESERVER"; + std::string logical_addr_str; // Parse command line arguments - for (int i = 1; i < argc; i++) { + for (int i = 1; i < argc; ++i) { string arg = argv[i]; if (arg == "--loopback") { useLoopback = true; LOG_DOIP_INFO("Loopback mode enabled"); + } else if (arg == "--daemonize") { + daemonize = true; + LOG_DOIP_INFO("Daemonize enabled"); + } else if (arg == "--no-daemonize") { + daemonize = false; + LOG_DOIP_INFO("Daemonize disabled"); + } else if (arg == "--eid" && i + 1 < argc) { + eid_str = argv[++i]; + } else if (arg == "--gid" && i + 1 < argc) { + gid_str = argv[++i]; + } else if (arg == "--vin" && i + 1 < argc) { + vin_str = argv[++i]; + } else if (arg == "--logical-address" && i + 1 < argc) { + logical_addr_str = argv[++i]; } else if (arg == "--help") { printUsage(argv[0]); return 0; @@ -100,19 +109,48 @@ int main(int argc, char *argv[]) { doip::Logger::setLevel(spdlog::level::debug); LOG_DOIP_INFO("Starting DoIP Server Example"); - ConfigureDoipServer(); - - // Configure server based on mode - if (useLoopback) { - server.setAnnouncementMode(true); + // Build server config from example settings and CLI + doip::ServerConfig cfg; + cfg.loopback = useLoopback; + cfg.daemonize = daemonize; + if (!vin_str.empty()) cfg.vin = DoIPVIN(vin_str); + if (!eid_str.empty()) cfg.eid = DoIPEID(eid_str); + if (!gid_str.empty()) cfg.gid = DoIPGID(gid_str); + if (!logical_addr_str.empty()) { + // parse hex (0x...) or decimal + try { + size_t pos = 0; + unsigned long val = 0; + if (logical_addr_str.rfind("0x", 0) == 0 || logical_addr_str.rfind("0X", 0) == 0) { + val = std::stoul(logical_addr_str, &pos, 16); + } else { + val = std::stoul(logical_addr_str, &pos, 0); + } + cfg.logicalAddress = static_cast(val & 0xFFFF); + } catch (...) { + LOG_DOIP_WARN("Failed to parse logical address '{}', using default 0x0E00", logical_addr_str); + } + } else { + cfg.logicalAddress = LOGICAL_ADDRESS.toUint16(); } - if (!server.setupUdpSocket()) { + server = std::make_unique(cfg); + server->setAnnouncementMode(cfg.loopback); + server->setLogicalGatewayAddress(cfg.logicalAddress); + server->setVIN(cfg.vin); + // Apply defaults used previously in example + server->setGID(0); + server->setFAR(DoIPFurtherAction::NoFurtherAction); + server->setEID(0); + server->setAnnounceInterval(2000); + server->setAnnounceNum(10); + + if (!server->setupUdpSocket()) { LOG_DOIP_CRITICAL("Failed to set up UDP socket"); return 1; } - if (!server.setupTcpSocket()) { + if (!server->setupTcpSocket()) { LOG_DOIP_CRITICAL("Failed to set up TCP socket"); return 1; } @@ -122,7 +160,7 @@ int main(int argc, char *argv[]) { doipReceiver.push_back(thread(&listenUdp)); doipReceiver.push_back(thread(&listenTcp)); - server.sendVehicleAnnouncement(); + server->sendVehicleAnnouncement(); LOG_DOIP_INFO("Vehicle announcement sent"); doipReceiver.at(0).join(); diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 890b93a..cc45392 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,28 @@ namespace doip { +/** + * @brief Server configuration structure used to initialize a DoIP server. + */ +struct ServerConfig { + // EID and GID as fixed identifiers (6 bytes). Default: zeros. + DoIPEID eid = DoIPEID::Zero; + DoIPGID gid = DoIPGID::Zero; + + // VIN as fixed identifier (17 bytes). Default: zeros. + DoIPVIN vin = DoIPVIN::Zero; + + // Logical/server address (default 0x0E00) + uint16_t logicalAddress = 0x0E00; + + // Use loopback announcements instead of broadcast + bool loopback = false; + + // Run the server as a daemon by default + bool daemonize = true; +}; + + constexpr int DOIP_SERVER_PORT = 13400; /** @@ -47,9 +70,8 @@ using ConnectionAcceptedHandler = std::function(D class DoIPServer { public: - DoIPServer() { - m_receiveBuf.reserve(MAX_ISOTP_MTU); - }; + explicit DoIPServer(); + explicit DoIPServer(const ServerConfig &config); ~DoIPServer(); @@ -67,33 +89,9 @@ class DoIPServer { [[nodiscard]] bool setupUdpSocket(); + [[nodiscard]] ssize_t receiveUdpMessage(); - // === High-level API (automatic mode) === - /** - * @brief Start the DoIP server with automatic connection handling - * - * This method sets up TCP and UDP sockets, spawns background threads to handle - * UDP messages and TCP connections, and returns immediately. The server continues - * running until stop() is called. - * - * @param onConnectionAccepted Callback invoked when a new client connects. - * Return a DoIPServerModel to accept the connection, - * or std::nullopt to reject it. - * @param sendAnnouncements If true, send vehicle announcements on startup - * @return true if server started successfully, false otherwise - */ - template - bool start(ConnectionAcceptedHandler onConnectionAccepted, bool sendAnnouncements = true); - - /** - * @brief Stop the DoIP server and wait for all threads to terminate - * - * This gracefully shuts down all active connections and background threads. - * Blocks until all cleanup is complete. - */ - void stop(); - /** * @brief Check if the server is currently running */ @@ -119,6 +117,7 @@ class DoIPServer { bool setEIDdefault(); void setVIN(const std::string &VINString); + void setVIN(const DoIPVIN &vin); const DoIPVIN &getVIN() const { return m_VIN; } void setEID(const uint64_t inputEID); @@ -154,6 +153,12 @@ class DoIPServer { std::vector m_workerThreads; ConnectionAcceptedHandler m_connectionHandler; + // Server configuration + ServerConfig m_config; + + void stop(); + void daemonize(); + ssize_t reactToReceivedUdpMessage(size_t bytesRead); ssize_t sendUdpMessage(const uint8_t *message, size_t messageLength); @@ -186,19 +191,6 @@ std::unique_ptr DoIPServer::waitForTcpConnection() { return nullptr; } - // Create a default server model with the gateway address - // Model model; - // model.serverAddress = m_gatewayAddress; - // if (model.onDiagnosticMessage == nullptr) { - // model.onDiagnosticMessage = [](IConnectionContext& ctx, const DoIPMessage &msg) noexcept -> DoIPDiagnosticAck { - // (void)ctx; - // (void)msg; - // LOG_DOIP_CRITICAL("Diagnostic message received on default-constructed Model"); - // // Default: always ACK - // return std::nullopt; - // }; - // } - return std::unique_ptr(new DoIPConnection(tcpSocket, std::make_unique())); } @@ -228,59 +220,6 @@ void DoIPServer::tcpListenerThread() { LOG_DOIP_INFO("TCP listener thread stopped"); } -/* - * High-level API: Start the server with automatic connection handling - */ -template -bool DoIPServer::start(ConnectionAcceptedHandler onConnectionAccepted, bool sendAnnouncements) { - if (m_running.load()) { - LOG_DOIP_WARN("Server is already running"); - return false; - } - - if (!onConnectionAccepted) { - LOG_DOIP_ERROR("Connection handler callback is required"); - return false; - } - - m_connectionHandler = onConnectionAccepted; - - // Setup sockets - if (!setupUdpSocket()) { - LOG_DOIP_ERROR("Failed to setup UDP socket"); - return false; - } - - - if (setupTcpSocket()) { - LOG_DOIP_ERROR("Failed to setup TCP socket"); - closeUdpSocket(); - return false; - } - - // Start background threads - m_running.store(true); - - try { - m_workerThreads.emplace_back(&DoIPServer::udpListenerThread, this); - m_workerThreads.emplace_back(&DoIPServer::tcpListenerThread, this); - - LOG_DOIP_INFO("DoIP Server started successfully"); - - // Send vehicle announcements if requested - if (sendAnnouncements) { - sendVehicleAnnouncement(); - } - - return true; - } catch (const std::exception &e) { - LOG_DOIP_ERROR("Failed to start worker threads: {}", e.what()); - m_running.store(false); - closeUdpSocket(); - closeTcpSocket(); - return false; - } -} } // namespace doip diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index bb74162..1bbca17 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -18,15 +18,100 @@ const char *DEFAULT_IFACE = "en0"; #pragma error "Unsupported platform" #endif +#include +#include +#include +#include + DoIPServer::~DoIPServer() { if (m_running.load()) { stop(); } } +DoIPServer::DoIPServer() + : DoIPServer(ServerConfig()) {} + +DoIPServer::DoIPServer(const ServerConfig &config) + : m_config(config) { + m_receiveBuf.reserve(MAX_ISOTP_MTU); + + // Apply VIN/EID/GID from config (types enforce size/padding) + m_VIN = m_config.vin; + m_EID = m_config.eid; + m_GID = m_config.gid; + + // Set logical gateway address from config + m_gatewayAddress.update(m_config.logicalAddress); + + // Apply EID/GID if provided (interpreted as numeric where possible) + // (old parsing logic removed - ServerConfig uses typed identifiers) + if (!m_config.loopback) { + setAnnouncementMode(false); + } else { + setAnnouncementMode(true); + } + + if (m_config.daemonize) { + daemonize(); + } +} + +void DoIPServer::daemonize() { + LOG_DOIP_INFO("Daemonizing DoIP Server..."); + pid_t pid = fork(); + if (pid < 0) { + LOG_DOIP_ERROR("First fork failed: {}", strerror(errno)); + return; + } + + if (pid > 0) { + // Parent exits; child continues + _exit(0); + } + + // Child: create new session and become session leader + if (setsid() < 0) { + LOG_DOIP_ERROR("setsid failed: {}", strerror(errno)); + return; + } + + // Second fork to ensure the daemon can't reacquire a tty + pid = fork(); + if (pid < 0) { + LOG_DOIP_ERROR("Second fork failed: {}", strerror(errno)); + return; + } + if (pid > 0) { + _exit(0); + } + + // Set file mode creation mask to a safe default + umask(0); + + // Change working directory to root to avoid blocking mounts + if (chdir("/") != 0) { + LOG_DOIP_WARN("chdir to / failed: {}", strerror(errno)); + } + + // Close and redirect standard file descriptors to /dev/null + int fd = open("/dev/null", O_RDWR); + if (fd >= 0) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) { + close(fd); + } + } else { + LOG_DOIP_WARN("Failed to open /dev/null: {}", strerror(errno)); + } + + LOG_DOIP_INFO("DoIP Server daemonized and running"); +} /* - * High-level API: Stop the server and cleanup + * Stop the server and cleanup */ void DoIPServer::stop() { LOG_DOIP_INFO("Stopping DoIP Server..."); @@ -67,8 +152,6 @@ void DoIPServer::udpListenerThread() { LOG_DOIP_INFO("UDP listener thread stopped"); } - - /* * Background thread: Handle individual TCP connection */ @@ -263,6 +346,10 @@ void DoIPServer::setVIN(const std::string &VINString) { m_VIN = DoIPVIN(VINString); } +void DoIPServer::setVIN(const DoIPVIN &vin) { + m_VIN = vin; +} + void DoIPServer::setLogicalGatewayAddress(const unsigned short inputLogAdd) { m_gatewayAddress.update(inputLogAdd); } From 0891ffd7c0ffd1cef3bbdcb35d0e658156f32998 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Sun, 7 Dec 2025 17:38:38 +0100 Subject: [PATCH 02/20] WIP: Track UDP reception problem on server --- doc/Fix-UDP-reception.md | 123 +++++++++++++++++++++++++++++++ examples/CMakeLists.txt | 20 +++++ examples/exampleDoIPClient.cpp | 5 +- examples/exampleDoIPDiscover.cpp | 5 +- examples/exampleDoIPServer.cpp | 7 +- inc/DoIPClient.h | 3 +- inc/DoIPServer.h | 7 +- src/DoIPClient.cpp | 9 ++- src/DoIPServer.cpp | 77 ++++++++++--------- test/CMakeLists.txt | 18 +++++ 10 files changed, 223 insertions(+), 51 deletions(-) create mode 100644 doc/Fix-UDP-reception.md diff --git a/doc/Fix-UDP-reception.md b/doc/Fix-UDP-reception.md new file mode 100644 index 0000000..aab174c --- /dev/null +++ b/doc/Fix-UDP-reception.md @@ -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). \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 79b2dbe..5bc5269 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,3 +31,23 @@ foreach(example_source ${EXAMPLE_SOURCES}) ) endforeach() + +if (WITH_UNIT_TEST) + # Integration fixture: start exampleDoIPServer as a daemon + add_test(NAME Integration_StartExampleServer + COMMAND $ --daemonize --loopback + ) + set_tests_properties(Integration_StartExampleServer PROPERTIES FIXTURES_SETUP example_server TIMEOUT 10) + + # Integration test: run discover against the running server + add_test(NAME Integration_DiscoverRuns + COMMAND $ --loopback + ) + set_tests_properties(Integration_DiscoverRuns PROPERTIES FIXTURES_REQUIRED example_server TIMEOUT 10) + + # 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() diff --git a/examples/exampleDoIPClient.cpp b/examples/exampleDoIPClient.cpp index 7ab6c83..fe18cdf 100644 --- a/examples/exampleDoIPClient.cpp +++ b/examples/exampleDoIPClient.cpp @@ -51,7 +51,10 @@ int main(int argc, char *argv[]) { // Listen for Vehicle Announcements first LOG_DOIP_INFO("Listening for Vehicle Announcements..."); - client.receiveVehicleAnnouncement(); + if (!client.receiveVehicleAnnouncement()) { + LOG_DOIP_WARN("No Vehicle Announcement received"); + return EXIT_FAILURE; + } // Send Vehicle Identification Request to configured address if (client.sendVehicleIdentificationRequest(serverAddress.c_str()) > 0) { diff --git a/examples/exampleDoIPDiscover.cpp b/examples/exampleDoIPDiscover.cpp index cc77986..1bc7fd9 100644 --- a/examples/exampleDoIPDiscover.cpp +++ b/examples/exampleDoIPDiscover.cpp @@ -51,7 +51,10 @@ int main(int argc, char *argv[]) { // Listen for Vehicle Announcements first LOG_DOIP_INFO("Listening for Vehicle Announcements..."); - client.receiveVehicleAnnouncement(); + if (!client.receiveVehicleAnnouncement()) { + LOG_DOIP_WARN("No Vehicle Announcement received"); + return EXIT_FAILURE; + } // Send Vehicle Identification Request to configured address if (client.sendVehicleIdentificationRequest(serverAddress.c_str()) > 0) { diff --git a/examples/exampleDoIPServer.cpp b/examples/exampleDoIPServer.cpp index f82184b..ec851a8 100644 --- a/examples/exampleDoIPServer.cpp +++ b/examples/exampleDoIPServer.cpp @@ -69,7 +69,7 @@ static void printUsage(const char *progName) { int main(int argc, char *argv[]) { bool useLoopback = false; - bool daemonize = true; + bool daemonize = false; std::string eid_str; std::string gid_str; std::string vin_str = "EXAMPLESERVER"; @@ -135,13 +135,8 @@ int main(int argc, char *argv[]) { } server = std::make_unique(cfg); - server->setAnnouncementMode(cfg.loopback); - server->setLogicalGatewayAddress(cfg.logicalAddress); - server->setVIN(cfg.vin); // Apply defaults used previously in example - server->setGID(0); server->setFAR(DoIPFurtherAction::NoFurtherAction); - server->setEID(0); server->setAnnounceInterval(2000); server->setAnnounceNum(10); diff --git a/inc/DoIPClient.h b/inc/DoIPClient.h index a82f8e8..166bf83 100644 --- a/inc/DoIPClient.h +++ b/inc/DoIPClient.h @@ -29,7 +29,8 @@ class DoIPClient { ssize_t sendVehicleIdentificationRequest(const char *inet_address); void receiveRoutingActivationResponse(); void receiveUdpMessage(); - void receiveVehicleAnnouncement(); + [[nodiscard]] + bool receiveVehicleAnnouncement(); /* * Send the builded request over the tcp-connection to server */ diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index cc45392..288d431 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -46,9 +46,11 @@ struct ServerConfig { bool loopback = false; // Run the server as a daemon by default - bool daemonize = true; + bool daemonize = false; }; +const ServerConfig DefaultServerConfig{}; + constexpr int DOIP_SERVER_PORT = 13400; @@ -70,8 +72,7 @@ using ConnectionAcceptedHandler = std::function(D class DoIPServer { public: - explicit DoIPServer(); - explicit DoIPServer(const ServerConfig &config); + explicit DoIPServer(const ServerConfig &config = DefaultServerConfig); ~DoIPServer(); diff --git a/src/DoIPClient.cpp b/src/DoIPClient.cpp index bd7c19a..c601300 100644 --- a/src/DoIPClient.cpp +++ b/src/DoIPClient.cpp @@ -192,7 +192,7 @@ void DoIPClient::receiveUdpMessage() { LOG_UDP_INFO("RX: {}", fmt::streamed(msg)); } -void DoIPClient::receiveVehicleAnnouncement() { +bool DoIPClient::receiveVehicleAnnouncement() { unsigned int length = sizeof(_announcementAddr); int bytesRead; @@ -212,13 +212,13 @@ void DoIPClient::receiveVehicleAnnouncement() { } else { LOG_UDP_ERROR("Error receiving Vehicle Announcement: {}", strerror(errno)); } - return; + return false; } auto optMsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); if (!optMsg.has_value()) { LOG_UDP_ERROR("Failed to parse Vehicle Announcement message"); - return; + return false; } DoIPMessage msg = optMsg.value(); @@ -228,7 +228,9 @@ void DoIPClient::receiveVehicleAnnouncement() { if (msg.getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse) { parseVIResponseInformation(msg.data()); displayVIResponseInformation(); + return true; } + return false; } ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { @@ -250,6 +252,7 @@ ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { DoIPMessage vehicleIdReq = message::makeVehicleIdentificationRequest(); ssize_t bytesSent = sendto(_sockFd_udp, vehicleIdReq.data(), vehicleIdReq.size(), 0, reinterpret_cast(&_serverAddr), sizeof(_serverAddr)); + LOG_UDP_INFO("Sent Vehicle Identification Request to {}:{}", inet_address, ntohs(_serverAddr.sin_port)); if (bytesSent > 0) { LOG_DOIP_INFO("Sending Vehicle Identification Request"); diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index 1bbca17..87264ce 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -18,10 +18,10 @@ const char *DEFAULT_IFACE = "en0"; #pragma error "Unsupported platform" #endif -#include -#include -#include #include +#include +#include +#include DoIPServer::~DoIPServer() { if (m_running.load()) { @@ -29,9 +29,6 @@ DoIPServer::~DoIPServer() { } } -DoIPServer::DoIPServer() - : DoIPServer(ServerConfig()) {} - DoIPServer::DoIPServer(const ServerConfig &config) : m_config(config) { m_receiveBuf.reserve(MAX_ISOTP_MTU); @@ -223,9 +220,18 @@ bool DoIPServer::setupUdpSocket() { return false; } - // setting the IP DoIPAddress for Multicast - setMulticastGroup("224.0.0.2"); - LOG_UDP_INFO("UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT); + // setting the IP DoIPAddress for Multicast/Broadcast + if (!m_useLoopbackAnnouncements) { // + setMulticastGroup("224.0.0.2"); + LOG_UDP_INFO("UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT); + } else { + LOG_UDP_INFO("UDP socket successfully bound to port {} with broadcast", DOIP_UDP_DISCOVERY_PORT); + } + LOG_UDP_DEBUG( + "Socket {} bound to {}:{}", + m_udp_sock, + inet_ntoa(m_serverAddress.sin_addr), + ntohs(m_serverAddress.sin_port)); return true; } @@ -246,43 +252,41 @@ void DoIPServer::closeUdpSocket() { * or -1 if error occurred */ ssize_t DoIPServer::receiveUdpMessage() { - // Set socket timeout to prevent blocking indefinitely struct timeval timeout; - timeout.tv_sec = 1; // 1 second timeout + timeout.tv_sec = 1; timeout.tv_usec = 0; setsockopt(m_udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); struct sockaddr_in clientAddr; - unsigned int length = sizeof(clientAddr); - const ssize_t readBytes = recvfrom(m_udp_sock, m_receiveBuf.data(), m_receiveBuf.size(), 0, reinterpret_cast(&clientAddr), &length); - - if (readBytes < 0) { - if (errno == EAGAIN) { - // Timeout - this is normal, just continue - return 0; + socklen_t length = sizeof(clientAddr); + + const ssize_t readBytes = recvfrom( + m_udp_sock, + m_receiveBuf.data(), + m_receiveBuf.size(), + 0, + reinterpret_cast(&clientAddr), + &length); + + const int recvErrno = errno; // Save errno immediately + // LOG_UDP_DEBUG("recvfrom returned: {} (errno: {})", readBytes, recvErrno); + + if (readBytes <= 0) { + if (recvErrno == EAGAIN) { // EAGAIN sufficient: error: logical ‘or’ of equal expressions [-Werror=logical-op] 271 | if (recvErrno == EAGAIN || recvErrno == EWOULDBLOCK) { + return 0; // Timeout (no sleep here) } else { - LOG_UDP_ERROR("Error receiving UDP message: {}", strerror(errno)); + LOG_UDP_ERROR("Error receiving UDP message: {}", strerror(recvErrno)); return -1; } } - // Don't log if no data received (can happen with some socket configurations) - if (readBytes > 0) { - LOG_UDP_INFO("RX {} bytes from {}:{}", readBytes, - inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); - } else { - // For debugging: log zero-byte messages at debug level - LOG_UDP_DEBUG("RX 0 bytes from {}:{}", - inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); - return 0; // Return early for zero-byte messages - } + LOG_UDP_INFO("RX {} bytes from {}:{}", + readBytes, + inet_ntoa(clientAddr.sin_addr), + ntohs(clientAddr.sin_port)); - // Store client address for response m_clientAddress = clientAddr; - - ssize_t sentBytes = reactToReceivedUdpMessage(static_cast(readBytes)); - - return sentBytes; + return reactToReceivedUdpMessage(static_cast(readBytes)); } /* @@ -306,7 +310,7 @@ ssize_t DoIPServer::reactToReceivedUdpMessage(size_t bytesRead) { switch (plType) { case DoIPPayloadType::VehicleIdentificationRequest: { DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_gatewayAddress, m_EID, m_GID); - LOG_DOIP_PROTOCOL("TX {}", fmt::streamed(msg)); + LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); ssize_t sendedBytes = sendUdpMessage(msg.data(), DOIP_HEADER_SIZE + msg.size()); return static_cast(sendedBytes); @@ -438,7 +442,8 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); if (sentBytes > 0) { - LOG_UDP_INFO("Sent Vehicle Announcement"); + LOG_UDP_INFO("Sent Vehicle Announcement {}/{}: {} bytes to {}:{}", i + 1, m_announceNum, sentBytes, + inet_ntoa(m_clientAddress.sin_addr), ntohs(m_clientAddress.sin_port)); } else { LOG_UDP_ERROR("Failed sending Vehicle Announcement: {}", strerror(errno)); LOG_UDP_ERROR("Message: {}", fmt::streamed(msg)); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d1a7839..af30bc7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,3 +29,21 @@ doctest_discover_tests(${DOIP_NAME}_tests TIMEOUT 30 ) +# Integration fixture: start exampleDoIPServer as a daemon +add_test(NAME Integration_StartExampleServer + COMMAND $ --daemonize +) +set_tests_properties(Integration_StartExampleServer PROPERTIES FIXTURES_SETUP example_server TIMEOUT 10) + +# Integration test: run discover against the running server +add_test(NAME Integration_DiscoverRuns + COMMAND $ +) +set_tests_properties(Integration_DiscoverRuns PROPERTIES FIXTURES_REQUIRED example_server TIMEOUT 10) + +# 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) + From c53d7be7bf7a4e8dc66f9e2a5829f31e9114c320 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 8 Dec 2025 17:04:19 +0100 Subject: [PATCH 03/20] fix: Split up rx/tx socket (WIP - still not work) --- inc/DoIPServer.h | 3 +- src/DoIPClient.cpp | 2 +- src/DoIPServer.cpp | 131 ++++++++++++++++++++++++++++----------------- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 288d431..9a9b51d 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -131,7 +131,8 @@ class DoIPServer { private: int m_tcp_sock{-1}; - int m_udp_sock{-1}; + int m_udp_receive_sock{-1}; + int m_udp_send_sock{-1}; struct sockaddr_in m_serverAddress {}; struct sockaddr_in m_clientAddress {}; ByteArray m_receiveBuf{}; diff --git a/src/DoIPClient.cpp b/src/DoIPClient.cpp index c601300..b60ed06 100644 --- a/src/DoIPClient.cpp +++ b/src/DoIPClient.cpp @@ -41,7 +41,7 @@ void DoIPClient::startUdpConnection() { LOG_UDP_INFO("Client-UDP-Socket created successfully"); _serverAddr.sin_family = AF_INET; - _serverAddr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); + _serverAddr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); // 13400 _serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); _clientAddr.sin_family = AF_INET; diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index 87264ce..18a5bf4 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -129,26 +129,6 @@ void DoIPServer::stop() { LOG_DOIP_INFO("DoIP Server stopped"); } -/* - * Background thread: UDP message listener - */ -void DoIPServer::udpListenerThread() { - LOG_DOIP_INFO("UDP listener thread started"); - - while (m_running.load()) { - ssize_t result = receiveUdpMessage(); - - // If timeout (result == 0), continue without delay - // The socket already has a timeout configured - if (result < 0 && m_running.load()) { - // Only log errors if we're still supposed to be running - LOG_UDP_DEBUG("UDP receive error, continuing..."); - } - } - - LOG_DOIP_INFO("UDP listener thread stopped"); -} - /* * Background thread: Handle individual TCP connection */ @@ -200,26 +180,57 @@ bool DoIPServer::setupTcpSocket() { return true; } +/* + * Closes the socket for this server + */ +void DoIPServer::closeTcpSocket() { + close(m_tcp_sock); +} + + bool DoIPServer::setupUdpSocket() { LOG_UDP_DEBUG("Setting up UDP socket on port {}", DOIP_UDP_DISCOVERY_PORT); - m_udp_sock = socket(AF_INET, SOCK_DGRAM, 0); + m_udp_receive_sock = socket(AF_INET, SOCK_DGRAM, 0); m_serverAddress.sin_family = AF_INET; m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); - if (m_udp_sock < 0) { + if (m_udp_receive_sock < 0) { LOG_UDP_ERROR("Failed to create UDP socket: {}", strerror(errno)); return false; } + int flags = fcntl(m_udp_receive_sock, F_GETFL, 0); + fcntl(m_udp_receive_sock, F_SETFL, flags | O_NONBLOCK); + + int reuse = 1; + setsockopt(m_udp_receive_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + // binds the socket to any IP DoIPAddress and the Port Number 13400 - if (bind(m_udp_sock, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)) < 0) { + if (bind(m_udp_receive_sock, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)) < 0) { LOG_UDP_ERROR("Failed to bind UDP socket: {}", strerror(errno)); return false; } + // Socket to send announcements + m_udp_send_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (m_udp_send_sock < 0) { + LOG_UDP_ERROR("Failed to create UDP send socket: {}", strerror(errno)); + close(m_udp_receive_sock); + return false; + } + + setsockopt(m_udp_send_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + if (bind(m_udp_send_sock, reinterpret_cast(&m_serverAddress), + sizeof(m_serverAddress)) < 0) { + LOG_UDP_ERROR("Failed to bind UDP send socket: {}", strerror(errno)); + close(m_udp_receive_sock); + return false; + } + // setting the IP DoIPAddress for Multicast/Broadcast if (!m_useLoopbackAnnouncements) { // setMulticastGroup("224.0.0.2"); @@ -227,23 +238,39 @@ bool DoIPServer::setupUdpSocket() { } else { LOG_UDP_INFO("UDP socket successfully bound to port {} with broadcast", DOIP_UDP_DISCOVERY_PORT); } + LOG_UDP_DEBUG( "Socket {} bound to {}:{}", - m_udp_sock, + m_udp_receive_sock, inet_ntoa(m_serverAddress.sin_addr), ntohs(m_serverAddress.sin_port)); + return true; } /* - * Closes the socket for this server + * Background thread: UDP message listener */ -void DoIPServer::closeTcpSocket() { - close(m_tcp_sock); +void DoIPServer::udpListenerThread() { + LOG_DOIP_INFO("UDP listener thread started"); + + while (m_running.load()) { + ssize_t result = receiveUdpMessage(); + + // If timeout (result == 0), continue without delay + // The socket already has a timeout configured + if (result < 0 && m_running.load()) { + // Only log errors if we're still supposed to be running + LOG_UDP_DEBUG("UDP receive error, continuing..."); + } + } + + LOG_DOIP_INFO("UDP listener thread stopped"); } void DoIPServer::closeUdpSocket() { - close(m_udp_sock); + close(m_udp_receive_sock); + close(m_udp_send_sock); } /* @@ -253,15 +280,15 @@ void DoIPServer::closeUdpSocket() { */ ssize_t DoIPServer::receiveUdpMessage() { struct timeval timeout; - timeout.tv_sec = 1; - timeout.tv_usec = 0; - setsockopt(m_udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + timeout.tv_sec = 0; + timeout.tv_usec = 100000; // 100ms + setsockopt(m_udp_receive_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); struct sockaddr_in clientAddr; socklen_t length = sizeof(clientAddr); const ssize_t readBytes = recvfrom( - m_udp_sock, + m_udp_receive_sock, m_receiveBuf.data(), m_receiveBuf.size(), 0, @@ -326,10 +353,10 @@ ssize_t DoIPServer::reactToReceivedUdpMessage(size_t bytesRead) { ssize_t DoIPServer::sendUdpMessage(const uint8_t *message, size_t messageLength) { // sendUdpMessage after receiving a message from the client // if the server receives a message from a client, than the response should be send back to the client address and port - m_clientAddress.sin_port = m_serverAddress.sin_port; - m_clientAddress.sin_addr.s_addr = m_serverAddress.sin_addr.s_addr; + // m_clientAddress.sin_port = m_serverAddress.sin_port; + // m_clientAddress.sin_addr.s_addr = m_serverAddress.sin_addr.s_addr; - int result = sendto(m_udp_sock, message, messageLength, 0, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); + int result = sendto(m_udp_send_sock, message, messageLength, 0, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); return result; } @@ -391,7 +418,7 @@ void DoIPServer::setMulticastGroup(const char *address) { int loop = 1; // set Option using the same Port for multiple Sockets - int setPort = setsockopt(m_udp_sock, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)); + int setPort = setsockopt(m_udp_receive_sock, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)); if (setPort < 0) { LOG_UDP_ERROR("Setting Port Error"); @@ -403,7 +430,7 @@ void DoIPServer::setMulticastGroup(const char *address) { mreq.imr_interface.s_addr = htonl(INADDR_ANY); // set Option to join Multicast Group - int setGroup = setsockopt(m_udp_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); + int setGroup = setsockopt(m_udp_receive_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); if (setGroup < 0) { LOG_UDP_ERROR("Setting address failed: {}", strerror(errno)); @@ -414,11 +441,11 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { // Choose address based on mode const char *address = m_useLoopbackAnnouncements ? "127.0.0.1" : "255.255.255.255"; - - // setting the destination port for the Announcement to 13401 - m_clientAddress.sin_port = htons(13401); - - int setAddressError = inet_aton(address, &(m_clientAddress.sin_addr)); + // Build destination address for the announcement + struct sockaddr_in destAddr{}; + destAddr.sin_family = AF_INET; + destAddr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); + int setAddressError = inet_aton(address, &destAddr.sin_addr); if (setAddressError != 0) { LOG_DOIP_INFO("{} address set successfully: {}", @@ -426,8 +453,8 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { } if (!m_useLoopbackAnnouncements) { - // Only set broadcast option for broadcast mode - int socketError = setsockopt(m_udp_sock, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); + // Only set broadcast option for broadcast mode on the send socket + int socketError = setsockopt(m_udp_send_sock, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); if (socketError == 0) { LOG_DOIP_INFO("Broadcast Option set successfully"); } @@ -439,15 +466,21 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_gatewayAddress, m_EID, m_GID, m_FurtherActionReq); for (int i = 0; i < m_announceNum; i++) { - sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); + sentBytes = sendto(m_udp_send_sock, msg.data(), msg.size(), 0, reinterpret_cast(&destAddr), sizeof(destAddr)); - if (sentBytes > 0) { - LOG_UDP_INFO("Sent Vehicle Announcement {}/{}: {} bytes to {}:{}", i + 1, m_announceNum, sentBytes, - inet_ntoa(m_clientAddress.sin_addr), ntohs(m_clientAddress.sin_port)); - } else { + if (sentBytes < 0) { LOG_UDP_ERROR("Failed sending Vehicle Announcement: {}", strerror(errno)); LOG_UDP_ERROR("Message: {}", fmt::streamed(msg)); + return -1; + } else { + if (sentBytes == 0) { + LOG_UDP_WARN("Vehicle Announcement sent incomplete: {}/{} bytes", sentBytes, msg.size()); + } else { + LOG_UDP_INFO("Vehicle Announcement sent successfully: {}/{} bytes", sentBytes, msg.size()); + } } + LOG_UDP_INFO("Sent Vehicle Announcement {}/{}: {} bytes to {}:{}", i + 1, m_announceNum, sentBytes, + inet_ntoa(destAddr.sin_addr), ntohs(destAddr.sin_port)); usleep(m_announceInterval * 1000); } return sentBytes; From b0aa415d9f7725c58ceda3b5c5a0a7f2baecd684 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 8 Dec 2025 17:04:52 +0100 Subject: [PATCH 04/20] doc: Add generated UDP example --- doc/udp/Makefile | 16 +++ doc/udp/README.md | 117 ++++++++++++++++ doc/udp/doip_client.c | 308 ++++++++++++++++++++++++++++++++++++++++++ doc/udp/doip_server.c | 285 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 726 insertions(+) create mode 100644 doc/udp/Makefile create mode 100644 doc/udp/README.md create mode 100644 doc/udp/doip_client.c create mode 100644 doc/udp/doip_server.c diff --git a/doc/udp/Makefile b/doc/udp/Makefile new file mode 100644 index 0000000..97121ff --- /dev/null +++ b/doc/udp/Makefile @@ -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 diff --git a/doc/udp/README.md b/doc/udp/README.md new file mode 100644 index 0000000..66c33b8 --- /dev/null +++ b/doc/udp/README.md @@ -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 +``` diff --git a/doc/udp/doip_client.c b/doc/udp/doip_client.c new file mode 100644 index 0000000..e7c60bf --- /dev/null +++ b/doc/udp/doip_client.c @@ -0,0 +1,308 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOIP_UDP_DISCOVERY_PORT 13400 +#define DOIP_UDP_TEST_EQUIPMENT_PORT 13401 +#define DOIP_PROTOCOL_VERSION 0x04 +#define DOIP_INVERSE_PROTOCOL_VERSION 0xFB + +// DoIP Payload Types +#define VEHICLE_IDENTIFICATION_REQUEST 0x0001 +#define VEHICLE_IDENTIFICATION_RESPONSE 0x0004 + +// Discovered vehicle information +typedef struct { + char vin[18]; + uint16_t logical_address; + uint8_t eid[6]; + uint8_t gid[6]; + char ip_address[INET_ADDRSTRLEN]; + uint16_t port; +} VehicleInfo; + +// Create DoIP header +void create_doip_header(uint8_t *buffer, uint16_t payload_type, uint32_t payload_length) { + buffer[0] = DOIP_PROTOCOL_VERSION; + buffer[1] = DOIP_INVERSE_PROTOCOL_VERSION; + buffer[2] = (payload_type >> 8) & 0xFF; + buffer[3] = payload_type & 0xFF; + buffer[4] = (payload_length >> 24) & 0xFF; + buffer[5] = (payload_length >> 16) & 0xFF; + buffer[6] = (payload_length >> 8) & 0xFF; + buffer[7] = payload_length & 0xFF; +} + +// Create Vehicle Identification Request +int create_vehicle_identification_request(uint8_t *buffer) { + create_doip_header(buffer, VEHICLE_IDENTIFICATION_REQUEST, 0); + return 8; // Header only, no payload +} + +// Parse DoIP header +bool parse_doip_header(const uint8_t *buffer, size_t length, uint16_t *payload_type, uint32_t *payload_length) { + if (length < 8) { + return false; + } + + if (buffer[0] != DOIP_PROTOCOL_VERSION || buffer[1] != DOIP_INVERSE_PROTOCOL_VERSION) { + printf("[CLIENT] Invalid DoIP protocol version\n"); + return false; + } + + *payload_type = (buffer[2] << 8) | buffer[3]; + *payload_length = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + + return true; +} + +// Parse Vehicle Identification Response +bool parse_vehicle_identification_response(const uint8_t *buffer, size_t length, VehicleInfo *info) { + if (length < 41) { // 8 (header) + 33 (minimum payload) + printf("[CLIENT] Message too short for Vehicle Identification Response\n"); + return false; + } + + int offset = 8; // Skip header + + // VIN (17 bytes) + memcpy(info->vin, buffer + offset, 17); + info->vin[17] = '\0'; + offset += 17; + + // Logical Address (2 bytes) + info->logical_address = (buffer[offset] << 8) | buffer[offset + 1]; + offset += 2; + + // EID (6 bytes) + memcpy(info->eid, buffer + offset, 6); + offset += 6; + + // GID (6 bytes) + memcpy(info->gid, buffer + offset, 6); + offset += 6; + + return true; +} + +// Print vehicle information +void print_vehicle_info(const VehicleInfo *info) { + printf("\n[CLIENT] ========== Vehicle Information ==========\n"); + printf("[CLIENT] VIN: %s\n", info->vin); + printf("[CLIENT] Logical Address: 0x%04X\n", info->logical_address); + printf("[CLIENT] EID: %02X:%02X:%02X:%02X:%02X:%02X\n", + info->eid[0], info->eid[1], info->eid[2], + info->eid[3], info->eid[4], info->eid[5]); + printf("[CLIENT] GID: %02X:%02X:%02X:%02X:%02X:%02X\n", + info->gid[0], info->gid[1], info->gid[2], + info->gid[3], info->gid[4], info->gid[5]); + printf("[CLIENT] Server IP: %s\n", info->ip_address); + printf("[CLIENT] Server Port: %d\n", info->port); + printf("[CLIENT] ============================================\n\n"); +} + +int main(int argc, char *argv[]) { + bool use_loopback = false; + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--loopback") == 0) { + use_loopback = true; + } + } + + printf("[CLIENT] Starting DoIP Client\n"); + printf("[CLIENT] Mode: %s\n", use_loopback ? "Loopback" : "Broadcast"); + + // Create socket for sending requests + int request_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (request_sock < 0) { + perror("[CLIENT] Failed to create request socket"); + return 1; + } + + // Create socket for receiving announcements + int announcement_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (announcement_sock < 0) { + perror("[CLIENT] Failed to create announcement socket"); + close(request_sock); + return 1; + } + + // Enable SO_REUSEADDR for announcement socket + int reuse = 1; + setsockopt(announcement_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + // Enable broadcast reception + int broadcast = 1; + if (setsockopt(announcement_sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0) { + perror("[CLIENT] Failed to enable broadcast reception"); + } + + // Bind announcement socket to port 13401 + struct sockaddr_in announcement_addr; + memset(&announcement_addr, 0, sizeof(announcement_addr)); + announcement_addr.sin_family = AF_INET; + announcement_addr.sin_addr.s_addr = htonl(INADDR_ANY); + announcement_addr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_PORT); + + if (bind(announcement_sock, (struct sockaddr *)&announcement_addr, sizeof(announcement_addr)) < 0) { + perror("[CLIENT] Failed to bind announcement socket"); + close(request_sock); + close(announcement_sock); + return 1; + } + + printf("[CLIENT] Announcement socket bound to 0.0.0.0:%d\n", DOIP_UDP_TEST_EQUIPMENT_PORT); + + // Set timeout for announcement socket + struct timeval timeout; + timeout.tv_sec = 5; + timeout.tv_usec = 0; + setsockopt(announcement_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + // Listen for Vehicle Announcement + printf("[CLIENT] Listening for Vehicle Announcements...\n"); + + uint8_t buffer[512]; + struct sockaddr_in server_addr; + socklen_t server_len = sizeof(server_addr); + + ssize_t received = recvfrom(announcement_sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&server_addr, &server_len); + + if (received < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + printf("[CLIENT] Timeout: No Vehicle Announcement received\n"); + } else { + perror("[CLIENT] Error receiving announcement"); + } + close(request_sock); + close(announcement_sock); + return 1; + } + + char server_ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &server_addr.sin_addr, server_ip, sizeof(server_ip)); + printf("[CLIENT] Received announcement: %zd bytes from %s:%d\n", + received, server_ip, ntohs(server_addr.sin_port)); + + // Parse the announcement + uint16_t payload_type; + uint32_t payload_length; + + if (!parse_doip_header(buffer, received, &payload_type, &payload_length)) { + printf("[CLIENT] Failed to parse DoIP header\n"); + close(request_sock); + close(announcement_sock); + return 1; + } + + if (payload_type != VEHICLE_IDENTIFICATION_RESPONSE) { + printf("[CLIENT] Unexpected payload type: 0x%04X\n", payload_type); + close(request_sock); + close(announcement_sock); + return 1; + } + + VehicleInfo vehicle_info; + memset(&vehicle_info, 0, sizeof(vehicle_info)); + + if (!parse_vehicle_identification_response(buffer, received, &vehicle_info)) { + printf("[CLIENT] Failed to parse Vehicle Identification Response\n"); + close(request_sock); + close(announcement_sock); + return 1; + } + + // Store server information + strncpy(vehicle_info.ip_address, server_ip, sizeof(vehicle_info.ip_address)); + vehicle_info.port = DOIP_UDP_DISCOVERY_PORT; // Requests go to port 13400 + + print_vehicle_info(&vehicle_info); + + // Now send a Vehicle Identification Request to the discovered server + printf("[CLIENT] Sending Vehicle Identification Request to %s:%d\n", + vehicle_info.ip_address, vehicle_info.port); + + struct sockaddr_in request_addr; + memset(&request_addr, 0, sizeof(request_addr)); + request_addr.sin_family = AF_INET; + request_addr.sin_port = htons(vehicle_info.port); + inet_pton(AF_INET, vehicle_info.ip_address, &request_addr.sin_addr); + + uint8_t request[8]; + int request_len = create_vehicle_identification_request(request); + + ssize_t sent = sendto(request_sock, request, request_len, 0, + (struct sockaddr *)&request_addr, sizeof(request_addr)); + + if (sent < 0) { + perror("[CLIENT] Failed to send request"); + close(request_sock); + close(announcement_sock); + return 1; + } + + printf("[CLIENT] Sent Vehicle Identification Request: %zd bytes\n", sent); + + // Wait for response + timeout.tv_sec = 3; + timeout.tv_usec = 0; + setsockopt(request_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + struct sockaddr_in response_addr; + socklen_t response_len = sizeof(response_addr); + + received = recvfrom(request_sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&response_addr, &response_len); + + if (received < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + printf("[CLIENT] Timeout: No response received\n"); + } else { + perror("[CLIENT] Error receiving response"); + } + close(request_sock); + close(announcement_sock); + return 1; + } + + char response_ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &response_addr.sin_addr, response_ip, sizeof(response_ip)); + printf("[CLIENT] Received response: %zd bytes from %s:%d\n", + received, response_ip, ntohs(response_addr.sin_port)); + + // Parse the response + if (!parse_doip_header(buffer, received, &payload_type, &payload_length)) { + printf("[CLIENT] Failed to parse response header\n"); + close(request_sock); + close(announcement_sock); + return 1; + } + + if (payload_type == VEHICLE_IDENTIFICATION_RESPONSE) { + VehicleInfo response_info; + memset(&response_info, 0, sizeof(response_info)); + + if (parse_vehicle_identification_response(buffer, received, &response_info)) { + strncpy(response_info.ip_address, response_ip, sizeof(response_info.ip_address)); + response_info.port = ntohs(response_addr.sin_port); + + printf("[CLIENT] Vehicle Identification Response received:\n"); + print_vehicle_info(&response_info); + } + } + + // Cleanup + close(request_sock); + close(announcement_sock); + + printf("[CLIENT] Discovery complete\n"); + return 0; +} diff --git a/doc/udp/doip_server.c b/doc/udp/doip_server.c new file mode 100644 index 0000000..157c506 --- /dev/null +++ b/doc/udp/doip_server.c @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOIP_UDP_DISCOVERY_PORT 13400 +#define DOIP_UDP_TEST_EQUIPMENT_PORT 13401 +#define DOIP_PROTOCOL_VERSION 0x04 +#define DOIP_INVERSE_PROTOCOL_VERSION 0xFB + +// DoIP Payload Types +#define VEHICLE_IDENTIFICATION_REQUEST 0x0001 +#define VEHICLE_IDENTIFICATION_RESPONSE 0x0004 + +// Server configuration +typedef struct { + char vin[17]; + uint16_t logical_address; + uint8_t eid[6]; + uint8_t gid[6]; + bool use_loopback; +} ServerConfig; + +// Global server state +static bool server_running = true; +static int udp_sock = -1; + +// Create DoIP header +void create_doip_header(uint8_t *buffer, uint16_t payload_type, uint32_t payload_length) { + buffer[0] = DOIP_PROTOCOL_VERSION; + buffer[1] = DOIP_INVERSE_PROTOCOL_VERSION; + buffer[2] = (payload_type >> 8) & 0xFF; + buffer[3] = payload_type & 0xFF; + buffer[4] = (payload_length >> 24) & 0xFF; + buffer[5] = (payload_length >> 16) & 0xFF; + buffer[6] = (payload_length >> 8) & 0xFF; + buffer[7] = payload_length & 0xFF; +} + +// Create Vehicle Identification Response +int create_vehicle_identification_response(uint8_t *buffer, const ServerConfig *config) { + uint32_t payload_length = 33; // VIN(17) + LogAddr(2) + EID(6) + GID(6) + FAR(1) + VIN/GID_sync(1) + + create_doip_header(buffer, VEHICLE_IDENTIFICATION_RESPONSE, payload_length); + + int offset = 8; + + // VIN (17 bytes) + memcpy(buffer + offset, config->vin, 17); + offset += 17; + + // Logical Address (2 bytes) + buffer[offset++] = (config->logical_address >> 8) & 0xFF; + buffer[offset++] = config->logical_address & 0xFF; + + // EID (6 bytes) + memcpy(buffer + offset, config->eid, 6); + offset += 6; + + // GID (6 bytes) + memcpy(buffer + offset, config->gid, 6); + offset += 6; + + // Further Action Required (1 byte) + buffer[offset++] = 0x00; + + // VIN/GID sync status (1 byte) - optional, set to 0x00 + buffer[offset++] = 0x00; + + return offset; // Total message length +} + +// Parse DoIP header +bool parse_doip_header(const uint8_t *buffer, size_t length, uint16_t *payload_type, uint32_t *payload_length) { + if (length < 8) { + return false; + } + + if (buffer[0] != DOIP_PROTOCOL_VERSION || buffer[1] != DOIP_INVERSE_PROTOCOL_VERSION) { + printf("Invalid DoIP protocol version\n"); + return false; + } + + *payload_type = (buffer[2] << 8) | buffer[3]; + *payload_length = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; + + return true; +} + +// Send Vehicle Announcement +void send_vehicle_announcement(const ServerConfig *config) { + uint8_t buffer[256]; + int msg_len = create_vehicle_identification_response(buffer, config); + + struct sockaddr_in dest_addr; + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_PORT); + + const char *dest_ip; + if (config->use_loopback) { + dest_ip = "127.0.0.1"; + inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr); + } else { + dest_ip = "255.255.255.255"; + dest_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + + // Enable broadcast + int broadcast = 1; + setsockopt(udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)); + } + + ssize_t sent = sendto(udp_sock, buffer, msg_len, 0, + (struct sockaddr *)&dest_addr, sizeof(dest_addr)); + + if (sent > 0) { + printf("[SERVER] Sent Vehicle Announcement: %zd bytes to %s:%d\n", + sent, dest_ip, DOIP_UDP_TEST_EQUIPMENT_PORT); + } else { + perror("[SERVER] Failed to send announcement"); + } +} + +// UDP Listener Thread +void *udp_listener_thread(void *arg) { + ServerConfig *config = (ServerConfig *)arg; + uint8_t buffer[512]; + struct sockaddr_in client_addr; + socklen_t client_len = sizeof(client_addr); + + printf("[SERVER] UDP listener thread started\n"); + + while (server_running) { + ssize_t received = recvfrom(udp_sock, buffer, sizeof(buffer), 0, + (struct sockaddr *)&client_addr, &client_len); + + if (received < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // Timeout, continue + continue; + } + if (server_running) { + perror("[SERVER] recvfrom error"); + } + break; + } + + if (received > 0) { + char client_ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); + printf("[SERVER] Received %zd bytes from %s:%d\n", + received, client_ip, ntohs(client_addr.sin_port)); + + uint16_t payload_type; + uint32_t payload_length; + + if (parse_doip_header(buffer, received, &payload_type, &payload_length)) { + printf("[SERVER] Payload Type: 0x%04X\n", payload_type); + + if (payload_type == VEHICLE_IDENTIFICATION_REQUEST) { + printf("[SERVER] Vehicle Identification Request received\n"); + + // Send response back to client + uint8_t response[256]; + int resp_len = create_vehicle_identification_response(response, config); + + ssize_t sent = sendto(udp_sock, response, resp_len, 0, + (struct sockaddr *)&client_addr, client_len); + + if (sent > 0) { + printf("[SERVER] Sent Vehicle Identification Response: %zd bytes to %s:%d\n", + sent, client_ip, ntohs(client_addr.sin_port)); + } else { + perror("[SERVER] Failed to send response"); + } + } + } + } + } + + printf("[SERVER] UDP listener thread stopped\n"); + return NULL; +} + +// Announcement Thread +void *announcement_thread(void *arg) { + ServerConfig *config = (ServerConfig *)arg; + + printf("[SERVER] Announcement thread started\n"); + + // Send 5 announcements with 2 second interval + for (int i = 0; i < 5 && server_running; i++) { + send_vehicle_announcement(config); + sleep(2); + } + + printf("[SERVER] Announcement thread stopped\n"); + return NULL; +} + +int main(int argc, char *argv[]) { + bool use_loopback = false; + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--loopback") == 0) { + use_loopback = true; + } + } + + // Configure server + ServerConfig config = { + .vin = "EXAMPLESERVER0000", + .logical_address = 0x0028, + .eid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .gid = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + .use_loopback = use_loopback + }; + + printf("[SERVER] Starting DoIP Server\n"); + printf("[SERVER] Mode: %s\n", use_loopback ? "Loopback" : "Broadcast"); + printf("[SERVER] VIN: %s\n", config.vin); + printf("[SERVER] Logical Address: 0x%04X\n", config.logical_address); + + // Create UDP socket + udp_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (udp_sock < 0) { + perror("Failed to create socket"); + return 1; + } + + // Set socket to non-blocking with timeout + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + setsockopt(udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + + // Enable SO_REUSEADDR + int reuse = 1; + setsockopt(udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + // Bind socket to port 13400 + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + server_addr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); + + if (bind(udp_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + perror("Failed to bind socket"); + close(udp_sock); + return 1; + } + + printf("[SERVER] Socket bound to 0.0.0.0:%d\n", DOIP_UDP_DISCOVERY_PORT); + + // Start threads + pthread_t listener_tid, announcement_tid; + + pthread_create(&listener_tid, NULL, udp_listener_thread, &config); + pthread_create(&announcement_tid, NULL, announcement_thread, &config); + + // Wait for announcement thread to complete + pthread_join(announcement_tid, NULL); + + // Keep server running for a bit to handle requests + printf("[SERVER] Announcements complete, waiting for requests...\n"); + sleep(10); + + // Shutdown + printf("[SERVER] Shutting down...\n"); + server_running = false; + + pthread_join(listener_tid, NULL); + + close(udp_sock); + + printf("[SERVER] Server stopped\n"); + return 0; +} From bc764993cc31d423af6e51b17f145fc155e281ee Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 8 Dec 2025 22:06:03 +0100 Subject: [PATCH 05/20] WIP: Migration to new code --- inc/DoIPConnection.h | 2 +- inc/DoIPServer.h | 20 +-- src/DoIPConnection.cpp | 4 +- src/DoIPServer.cpp | 332 ++++++++++++++++++----------------------- 4 files changed, 153 insertions(+), 205 deletions(-) diff --git a/inc/DoIPConnection.h b/inc/DoIPConnection.h index 73ce0b0..716dd9d 100644 --- a/inc/DoIPConnection.h +++ b/inc/DoIPConnection.h @@ -100,7 +100,7 @@ class DoIPConnection : public DoIPDefaultConnection { bool hasDownstreamHandler() const override; private: - DoIPAddress m_gatewayAddress; + DoIPAddress m_logicalAddress; // TCP socket-specific members int m_tcpSocket; diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 9a9b51d..51cc31c 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -40,7 +40,7 @@ struct ServerConfig { DoIPVIN vin = DoIPVIN::Zero; // Logical/server address (default 0x0E00) - uint16_t logicalAddress = 0x0E00; + uint16_t logicalAddress = 0x0028; // Use loopback announcements instead of broadcast bool loopback = false; @@ -131,22 +131,20 @@ class DoIPServer { private: int m_tcp_sock{-1}; - int m_udp_receive_sock{-1}; - int m_udp_send_sock{-1}; + int m_udp_sock{-1}; struct sockaddr_in m_serverAddress {}; struct sockaddr_in m_clientAddress {}; ByteArray m_receiveBuf{}; DoIPVIN m_VIN; - DoIPAddress m_gatewayAddress = DoIPAddress::ZeroAddress; + DoIPAddress m_logicalAddress = DoIPAddress(0x0028); DoIPEID m_EID = DoIPEID::Zero; DoIPGID m_GID = DoIPGID::Zero; DoIPFurtherAction m_FurtherActionReq = DoIPFurtherAction::NoFurtherAction; - //TimerManager m_timerManager{}; int m_announceNum = 3; // Default Value = 3 unsigned int m_announceInterval = 500; // Default Value = 500ms - bool m_useLoopbackAnnouncements = false; // Default: use broadcast + bool m_loopbackMode = false; // Default: use broadcast int m_broadcast = 1; @@ -161,20 +159,18 @@ class DoIPServer { void stop(); void daemonize(); - ssize_t reactToReceivedUdpMessage(size_t bytesRead); - ssize_t sendUdpMessage(const uint8_t *message, size_t messageLength); - void setMulticastGroup(const char *address); ssize_t sendNegativeUdpAck(DoIPNegativeAck ackCode); - // Worker thread functions - void udpListenerThread(); - template void tcpListenerThread(); void connectionHandlerThread(std::unique_ptr connection); + + void udpListenerThread(); + void udpAnnouncementThread(); + ssize_t sendVehicleAnnouncement2(); }; // Template implementation must be in header for external linkage diff --git a/src/DoIPConnection.cpp b/src/DoIPConnection.cpp index f454ff2..be4b3fe 100644 --- a/src/DoIPConnection.cpp +++ b/src/DoIPConnection.cpp @@ -148,11 +148,11 @@ DoIPAddress DoIPConnection::getServerAddress() const { } DoIPAddress DoIPConnection::getClientAddress() const { - return m_gatewayAddress; + return m_logicalAddress; } void DoIPConnection::setClientAddress(const DoIPAddress &address) { - m_gatewayAddress = address; + m_logicalAddress = address; } DoIPDiagnosticAck DoIPConnection::notifyDiagnosticMessage(const DoIPMessage &msg) { diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index 18a5bf4..6f1d800 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -39,7 +39,7 @@ DoIPServer::DoIPServer(const ServerConfig &config) m_GID = m_config.gid; // Set logical gateway address from config - m_gatewayAddress.update(m_config.logicalAddress); + m_logicalAddress.update(m_config.logicalAddress); // Apply EID/GID if provided (interpreted as numeric where possible) // (old parsing logic removed - ServerConfig uses typed identifiers) @@ -187,52 +187,39 @@ void DoIPServer::closeTcpSocket() { close(m_tcp_sock); } - bool DoIPServer::setupUdpSocket() { LOG_UDP_DEBUG("Setting up UDP socket on port {}", DOIP_UDP_DISCOVERY_PORT); - m_udp_receive_sock = socket(AF_INET, SOCK_DGRAM, 0); - - m_serverAddress.sin_family = AF_INET; - m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); - - if (m_udp_receive_sock < 0) { - LOG_UDP_ERROR("Failed to create UDP socket: {}", strerror(errno)); - return false; + m_udp_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (m_udp_sock < 0) { + perror("Failed to create socket"); + return 1; } - int flags = fcntl(m_udp_receive_sock, F_GETFL, 0); - fcntl(m_udp_receive_sock, F_SETFL, flags | O_NONBLOCK); + // Set socket to non-blocking with timeout + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + setsockopt(m_udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + // Enable SO_REUSEADDR int reuse = 1; - setsockopt(m_udp_receive_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); - - // binds the socket to any IP DoIPAddress and the Port Number 13400 - if (bind(m_udp_receive_sock, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)) < 0) { - LOG_UDP_ERROR("Failed to bind UDP socket: {}", strerror(errno)); - return false; - } + setsockopt(m_udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); - // Socket to send announcements - m_udp_send_sock = socket(AF_INET, SOCK_DGRAM, 0); - if (m_udp_send_sock < 0) { - LOG_UDP_ERROR("Failed to create UDP send socket: {}", strerror(errno)); - close(m_udp_receive_sock); - return false; - } - - setsockopt(m_udp_send_sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + // Bind socket to port 13400 + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); + server_addr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); - if (bind(m_udp_send_sock, reinterpret_cast(&m_serverAddress), - sizeof(m_serverAddress)) < 0) { - LOG_UDP_ERROR("Failed to bind UDP send socket: {}", strerror(errno)); - close(m_udp_receive_sock); - return false; + if (bind(m_udp_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + perror("Failed to bind socket"); + close(m_udp_sock); + return 1; } - // setting the IP DoIPAddress for Multicast/Broadcast - if (!m_useLoopbackAnnouncements) { // + if (!m_loopbackMode) { // setMulticastGroup("224.0.0.2"); LOG_UDP_INFO("UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT); } else { @@ -241,123 +228,16 @@ bool DoIPServer::setupUdpSocket() { LOG_UDP_DEBUG( "Socket {} bound to {}:{}", - m_udp_receive_sock, + m_udp_sock, inet_ntoa(m_serverAddress.sin_addr), ntohs(m_serverAddress.sin_port)); return true; } -/* - * Background thread: UDP message listener - */ -void DoIPServer::udpListenerThread() { - LOG_DOIP_INFO("UDP listener thread started"); - - while (m_running.load()) { - ssize_t result = receiveUdpMessage(); - - // If timeout (result == 0), continue without delay - // The socket already has a timeout configured - if (result < 0 && m_running.load()) { - // Only log errors if we're still supposed to be running - LOG_UDP_DEBUG("UDP receive error, continuing..."); - } - } - - LOG_DOIP_INFO("UDP listener thread stopped"); -} - void DoIPServer::closeUdpSocket() { - close(m_udp_receive_sock); - close(m_udp_send_sock); -} - -/* - * Receives a udp message and calls reactToReceivedUdpMessage method - * @return amount of bytes which were send back to client - * or -1 if error occurred - */ -ssize_t DoIPServer::receiveUdpMessage() { - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 100000; // 100ms - setsockopt(m_udp_receive_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); - - struct sockaddr_in clientAddr; - socklen_t length = sizeof(clientAddr); - - const ssize_t readBytes = recvfrom( - m_udp_receive_sock, - m_receiveBuf.data(), - m_receiveBuf.size(), - 0, - reinterpret_cast(&clientAddr), - &length); - - const int recvErrno = errno; // Save errno immediately - // LOG_UDP_DEBUG("recvfrom returned: {} (errno: {})", readBytes, recvErrno); - - if (readBytes <= 0) { - if (recvErrno == EAGAIN) { // EAGAIN sufficient: error: logical ‘or’ of equal expressions [-Werror=logical-op] 271 | if (recvErrno == EAGAIN || recvErrno == EWOULDBLOCK) { - return 0; // Timeout (no sleep here) - } else { - LOG_UDP_ERROR("Error receiving UDP message: {}", strerror(recvErrno)); - return -1; - } - } - - LOG_UDP_INFO("RX {} bytes from {}:{}", - readBytes, - inet_ntoa(clientAddr.sin_addr), - ntohs(clientAddr.sin_port)); - - m_clientAddress = clientAddr; - return reactToReceivedUdpMessage(static_cast(readBytes)); -} - -/* - * Receives a udp message and determine how to process the message - * @return amount of bytes which were send back to client - * or -1 if error occurred - */ -ssize_t DoIPServer::reactToReceivedUdpMessage(size_t bytesRead) { - (void)bytesRead; - ssize_t sentBytes = -1; - // GenericHeaderAction action = parseGenericHeader(data, bytesRead); - - auto optHeader = DoIPMessage::tryParseHeader(m_receiveBuf.data(), DOIP_HEADER_SIZE); - if (!optHeader.has_value()) { - return sendNegativeUdpAck(DoIPNegativeAck::IncorrectPatternFormat); - } - - auto plType = optHeader->first; - // auto payloadLength = optHeader->second; - LOG_UDP_INFO("RX: {}", fmt::streamed(plType)); - switch (plType) { - case DoIPPayloadType::VehicleIdentificationRequest: { - DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_gatewayAddress, m_EID, m_GID); - LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); - ssize_t sendedBytes = sendUdpMessage(msg.data(), DOIP_HEADER_SIZE + msg.size()); - - return static_cast(sendedBytes); - } - - default: { - LOG_DOIP_ERROR("Invalid payload type 0x{:04X} received (receiveUdpMessage())", static_cast(plType)); - return sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); - } - } - return sentBytes; -} - -ssize_t DoIPServer::sendUdpMessage(const uint8_t *message, size_t messageLength) { // sendUdpMessage after receiving a message from the client - // if the server receives a message from a client, than the response should be send back to the client address and port - // m_clientAddress.sin_port = m_serverAddress.sin_port; - // m_clientAddress.sin_addr.s_addr = m_serverAddress.sin_addr.s_addr; - - int result = sendto(m_udp_send_sock, message, messageLength, 0, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); - return result; + m_running.store(false); + close(m_udp_sock); } bool DoIPServer::setEIDdefault() { @@ -382,7 +262,7 @@ void DoIPServer::setVIN(const DoIPVIN &vin) { } void DoIPServer::setLogicalGatewayAddress(const unsigned short inputLogAdd) { - m_gatewayAddress.update(inputLogAdd); + m_logicalAddress.update(inputLogAdd); } void DoIPServer::setEID(const uint64_t inputEID) { @@ -406,7 +286,7 @@ void DoIPServer::setAnnounceInterval(unsigned int Interval) { } void DoIPServer::setAnnouncementMode(bool useLoopback) { - m_useLoopbackAnnouncements = useLoopback; + m_loopbackMode = useLoopback; if (useLoopback) { LOG_DOIP_INFO("Vehicle announcements will use loopback (127.0.0.1)"); } else { @@ -418,7 +298,7 @@ void DoIPServer::setMulticastGroup(const char *address) { int loop = 1; // set Option using the same Port for multiple Sockets - int setPort = setsockopt(m_udp_receive_sock, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)); + int setPort = setsockopt(m_udp_sock, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)); if (setPort < 0) { LOG_UDP_ERROR("Setting Port Error"); @@ -430,63 +310,135 @@ void DoIPServer::setMulticastGroup(const char *address) { mreq.imr_interface.s_addr = htonl(INADDR_ANY); // set Option to join Multicast Group - int setGroup = setsockopt(m_udp_receive_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); + int setGroup = setsockopt(m_udp_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); if (setGroup < 0) { LOG_UDP_ERROR("Setting address failed: {}", strerror(errno)); } } -ssize_t DoIPServer::sendVehicleAnnouncement() { +ssize_t DoIPServer::sendNegativeUdpAck(DoIPNegativeAck ackCode) { + //DoIPMessage message = message::makeNegativeAckMessage(ackCode); - // Choose address based on mode - const char *address = m_useLoopbackAnnouncements ? "127.0.0.1" : "255.255.255.255"; - // Build destination address for the announcement - struct sockaddr_in destAddr{}; - destAddr.sin_family = AF_INET; - destAddr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); - int setAddressError = inet_aton(address, &destAddr.sin_addr); + // return sendUdpMessage(message.data(), message.size()); + LOG_UDP_CRITICAL("sendNegativeUdpAck NOT IMPL"); + return -1; +} - if (setAddressError != 0) { - LOG_DOIP_INFO("{} address set successfully: {}", - m_useLoopbackAnnouncements ? "Loopback" : "Broadcast", address); - } +// new version starts here +void DoIPServer::udpListenerThread() { - if (!m_useLoopbackAnnouncements) { - // Only set broadcast option for broadcast mode on the send socket - int socketError = setsockopt(m_udp_send_sock, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); - if (socketError == 0) { - LOG_DOIP_INFO("Broadcast Option set successfully"); - } - } + struct sockaddr_in client_addr; + socklen_t client_len = sizeof(client_addr); - ssize_t sentBytes = -1; + LOG_UDP_INFO("UDP listener thread started"); - // uint8_t *message = createVehicleIdentificationResponse(VIN, m_gatewayAddress, m_EID, GID, FurtherActionReq); - DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_gatewayAddress, m_EID, m_GID, m_FurtherActionReq); + while (m_running) { + ssize_t received = recvfrom(m_udp_sock, m_receiveBuf.data(), sizeof(m_receiveBuf), 0, + (struct sockaddr *)&client_addr, &client_len); - for (int i = 0; i < m_announceNum; i++) { - sentBytes = sendto(m_udp_send_sock, msg.data(), msg.size(), 0, reinterpret_cast(&destAddr), sizeof(destAddr)); + if (received < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // Timeout, continue + continue; + } + if (m_running) { + perror("[SERVER] recvfrom error"); + } + break; + } - if (sentBytes < 0) { - LOG_UDP_ERROR("Failed sending Vehicle Announcement: {}", strerror(errno)); - LOG_UDP_ERROR("Message: {}", fmt::streamed(msg)); - return -1; - } else { - if (sentBytes == 0) { - LOG_UDP_WARN("Vehicle Announcement sent incomplete: {}/{} bytes", sentBytes, msg.size()); - } else { - LOG_UDP_INFO("Vehicle Announcement sent successfully: {}/{} bytes", sentBytes, msg.size()); + if (received > 0) { + char client_ip[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); + printf("[SERVER] Received %zd bytes from %s:%d\n", + received, client_ip, ntohs(client_addr.sin_port)); + + auto optHeader = DoIPMessage::tryParseHeader(m_receiveBuf.data(), DOIP_HEADER_SIZE); + if (!optHeader.has_value()) { + auto sentBytes = sendNegativeUdpAck(DoIPNegativeAck::IncorrectPatternFormat); + if (sentBytes < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // Timeout, continue + continue; + } + if (m_running) { + perror("[SERVER] recvfrom error"); + } + break; + } + } + auto plType = optHeader->first; + // auto payloadLength = optHeader->second; + LOG_UDP_INFO("RX: {}", fmt::streamed(plType)); + switch (plType) { + case DoIPPayloadType::VehicleIdentificationRequest: { + DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_logicalAddress, m_EID, m_GID); + LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); + + auto sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, + (struct sockaddr *)&client_addr, client_len); + + if (sentBytes > 0) { + LOG_DOIP_ERROR("[SERVER] Sent Vehicle Identification Response: %zd bytes to %s:%d\n", + sentBytes, client_ip, ntohs(client_addr.sin_port)); + } else { + perror("[SERVER] Failed to send response"); + } + } + + default: { + LOG_DOIP_ERROR("Invalid payload type 0x{:04X} received (receiveUdpMessage())", static_cast(plType)); + auto sentBytes = sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); + } } } - LOG_UDP_INFO("Sent Vehicle Announcement {}/{}: {} bytes to {}:{}", i + 1, m_announceNum, sentBytes, - inet_ntoa(destAddr.sin_addr), ntohs(destAddr.sin_port)); - usleep(m_announceInterval * 1000); } - return sentBytes; + + printf("[SERVER] UDP listener thread stopped\n"); } -ssize_t DoIPServer::sendNegativeUdpAck(DoIPNegativeAck ackCode) { - DoIPMessage message = message::makeNegativeAckMessage(ackCode); - return sendUdpMessage(message.data(), message.size()); +void DoIPServer::udpAnnouncementThread() { + LOG_DOIP_INFO("Announcement thread started"); + + // Send 5 announcements with 2 second interval + for (int i = 0; i < m_announceNum && m_running; i++) { + sendVehicleAnnouncement2(); + sleep(m_announceInterval); + } + + LOG_DOIP_INFO("Announcement thread stopped"); } + +ssize_t DoIPServer::sendVehicleAnnouncement2() { + DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_logicalAddress, m_EID, m_GID); + + struct sockaddr_in dest_addr; + memset(&dest_addr, 0, sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); + + const char *dest_ip; + if (m_loopbackMode) { + dest_ip = "127.0.0.1"; + inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr); + } else { + dest_ip = "255.255.255.255"; + dest_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + + // Enable broadcast + int broadcast = 1; + setsockopt(m_udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)); + } + + ssize_t sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, + (struct sockaddr *)&dest_addr, sizeof(dest_addr)); + + LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); + if (sentBytes > 0) { + printf("[SERVER] Sent Vehicle Announcement: %zd bytes to %s:%d\n", + sentBytes, dest_ip, DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); + } else { + perror("[SERVER] Failed to send announcement"); + } +} \ No newline at end of file From 545b358355be36844028ac020b99d70d02e2e747 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Mon, 8 Dec 2025 22:52:49 +0100 Subject: [PATCH 06/20] fix: UDP connection/discover works now --- examples/CMakeLists.txt | 4 +-- examples/exampleDoIPServer.cpp | 27 ++++------------- inc/DoIPServer.h | 7 ++--- src/DoIPServer.cpp | 53 +++++++++++++++++++++------------- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5bc5269..8b0ce4e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -37,13 +37,13 @@ if (WITH_UNIT_TEST) add_test(NAME Integration_StartExampleServer COMMAND $ --daemonize --loopback ) - set_tests_properties(Integration_StartExampleServer PROPERTIES FIXTURES_SETUP example_server TIMEOUT 10) + 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 $ --loopback ) - set_tests_properties(Integration_DiscoverRuns PROPERTIES FIXTURES_REQUIRED example_server TIMEOUT 10) + set_tests_properties(Integration_DiscoverRuns PROPERTIES FIXTURES_REQUIRED example_server TIMEOUT 20) # Teardown fixture: stop exampleDoIPServer add_test(NAME Integration_StopExampleServer diff --git a/examples/exampleDoIPServer.cpp b/examples/exampleDoIPServer.cpp index ec851a8..3b05bcc 100644 --- a/examples/exampleDoIPServer.cpp +++ b/examples/exampleDoIPServer.cpp @@ -21,23 +21,8 @@ std::vector doipReceiver; bool serverActive = false; std::unique_ptr tcpConnection(nullptr); -void listenUdp(); void listenTcp(); -/* - * Check permantly if udp message was received - */ -void listenUdp() { - LOG_UDP_INFO("UDP listener thread started"); - while (serverActive) { - ssize_t result = server->receiveUdpMessage(); - // If timeout (result == 0), sleep briefly to prevent CPU spinning - if (result == 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } -} - /* * Check permantly if tcp message was received */ @@ -151,15 +136,15 @@ int main(int argc, char *argv[]) { } serverActive = true; - LOG_DOIP_INFO("Starting UDP and TCP listener threads"); - doipReceiver.push_back(thread(&listenUdp)); - doipReceiver.push_back(thread(&listenTcp)); - server->sendVehicleAnnouncement(); - LOG_DOIP_INFO("Vehicle announcement sent"); + // TODO:: Add signal handler + while(server->isRunning()) { + sleep(1); + } + doipReceiver.push_back(thread(&listenTcp)); + LOG_DOIP_INFO("Starting TCP listener threads"); doipReceiver.at(0).join(); - doipReceiver.at(1).join(); LOG_DOIP_INFO("DoIP Server Example terminated"); return 0; } diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 51cc31c..6c778d9 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -90,8 +90,6 @@ class DoIPServer { [[nodiscard]] bool setupUdpSocket(); - [[nodiscard]] - ssize_t receiveUdpMessage(); /** * @brief Check if the server is currently running @@ -107,8 +105,6 @@ class DoIPServer { void setLogicalGatewayAddress(const unsigned short inputLogAdd); - ssize_t sendVehicleAnnouncement(); - /** * @brief Sets the EID to a default value based on the MAC address. * @@ -135,6 +131,8 @@ class DoIPServer { struct sockaddr_in m_serverAddress {}; struct sockaddr_in m_clientAddress {}; ByteArray m_receiveBuf{}; + std::string m_clientIp{}; + int m_clientPort{}; DoIPVIN m_VIN; DoIPAddress m_logicalAddress = DoIPAddress(0x0028); @@ -151,6 +149,7 @@ class DoIPServer { // Automatic mode state std::atomic m_running{false}; std::vector m_workerThreads; + std::mutex m_mutex; ConnectionAcceptedHandler m_connectionHandler; // Server configuration diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index 6f1d800..e97e54a 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -213,7 +213,7 @@ bool DoIPServer::setupUdpSocket() { server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); - if (bind(m_udp_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) { + if (bind(m_udp_sock, reinterpret_cast(&server_addr), sizeof(server_addr)) < 0) { perror("Failed to bind socket"); close(m_udp_sock); return 1; @@ -232,11 +232,19 @@ bool DoIPServer::setupUdpSocket() { inet_ntoa(m_serverAddress.sin_addr), ntohs(m_serverAddress.sin_port)); + m_running.store(true); + m_workerThreads.emplace_back([this]() { udpListenerThread(); }); + m_workerThreads.emplace_back([this]() { udpAnnouncementThread(); }); + return true; } void DoIPServer::closeUdpSocket() { m_running.store(false); + for (auto &thread : m_workerThreads) { + if (thread.joinable()) + thread.join(); + } close(m_udp_sock); } @@ -310,7 +318,7 @@ void DoIPServer::setMulticastGroup(const char *address) { mreq.imr_interface.s_addr = htonl(INADDR_ANY); // set Option to join Multicast Group - int setGroup = setsockopt(m_udp_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); + int setGroup = setsockopt(m_udp_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast(&mreq), sizeof(mreq)); if (setGroup < 0) { LOG_UDP_ERROR("Setting address failed: {}", strerror(errno)); @@ -318,9 +326,10 @@ void DoIPServer::setMulticastGroup(const char *address) { } ssize_t DoIPServer::sendNegativeUdpAck(DoIPNegativeAck ackCode) { - //DoIPMessage message = message::makeNegativeAckMessage(ackCode); + // DoIPMessage message = message::makeNegativeAckMessage(ackCode); // return sendUdpMessage(message.data(), message.size()); + (void)ackCode; LOG_UDP_CRITICAL("sendNegativeUdpAck NOT IMPL"); return -1; } @@ -335,35 +344,38 @@ void DoIPServer::udpListenerThread() { while (m_running) { ssize_t received = recvfrom(m_udp_sock, m_receiveBuf.data(), sizeof(m_receiveBuf), 0, - (struct sockaddr *)&client_addr, &client_len); + reinterpret_cast(&client_addr), &client_len); if (received < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (errno == EAGAIN /* || errno == EWOULDBLOCK*/) { // Timeout, continue continue; } if (m_running) { - perror("[SERVER] recvfrom error"); + perror("recvfrom error"); } break; } if (received > 0) { + std::scoped_lock lock(m_mutex); char client_ip[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); - printf("[SERVER] Received %zd bytes from %s:%d\n", - received, client_ip, ntohs(client_addr.sin_port)); + m_clientIp = std::string(client_ip); + m_clientPort = ntohs(client_addr.sin_port); + + LOG_UDP_INFO("Received {} bytes from {}:{}", received, m_clientIp, m_clientPort); auto optHeader = DoIPMessage::tryParseHeader(m_receiveBuf.data(), DOIP_HEADER_SIZE); if (!optHeader.has_value()) { auto sentBytes = sendNegativeUdpAck(DoIPNegativeAck::IncorrectPatternFormat); if (sentBytes < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (errno == EAGAIN /*|| errno == EWOULDBLOCK*/) { // Timeout, continue continue; } if (m_running) { - perror("[SERVER] recvfrom error"); + LOG_UDP_ERROR("Failed to receive: {}", strerror(errno)); } break; } @@ -377,25 +389,25 @@ void DoIPServer::udpListenerThread() { LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); auto sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, - (struct sockaddr *)&client_addr, client_len); + reinterpret_cast(&client_addr), client_len); if (sentBytes > 0) { - LOG_DOIP_ERROR("[SERVER] Sent Vehicle Identification Response: %zd bytes to %s:%d\n", + LOG_DOIP_INFO("Sent Vehicle Identification Response to {}:{}", sentBytes, client_ip, ntohs(client_addr.sin_port)); } else { - perror("[SERVER] Failed to send response"); + perror("Failed to send response"); } - } + } break; default: { LOG_DOIP_ERROR("Invalid payload type 0x{:04X} received (receiveUdpMessage())", static_cast(plType)); - auto sentBytes = sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); + /*auto sentBytes =*/sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); } } } } - printf("[SERVER] UDP listener thread stopped\n"); + printf("UDP listener thread stopped\n"); } void DoIPServer::udpAnnouncementThread() { @@ -404,7 +416,7 @@ void DoIPServer::udpAnnouncementThread() { // Send 5 announcements with 2 second interval for (int i = 0; i < m_announceNum && m_running; i++) { sendVehicleAnnouncement2(); - sleep(m_announceInterval); + usleep(m_announceInterval * 1000); } LOG_DOIP_INFO("Announcement thread stopped"); @@ -432,13 +444,14 @@ ssize_t DoIPServer::sendVehicleAnnouncement2() { } ssize_t sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, - (struct sockaddr *)&dest_addr, sizeof(dest_addr)); + reinterpret_cast(&dest_addr), sizeof(dest_addr)); LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); if (sentBytes > 0) { - printf("[SERVER] Sent Vehicle Announcement: %zd bytes to %s:%d\n", + LOG_UDP_INFO("Sent Vehicle Announcement: {} bytes to {}:{}", sentBytes, dest_ip, DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); } else { - perror("[SERVER] Failed to send announcement"); + LOG_UDP_ERROR("Failed to send announcement: {}", strerror(errno)); } + return sentBytes; } \ No newline at end of file From 3217dedd5d1909c7d0041f2c58ab535451b56484 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Tue, 9 Dec 2025 00:52:38 +0100 Subject: [PATCH 07/20] fix: Return explicit return code --- examples/exampleDoIPDiscover.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/exampleDoIPDiscover.cpp b/examples/exampleDoIPDiscover.cpp index 1bc7fd9..76de9ab 100644 --- a/examples/exampleDoIPDiscover.cpp +++ b/examples/exampleDoIPDiscover.cpp @@ -65,4 +65,5 @@ int main(int argc, char *argv[]) { // Now start TCP connection for diagnostic communication LOG_DOIP_INFO("Discovery complete, closing UDP connections"); client.closeUdpConnection(); + return 0; } From 037a4760da3d385bf10ce05b2edbc8b947237c35 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Tue, 9 Dec 2025 00:53:11 +0100 Subject: [PATCH 08/20] style: Reorder includes; replace printf with LOG_... --- src/DoIPServer.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index e97e54a..98b92af 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -1,12 +1,17 @@ +#include // for std::remove_if +#include // for errno +#include // for strerror +#include +#include +#include +#include + #include "DoIPServer.h" #include "DoIPConnection.h" #include "DoIPMessage.h" #include "DoIPServerModel.h" #include "Logger.h" #include "MacAddress.h" -#include // for std::remove_if -#include // for errno -#include // for strerror using namespace doip; @@ -18,10 +23,6 @@ const char *DEFAULT_IFACE = "en0"; #pragma error "Unsupported platform" #endif -#include -#include -#include -#include DoIPServer::~DoIPServer() { if (m_running.load()) { @@ -407,13 +408,13 @@ void DoIPServer::udpListenerThread() { } } - printf("UDP listener thread stopped\n"); + LOG_UDP_INFO("UDP listener thread stopped"); } void DoIPServer::udpAnnouncementThread() { LOG_DOIP_INFO("Announcement thread started"); - // Send 5 announcements with 2 second interval + // Send announcements with configured interval and count for (int i = 0; i < m_announceNum && m_running; i++) { sendVehicleAnnouncement2(); usleep(m_announceInterval * 1000); From f3dc96c877c39984d1e2a279aae844a4015aa065 Mon Sep 17 00:00:00 2001 From: Oliver Wieland Date: Tue, 9 Dec 2025 00:53:43 +0100 Subject: [PATCH 09/20] chore: Remove dup test; add enable_testing --- examples/CMakeLists.txt | 5 +++-- test/CMakeLists.txt | 18 ------------------ 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8b0ce4e..45cf336 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -33,6 +33,7 @@ foreach(example_source ${EXAMPLE_SOURCES}) endforeach() if (WITH_UNIT_TEST) + enable_testing() # Integration fixture: start exampleDoIPServer as a daemon add_test(NAME Integration_StartExampleServer COMMAND $ --daemonize --loopback @@ -41,13 +42,13 @@ if (WITH_UNIT_TEST) # Integration test: run discover against the running server add_test(NAME Integration_DiscoverRuns - COMMAND $ --loopback + COMMAND $ --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 + COMMAND pkill -f exampleDoIPServer ) set_tests_properties(Integration_StopExampleServer PROPERTIES FIXTURES_CLEANUP example_server TIMEOUT 10) endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index af30bc7..d1a7839 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,21 +29,3 @@ doctest_discover_tests(${DOIP_NAME}_tests TIMEOUT 30 ) -# Integration fixture: start exampleDoIPServer as a daemon -add_test(NAME Integration_StartExampleServer - COMMAND $ --daemonize -) -set_tests_properties(Integration_StartExampleServer PROPERTIES FIXTURES_SETUP example_server TIMEOUT 10) - -# Integration test: run discover against the running server -add_test(NAME Integration_DiscoverRuns - COMMAND $ -) -set_tests_properties(Integration_DiscoverRuns PROPERTIES FIXTURES_REQUIRED example_server TIMEOUT 10) - -# 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) - From 1bf7c5e6a3e2f46ce678b58a5b0b01fcf2a0c65c Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 09:30:10 +0100 Subject: [PATCH 10/20] refactor: Update DoIPServer to use structured types and improve method names --- examples/exampleDoIPServer.cpp | 7 +-- inc/DoIPServer.h | 48 ++++++++++---------- src/DoIPServer.cpp | 68 +++++++++++++---------------- test/DoIPServer_Test.cpp | 14 +++--- test/VehicleIdentification_Test.cpp | 4 +- 5 files changed, 65 insertions(+), 76 deletions(-) diff --git a/examples/exampleDoIPServer.cpp b/examples/exampleDoIPServer.cpp index 3b05bcc..15089bf 100644 --- a/examples/exampleDoIPServer.cpp +++ b/examples/exampleDoIPServer.cpp @@ -98,6 +98,7 @@ int main(int argc, char *argv[]) { doip::ServerConfig cfg; cfg.loopback = useLoopback; cfg.daemonize = daemonize; + // TODO: Use CLI11 or similar for argument parsing if (!vin_str.empty()) cfg.vin = DoIPVIN(vin_str); if (!eid_str.empty()) cfg.eid = DoIPEID(eid_str); if (!gid_str.empty()) cfg.gid = DoIPGID(gid_str); @@ -111,17 +112,17 @@ int main(int argc, char *argv[]) { } else { val = std::stoul(logical_addr_str, &pos, 0); } - cfg.logicalAddress = static_cast(val & 0xFFFF); + cfg.logicalAddress = DoIPAddress(static_cast(val & 0xFFFF)); } catch (...) { LOG_DOIP_WARN("Failed to parse logical address '{}', using default 0x0E00", logical_addr_str); } } else { - cfg.logicalAddress = LOGICAL_ADDRESS.toUint16(); + cfg.logicalAddress = LOGICAL_ADDRESS; } server = std::make_unique(cfg); // Apply defaults used previously in example - server->setFAR(DoIPFurtherAction::NoFurtherAction); + server->setFurtherActionRequired(DoIPFurtherAction::NoFurtherAction); server->setAnnounceInterval(2000); server->setAnnounceNum(10); diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 6c778d9..a7d45d8 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -40,19 +40,23 @@ struct ServerConfig { DoIPVIN vin = DoIPVIN::Zero; // Logical/server address (default 0x0E00) - uint16_t logicalAddress = 0x0028; + DoIPAddress logicalAddress = DoIPAddress(0x0028); // Use loopback announcements instead of broadcast bool loopback = false; // Run the server as a daemon by default bool daemonize = false; + + int announceCount = 3; // Default Value = 3 + unsigned int announceInterval = 500; // Default Value = 500ms + }; const ServerConfig DefaultServerConfig{}; -constexpr int DOIP_SERVER_PORT = 13400; +constexpr int DOIP_SERVER_TCP_PORT = 13400; /** * @brief Callback invoked when a new TCP connection is established @@ -81,7 +85,6 @@ class DoIPServer { DoIPServer(DoIPServer &&) = delete; DoIPServer &operator=(DoIPServer &&) = delete; - // === Low-level API (manual mode) === [[nodiscard]] bool setupTcpSocket(); @@ -94,16 +97,17 @@ class DoIPServer { /** * @brief Check if the server is currently running */ + [[nodiscard]] bool isRunning() const { return m_running.load(); } void setAnnounceNum(int Num); void setAnnounceInterval(unsigned int Interval); - void setAnnouncementMode(bool useLoopback); + void setLoopbackMode(bool useLoopback); void closeTcpSocket(); void closeUdpSocket(); - void setLogicalGatewayAddress(const unsigned short inputLogAdd); + void setLogicalGatewayAddress(unsigned short logicalAddress); /** * @brief Sets the EID to a default value based on the MAC address. @@ -111,19 +115,23 @@ class DoIPServer { * @return true if the EID was successfully set to the default value. * @return false if the default EID could not be set. */ - bool setEIDdefault(); + bool setDefaultEid(); + + void setVin(const std::string &VINString); + void setVin(const DoIPVIN &vin); + const DoIPVIN &getVin() const { return m_config.vin; } - void setVIN(const std::string &VINString); - void setVIN(const DoIPVIN &vin); - const DoIPVIN &getVIN() const { return m_VIN; } + void setEid(uint64_t nputEID); + const DoIPEID &getEid() const { return m_config.eid; } - void setEID(const uint64_t inputEID); - const DoIPEID &getEID() const { return m_EID; } + void setGid(uint64_t inputGID); + const DoIPGID &getGid() const { return m_config.gid; } - void setGID(const uint64_t inputGID); - const DoIPGID &getGID() const { return m_GID; } + DoIPFurtherAction getFurtherActionRequired() const { return m_FurtherActionReq; } + void setFurtherActionRequired(DoIPFurtherAction furtherActionRequired); - void setFAR(DoIPFurtherAction inputFAR); + std::string getClientIp() const { return m_clientIp; } + int getClientPort() const { return m_clientPort; } private: int m_tcp_sock{-1}; @@ -133,24 +141,12 @@ class DoIPServer { ByteArray m_receiveBuf{}; std::string m_clientIp{}; int m_clientPort{}; - - DoIPVIN m_VIN; - DoIPAddress m_logicalAddress = DoIPAddress(0x0028); - DoIPEID m_EID = DoIPEID::Zero; - DoIPGID m_GID = DoIPGID::Zero; DoIPFurtherAction m_FurtherActionReq = DoIPFurtherAction::NoFurtherAction; - int m_announceNum = 3; // Default Value = 3 - unsigned int m_announceInterval = 500; // Default Value = 500ms - bool m_loopbackMode = false; // Default: use broadcast - - int m_broadcast = 1; - // Automatic mode state std::atomic m_running{false}; std::vector m_workerThreads; std::mutex m_mutex; - ConnectionAcceptedHandler m_connectionHandler; // Server configuration ServerConfig m_config; diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index 98b92af..d57b838 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -34,20 +34,12 @@ DoIPServer::DoIPServer(const ServerConfig &config) : m_config(config) { m_receiveBuf.reserve(MAX_ISOTP_MTU); - // Apply VIN/EID/GID from config (types enforce size/padding) - m_VIN = m_config.vin; - m_EID = m_config.eid; - m_GID = m_config.gid; - - // Set logical gateway address from config - m_logicalAddress.update(m_config.logicalAddress); - // Apply EID/GID if provided (interpreted as numeric where possible) // (old parsing logic removed - ServerConfig uses typed identifiers) if (!m_config.loopback) { - setAnnouncementMode(false); + setLoopbackMode(false); } else { - setAnnouncementMode(true); + setLoopbackMode(true); } if (m_config.daemonize) { @@ -153,7 +145,7 @@ void DoIPServer::connectionHandlerThread(std::unique_ptr connect * Set up a tcp socket, so the socket is ready to accept a connection */ bool DoIPServer::setupTcpSocket() { - LOG_DOIP_DEBUG("Setting up TCP socket on port {}", DOIP_SERVER_PORT); + LOG_DOIP_DEBUG("Setting up TCP socket on port {}", DOIP_SERVER_TCP_PORT); m_tcp_sock = socket(AF_INET, SOCK_STREAM, 0); if (m_tcp_sock < 0) { @@ -169,7 +161,7 @@ bool DoIPServer::setupTcpSocket() { m_serverAddress.sin_family = AF_INET; m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - m_serverAddress.sin_port = htons(DOIP_SERVER_PORT); + m_serverAddress.sin_port = htons(DOIP_SERVER_TCP_PORT); // binds the socket to the address and port number if (bind(m_tcp_sock, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)) < 0) { @@ -177,7 +169,7 @@ bool DoIPServer::setupTcpSocket() { return false; } - LOG_TCP_INFO("TCP socket successfully bound to port {}", DOIP_SERVER_PORT); + LOG_TCP_INFO("TCP socket successfully bound to port {}", DOIP_SERVER_TCP_PORT); return true; } @@ -220,7 +212,7 @@ bool DoIPServer::setupUdpSocket() { return 1; } // setting the IP DoIPAddress for Multicast/Broadcast - if (!m_loopbackMode) { // + if (!m_config.loopback) { // setMulticastGroup("224.0.0.2"); LOG_UDP_INFO("UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT); } else { @@ -249,53 +241,53 @@ void DoIPServer::closeUdpSocket() { close(m_udp_sock); } -bool DoIPServer::setEIDdefault() { +bool DoIPServer::setDefaultEid() { MacAddress mac = {0}; if (!getFirstMacAddress(mac)) { LOG_DOIP_ERROR("Failed to get MAC address, using default EID"); - m_EID = DoIPEID::Zero; + m_config.eid = DoIPEID::Zero; return false; } // Set EID based on MAC address (last 6 bytes) - m_EID = DoIPEID(mac.data(), m_EID.ID_LENGTH); + m_config.eid = DoIPEID(mac.data(), m_config.eid.ID_LENGTH); return true; } -void DoIPServer::setVIN(const std::string &VINString) { +void DoIPServer::setVin(const std::string &VINString) { - m_VIN = DoIPVIN(VINString); + m_config.vin = DoIPVIN(VINString); } -void DoIPServer::setVIN(const DoIPVIN &vin) { - m_VIN = vin; +void DoIPServer::setVin(const DoIPVIN &vin) { + m_config.vin = vin; } -void DoIPServer::setLogicalGatewayAddress(const unsigned short inputLogAdd) { - m_logicalAddress.update(inputLogAdd); +void DoIPServer::setLogicalGatewayAddress(const unsigned short logicalAddress) { + m_config.logicalAddress.update(logicalAddress); } -void DoIPServer::setEID(const uint64_t inputEID) { - m_EID = DoIPEID(inputEID); +void DoIPServer::setEid(const uint64_t inputEID) { + m_config.eid = DoIPEID(inputEID); } -void DoIPServer::setGID(const uint64_t inputGID) { - m_GID = DoIPGID(inputGID); +void DoIPServer::setGid(const uint64_t inputGID) { + m_config.gid = DoIPGID(inputGID); } -void DoIPServer::setFAR(DoIPFurtherAction inputFAR) { - m_FurtherActionReq = inputFAR; +void DoIPServer::setFurtherActionRequired(DoIPFurtherAction furtherActionRequired) { + m_FurtherActionReq = furtherActionRequired; } void DoIPServer::setAnnounceNum(int Num) { - m_announceNum = Num; + m_config.announceCount = Num; } void DoIPServer::setAnnounceInterval(unsigned int Interval) { - m_announceInterval = Interval; + m_config.announceInterval = Interval; } -void DoIPServer::setAnnouncementMode(bool useLoopback) { - m_loopbackMode = useLoopback; +void DoIPServer::setLoopbackMode(bool useLoopback) { + m_config.loopback = useLoopback; if (useLoopback) { LOG_DOIP_INFO("Vehicle announcements will use loopback (127.0.0.1)"); } else { @@ -386,7 +378,7 @@ void DoIPServer::udpListenerThread() { LOG_UDP_INFO("RX: {}", fmt::streamed(plType)); switch (plType) { case DoIPPayloadType::VehicleIdentificationRequest: { - DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_logicalAddress, m_EID, m_GID); + DoIPMessage msg = message::makeVehicleIdentificationResponse(m_config.vin, m_config.logicalAddress, m_config.eid, m_config.gid); LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); auto sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, @@ -415,16 +407,16 @@ void DoIPServer::udpAnnouncementThread() { LOG_DOIP_INFO("Announcement thread started"); // Send announcements with configured interval and count - for (int i = 0; i < m_announceNum && m_running; i++) { + for (int i = 0; i < m_config.announceCount && m_running; i++) { sendVehicleAnnouncement2(); - usleep(m_announceInterval * 1000); + usleep(m_config.announceInterval * 1000); } LOG_DOIP_INFO("Announcement thread stopped"); } ssize_t DoIPServer::sendVehicleAnnouncement2() { - DoIPMessage msg = message::makeVehicleIdentificationResponse(m_VIN, m_logicalAddress, m_EID, m_GID); + DoIPMessage msg = message::makeVehicleIdentificationResponse(m_config.vin, m_config.logicalAddress, m_config.eid, m_config.gid); struct sockaddr_in dest_addr; memset(&dest_addr, 0, sizeof(dest_addr)); @@ -432,7 +424,7 @@ ssize_t DoIPServer::sendVehicleAnnouncement2() { dest_addr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); const char *dest_ip; - if (m_loopbackMode) { + if (m_config.loopback) { dest_ip = "127.0.0.1"; inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr); } else { diff --git a/test/DoIPServer_Test.cpp b/test/DoIPServer_Test.cpp index 24c0e75..1a27570 100644 --- a/test/DoIPServer_Test.cpp +++ b/test/DoIPServer_Test.cpp @@ -28,8 +28,8 @@ TEST_SUITE("DoIPServer Tests") { */ TEST_CASE_FIXTURE(DoIPServerFixture, "Set VIN Test") { std::string testVIN = "TESTVIN1234567890"; - server.setVIN(testVIN); - DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVIN(), DoIPAddress::ZeroAddress, server.getEID(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); + server.setVin(testVIN); + DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); ByteArrayRef payload = msg.getPayload(); // Check that the VIN in the payload matches the set VIN @@ -40,8 +40,8 @@ TEST_SUITE("DoIPServer Tests") { TEST_CASE_FIXTURE(DoIPServerFixture, "Set EID Test") { uint64_t testEID = 0x123456789ABC; - server.setEID(testEID); - DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVIN(), DoIPAddress::ZeroAddress, server.getEID(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); + server.setEid(testEID); + DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); ByteArrayRef payload = msg.getPayload(); // Check that the EID in the payload matches the set EID @@ -51,13 +51,13 @@ TEST_SUITE("DoIPServer Tests") { } TEST_CASE_FIXTURE(DoIPServerFixture, "Set EID default") { - bool result = server.setEIDdefault(); + bool result = server.setDefaultEid(); CHECK(result == true); - DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVIN(), DoIPAddress::ZeroAddress, server.getEID(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); + DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); ByteArrayRef payload = msg.getPayload(); - std::cerr << "EID set to: " << server.getEID().toHexString() << '\n'; + std::cerr << "EID set to: " << server.getEid().toHexString() << '\n'; auto zeros = std::count_if(payload.first + 17 + 2, payload.first + 17 + 2 + 6, [](uint8_t byte) { return byte == 0; }); CHECK(zeros < 6); // At least one byte should not be zero } diff --git a/test/VehicleIdentification_Test.cpp b/test/VehicleIdentification_Test.cpp index 553104f..ffd98b7 100644 --- a/test/VehicleIdentification_Test.cpp +++ b/test/VehicleIdentification_Test.cpp @@ -15,7 +15,7 @@ TEST_SUITE("VehicleIdentificationHandler") { DoIPVIN shortVINPadded = DoIPVIN("shortVin000000000"); DoIPEID EID = DoIPEID::Zero; DoIPGID GID = DoIPGID::Zero; - DoIPFurtherAction far = DoIPFurtherAction::NoFurtherAction; + DoIPFurtherAction furtherActionRequired = DoIPFurtherAction::NoFurtherAction; DoIPFurtherAction far_cs = DoIPFurtherAction::RoutingActivationForCentralSecurity; VehicleIdentificationHandlerFixture() { @@ -31,7 +31,7 @@ TEST_SUITE("VehicleIdentificationHandler") { * Checks if a VIN with 17 bytes matches correctly the input data */ TEST_CASE_FIXTURE(VehicleIdentificationHandlerFixture, "VIN 17 Bytes") { - DoIPMessage msg = message::makeVehicleIdentificationResponse(matchingVIN, DoIPAddress::ZeroAddress, EID, GID, far); + DoIPMessage msg = message::makeVehicleIdentificationResponse(matchingVIN, DoIPAddress::ZeroAddress, EID, GID, furtherActionRequired); ByteArrayRef payload = msg.getPayload(); ByteArray expected{ // VIN (17 bytes) From aa39417203661a02bd4e7511e8ef652b407d6d96 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 09:55:40 +0100 Subject: [PATCH 11/20] refactor: Update Doxyfile configuration for improved documentation generation --- Doxyfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doxyfile b/Doxyfile index 2213d85..dd57630 100644 --- a/Doxyfile +++ b/Doxyfile @@ -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 @@ -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 @@ -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" @@ -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 From f53da2217d40df324587f60c5c67bbe2f21b6662 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 09:56:06 +0100 Subject: [PATCH 12/20] style: Add comments, format code --- inc/DoIPServer.h | 107 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 14 deletions(-) diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index a7d45d8..29e8721 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -9,8 +9,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -18,8 +18,8 @@ #include #include "ByteArray.h" -#include "DoIPConnection.h" #include "DoIPConfig.h" +#include "DoIPConnection.h" #include "DoIPFurtherAction.h" #include "DoIPIdentifiers.h" #include "DoIPNegativeAck.h" @@ -48,14 +48,12 @@ struct ServerConfig { // Run the server as a daemon by default bool daemonize = false; - int announceCount = 3; // Default Value = 3 - unsigned int announceInterval = 500; // Default Value = 500ms - + int announceCount = 3; // Default Value = 3 + unsigned int announceInterval = 500; // Default Value = 500ms }; const ServerConfig DefaultServerConfig{}; - constexpr int DOIP_SERVER_TCP_PORT = 13400; /** @@ -67,17 +65,20 @@ using ConnectionAcceptedHandler = std::function(D /** * @brief DoIP Server class to handle incoming DoIP connections and UDP messages. * - * This class manages the low-level TCP/UDP socket handling and provides a clean - * callback-based interface for application logic. The server can operate in two modes: - * - * 1. Manual mode: User calls waitForTcpConnection() and manages the receive loop - * 2. Automatic mode: User calls start() with a callback, server manages everything + * This class manages the low-level TCP/UDP socket handling. */ class DoIPServer { public: + /** + * @brief Construct a DoIP server with the given configuration. + * @param config Server configuration (EID/GID/VIN, announce params, etc.). + */ explicit DoIPServer(const ServerConfig &config = DefaultServerConfig); + /** + * @brief Destructor. Ensures sockets/threads are closed/stopped. + */ ~DoIPServer(); DoIPServer(const DoIPServer &) = delete; @@ -86,12 +87,25 @@ class DoIPServer { DoIPServer &operator=(DoIPServer &&) = delete; [[nodiscard]] + /** + * @brief Initialize and bind the TCP socket for DoIP. + * @return true on success, false otherwise. + */ bool setupTcpSocket(); template + /** + * @brief Block until a TCP client connects and create a DoIP connection. + * @tparam Model Server model type used by the connection (default `DefaultDoIPServerModel`). + * @return Unique pointer to established `DoIPConnection`, or nullptr on failure. + */ std::unique_ptr waitForTcpConnection(); [[nodiscard]] + /** + * @brief Initialize and bind the UDP socket for announcements and UDP messages. + * @return true on success, false otherwise. + */ bool setupUdpSocket(); /** @@ -100,13 +114,35 @@ class DoIPServer { [[nodiscard]] bool isRunning() const { return m_running.load(); } + /** + * @brief Set the number of vehicle announcements to send. + * @param Num Count of announcements. + */ void setAnnounceNum(int Num); + /** + * @brief Set the interval between announcements in milliseconds. + * @param Interval Interval in ms. + */ void setAnnounceInterval(unsigned int Interval); + /** + * @brief Enable/disable loopback mode for announcements (no broadcast). + * @param useLoopback True to use loopback, false for broadcast. + */ void setLoopbackMode(bool useLoopback); + /** + * @brief Close the TCP socket if open. + */ void closeTcpSocket(); + /** + * @brief Close the UDP socket if open. + */ void closeUdpSocket(); + /** + * @brief Set the logical DoIP gateway address. + * @param logicalAddress Logical address value. + */ void setLogicalGatewayAddress(unsigned short logicalAddress); /** @@ -117,27 +153,71 @@ class DoIPServer { */ bool setDefaultEid(); + /** + * @brief Set VIN from a 17-character string. + * @param VINString VIN string (17 bytes expected). + */ void setVin(const std::string &VINString); + /** + * @brief Set VIN from a `DoIPVIN` instance. + * @param vin VIN value. + */ void setVin(const DoIPVIN &vin); + /** + * @brief Get current VIN. + * @return Reference to configured VIN. + */ const DoIPVIN &getVin() const { return m_config.vin; } + /** + * @brief Set EID value. + * @param nputEID EID as 64-bit value (lower 48 bits used). + */ void setEid(uint64_t nputEID); + /** + * @brief Get current EID. + * @return Reference to configured EID. + */ const DoIPEID &getEid() const { return m_config.eid; } + /** + * @brief Set GID value. + * @param inputGID GID as 64-bit value (lower 48 bits used). + */ void setGid(uint64_t inputGID); + /** + * @brief Get current GID. + * @return Reference to configured GID. + */ const DoIPGID &getGid() const { return m_config.gid; } + /** + * @brief Get current further action requirement status. + * @return Current `DoIPFurtherAction` value. + */ DoIPFurtherAction getFurtherActionRequired() const { return m_FurtherActionReq; } + /** + * @brief Set further action requirement status. + * @param furtherActionRequired Value to set. + */ void setFurtherActionRequired(DoIPFurtherAction furtherActionRequired); + /** + * @brief Get last accepted client IP (string form). + * @return IP address string. + */ std::string getClientIp() const { return m_clientIp; } + /** + * @brief Get last accepted client TCP port. + * @return Client port number. + */ int getClientPort() const { return m_clientPort; } private: int m_tcp_sock{-1}; int m_udp_sock{-1}; - struct sockaddr_in m_serverAddress {}; - struct sockaddr_in m_clientAddress {}; + struct sockaddr_in m_serverAddress{}; + struct sockaddr_in m_clientAddress{}; ByteArray m_receiveBuf{}; std::string m_clientIp{}; int m_clientPort{}; @@ -213,7 +293,6 @@ void DoIPServer::tcpListenerThread() { LOG_DOIP_INFO("TCP listener thread stopped"); } - } // namespace doip #endif /* DOIPSERVER_H */ \ No newline at end of file From 6faa5cd04cbb3ee0bafe180c5c0775e54d2841f6 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 10:29:22 +0100 Subject: [PATCH 13/20] WIP: Code cleanup --- inc/DoIPServer.h | 4 +++- src/DoIPServer.cpp | 46 +++++++++++++++++++--------------------------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index 29e8721..fd7b00a 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -245,7 +245,9 @@ class DoIPServer { void udpListenerThread(); void udpAnnouncementThread(); - ssize_t sendVehicleAnnouncement2(); + ssize_t sendVehicleAnnouncement(); + + ssize_t sendUdpMessage(const uint8_t *message, size_t messageLength); }; // Template implementation must be in header for external linkage diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index d57b838..c9efed1 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -15,14 +15,6 @@ using namespace doip; -#if defined(__linux__) -const char *DEFAULT_IFACE = "eth0"; -#elif defined(__APPLE__) -const char *DEFAULT_IFACE = "en0"; -#else -#pragma error "Unsupported platform" -#endif - DoIPServer::~DoIPServer() { if (m_running.load()) { @@ -34,13 +26,7 @@ DoIPServer::DoIPServer(const ServerConfig &config) : m_config(config) { m_receiveBuf.reserve(MAX_ISOTP_MTU); - // Apply EID/GID if provided (interpreted as numeric where possible) - // (old parsing logic removed - ServerConfig uses typed identifiers) - if (!m_config.loopback) { - setLoopbackMode(false); - } else { - setLoopbackMode(true); - } + setLoopbackMode(m_config.loopback); if (m_config.daemonize) { daemonize(); @@ -235,8 +221,9 @@ bool DoIPServer::setupUdpSocket() { void DoIPServer::closeUdpSocket() { m_running.store(false); for (auto &thread : m_workerThreads) { - if (thread.joinable()) + if (thread.joinable()) { thread.join(); + } } close(m_udp_sock); } @@ -288,7 +275,7 @@ void DoIPServer::setAnnounceInterval(unsigned int Interval) { void DoIPServer::setLoopbackMode(bool useLoopback) { m_config.loopback = useLoopback; - if (useLoopback) { + if (m_config.loopback) { LOG_DOIP_INFO("Vehicle announcements will use loopback (127.0.0.1)"); } else { LOG_DOIP_INFO("Vehicle announcements will use broadcast (255.255.255.255)"); @@ -329,15 +316,13 @@ ssize_t DoIPServer::sendNegativeUdpAck(DoIPNegativeAck ackCode) { // new version starts here void DoIPServer::udpListenerThread() { - - struct sockaddr_in client_addr; - socklen_t client_len = sizeof(client_addr); + socklen_t client_len = sizeof(m_clientAddress); LOG_UDP_INFO("UDP listener thread started"); while (m_running) { ssize_t received = recvfrom(m_udp_sock, m_receiveBuf.data(), sizeof(m_receiveBuf), 0, - reinterpret_cast(&client_addr), &client_len); + reinterpret_cast(&m_clientAddress), &client_len); if (received < 0) { if (errno == EAGAIN /* || errno == EWOULDBLOCK*/) { @@ -353,9 +338,9 @@ void DoIPServer::udpListenerThread() { if (received > 0) { std::scoped_lock lock(m_mutex); char client_ip[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); + inet_ntop(AF_INET, &m_clientAddress.sin_addr, client_ip, sizeof(client_ip)); m_clientIp = std::string(client_ip); - m_clientPort = ntohs(client_addr.sin_port); + m_clientPort = ntohs(m_clientAddress.sin_port); LOG_UDP_INFO("Received {} bytes from {}:{}", received, m_clientIp, m_clientPort); @@ -382,11 +367,11 @@ void DoIPServer::udpListenerThread() { LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); auto sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, - reinterpret_cast(&client_addr), client_len); + reinterpret_cast(&m_clientAddress), client_len); if (sentBytes > 0) { LOG_DOIP_INFO("Sent Vehicle Identification Response to {}:{}", - sentBytes, client_ip, ntohs(client_addr.sin_port)); + sentBytes, client_ip, ntohs(m_clientAddress.sin_port)); } else { perror("Failed to send response"); } @@ -408,14 +393,14 @@ void DoIPServer::udpAnnouncementThread() { // Send announcements with configured interval and count for (int i = 0; i < m_config.announceCount && m_running; i++) { - sendVehicleAnnouncement2(); + sendVehicleAnnouncement(); usleep(m_config.announceInterval * 1000); } LOG_DOIP_INFO("Announcement thread stopped"); } -ssize_t DoIPServer::sendVehicleAnnouncement2() { +ssize_t DoIPServer::sendVehicleAnnouncement() { DoIPMessage msg = message::makeVehicleIdentificationResponse(m_config.vin, m_config.logicalAddress, m_config.eid, m_config.gid); struct sockaddr_in dest_addr; @@ -447,4 +432,11 @@ ssize_t DoIPServer::sendVehicleAnnouncement2() { LOG_UDP_ERROR("Failed to send announcement: {}", strerror(errno)); } return sentBytes; +} + +ssize_t DoIPServer::sendUdpMessage(const uint8_t *message, size_t messageLength) { + (void)message; + (void)messageLength; + LOG_UDP_CRITICAL("sendUdpMessage NOT IMPL"); + return -1; } \ No newline at end of file From 5a933f153ce3f5f26965e8138227b3279312c75d Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 12:45:05 +0100 Subject: [PATCH 14/20] chore: revisit clang-tidy rules --- .clang-tidy | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 0a0462f..bcca1e5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -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 \ No newline at end of file From 49e8d0f0b3925b8e1c0d8aeeb7e82a24dd54baf1 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 13:06:56 +0100 Subject: [PATCH 15/20] refactor: Introduce sendUdpResponse fix: Make setMulticastGroup const --- inc/DoIPServer.h | 4 +-- src/DoIPServer.cpp | 61 +++++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 33 deletions(-) diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index fd7b00a..fc7aa50 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -234,7 +234,7 @@ class DoIPServer { void stop(); void daemonize(); - void setMulticastGroup(const char *address); + void setMulticastGroup(const char *address) const; ssize_t sendNegativeUdpAck(DoIPNegativeAck ackCode); @@ -247,7 +247,7 @@ class DoIPServer { void udpAnnouncementThread(); ssize_t sendVehicleAnnouncement(); - ssize_t sendUdpMessage(const uint8_t *message, size_t messageLength); + ssize_t sendUdpResponse(DoIPMessage msg); }; // Template implementation must be in header for external linkage diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index c9efed1..3e13c52 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -6,16 +6,15 @@ #include #include -#include "DoIPServer.h" #include "DoIPConnection.h" #include "DoIPMessage.h" +#include "DoIPServer.h" #include "DoIPServerModel.h" #include "Logger.h" #include "MacAddress.h" using namespace doip; - DoIPServer::~DoIPServer() { if (m_running.load()) { stop(); @@ -249,7 +248,7 @@ void DoIPServer::setVin(const DoIPVIN &vin) { m_config.vin = vin; } -void DoIPServer::setLogicalGatewayAddress(const unsigned short logicalAddress) { +void DoIPServer::setLogicalGatewayAddress(unsigned short logicalAddress) { m_config.logicalAddress.update(logicalAddress); } @@ -282,7 +281,7 @@ void DoIPServer::setLoopbackMode(bool useLoopback) { } } -void DoIPServer::setMulticastGroup(const char *address) { +void DoIPServer::setMulticastGroup(const char *address) const { int loop = 1; // set Option using the same Port for multiple Sockets @@ -306,12 +305,9 @@ void DoIPServer::setMulticastGroup(const char *address) { } ssize_t DoIPServer::sendNegativeUdpAck(DoIPNegativeAck ackCode) { - // DoIPMessage message = message::makeNegativeAckMessage(ackCode); + DoIPMessage msg = message::makeNegativeAckMessage(ackCode); - // return sendUdpMessage(message.data(), message.size()); - (void)ackCode; - LOG_UDP_CRITICAL("sendNegativeUdpAck NOT IMPL"); - return -1; + return sendUdpResponse(msg); } // new version starts here @@ -349,38 +345,34 @@ void DoIPServer::udpListenerThread() { auto sentBytes = sendNegativeUdpAck(DoIPNegativeAck::IncorrectPatternFormat); if (sentBytes < 0) { if (errno == EAGAIN /*|| errno == EWOULDBLOCK*/) { - // Timeout, continue + usleep(100); continue; } - if (m_running) { - LOG_UDP_ERROR("Failed to receive: {}", strerror(errno)); - } break; } } auto plType = optHeader->first; // auto payloadLength = optHeader->second; LOG_UDP_INFO("RX: {}", fmt::streamed(plType)); + + ssize_t sentBytes = 0; switch (plType) { case DoIPPayloadType::VehicleIdentificationRequest: { DoIPMessage msg = message::makeVehicleIdentificationResponse(m_config.vin, m_config.logicalAddress, m_config.eid, m_config.gid); - LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); - - auto sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, - reinterpret_cast(&m_clientAddress), client_len); - - if (sentBytes > 0) { - LOG_DOIP_INFO("Sent Vehicle Identification Response to {}:{}", - sentBytes, client_ip, ntohs(m_clientAddress.sin_port)); - } else { - perror("Failed to send response"); - } + sentBytes = sendUdpResponse(msg); } break; default: { LOG_DOIP_ERROR("Invalid payload type 0x{:04X} received (receiveUdpMessage())", static_cast(plType)); - /*auto sentBytes =*/sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); + sentBytes = sendNegativeUdpAck(DoIPNegativeAck::UnknownPayloadType); } + if (sentBytes < 0) { + if (errno == EAGAIN /*|| errno == EWOULDBLOCK*/) { + usleep(100); + continue; + } + break; + } } } } @@ -427,16 +419,23 @@ ssize_t DoIPServer::sendVehicleAnnouncement() { LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); if (sentBytes > 0) { LOG_UDP_INFO("Sent Vehicle Announcement: {} bytes to {}:{}", - sentBytes, dest_ip, DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); + sentBytes, dest_ip, DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); } else { LOG_UDP_ERROR("Failed to send announcement: {}", strerror(errno)); } return sentBytes; } -ssize_t DoIPServer::sendUdpMessage(const uint8_t *message, size_t messageLength) { - (void)message; - (void)messageLength; - LOG_UDP_CRITICAL("sendUdpMessage NOT IMPL"); - return -1; +ssize_t DoIPServer::sendUdpResponse(DoIPMessage msg) { + auto sentBytes = sendto(m_udp_sock, msg.data(), msg.size(), 0, + reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); + + if (sentBytes > 0) { + LOG_DOIP_INFO("TX {}", fmt::streamed(msg)); + LOG_UDP_INFO("Sent UDS response: {} bytes to {}:{}", + sentBytes, m_clientIp, ntohs(m_clientAddress.sin_port)); + } else { + LOG_DOIP_ERROR("Failed to send message: {}", strerror(errno)); + } + return sentBytes; } \ No newline at end of file From 800d90cd6071de9924639f39dd2942b7228fd36e Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 13:25:53 +0100 Subject: [PATCH 16/20] feat: Introduce DOIP_MAXIMUM_MTU --- CMakeLists.txt | 3 ++- inc/DoIPConnection.h | 5 ++--- inc/gen/DoIPConfig.h.in | 13 +++++++++++-- src/DoIPServer.cpp | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 02a81e7..b1c33d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/inc/DoIPConnection.h b/inc/DoIPConnection.h index 716dd9d..3a3a187 100644 --- a/inc/DoIPConnection.h +++ b/inc/DoIPConnection.h @@ -2,6 +2,7 @@ #define DOIPCONNECTION_H +#include "DoIPConfig.h" #include "DoIPMessage.h" #include "DoIPNegativeAck.h" #include "DoIPNegativeDiagnosticAck.h" @@ -20,8 +21,6 @@ namespace doip { -/** Maximum size of the ISO-TP message - used as initial value for RX buffer to avoid reallocs */ -constexpr size_t MAX_ISOTP_MTU = 4095; class DoIPConnection : public DoIPDefaultConnection { public: @@ -104,7 +103,7 @@ class DoIPConnection : public DoIPDefaultConnection { // TCP socket-specific members int m_tcpSocket; - std::array m_receiveBuf{}; + std::array m_receiveBuf{}; bool m_isClosing{false}; // TODO: Guard against recursive closeConnection calls -> solve this std::optional m_pendingDownstreamRequest; diff --git a/inc/gen/DoIPConfig.h.in b/inc/gen/DoIPConfig.h.in index b017259..669f986 100644 --- a/inc/gen/DoIPConfig.h.in +++ b/inc/gen/DoIPConfig.h.in @@ -1,5 +1,7 @@ #pragma once +#include + #define DOIP_TIMEOUT_MS @DOIP_TIMEOUT_MS@ #define DOIP_MAX_CONNECTIONS @DOIP_MAX_CONNECTIONS@ @@ -10,7 +12,14 @@ * @note This value is configurable via CMake option DOIP_ALIVE_CHECK_RETRIES. * @note The standard does not mandate retries, so the default is 1 (no retries). For robustness, retries can be enabled. */ -#define DOIP_ALIVE_CHECK_RETRIES @DOIP_ALIVE_CHECK_RETRIES@ +constexpr uint8_t DOIP_ALIVE_CHECK_RETRIES = @DOIP_ALIVE_CHECK_RETRIES@; + +/** + * @brief Maximum Transmission Unit (MTU) size for DoIP messages. + * Currently set to 4095 bytes as per ISO 13400-2:2019 recommendations. + */ +constexpr uint32_t DOIP_MAXIMUM_MTU = @DOIP_MAXIMUM_MTU@; + // Table 48: UDP Ports for DoIP /** @@ -19,6 +28,6 @@ constexpr int DOIP_UDP_DISCOVERY_PORT = 13400; /** - * @brief UDP test equipment request port for DoIP as per ISO 13400-2:2019 + * @brief UDP test equipment request port for DoIP as per ISO 13400-2:2019. */ constexpr int DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT = 13401; \ No newline at end of file diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index 3e13c52..a33b800 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -23,7 +23,7 @@ DoIPServer::~DoIPServer() { DoIPServer::DoIPServer(const ServerConfig &config) : m_config(config) { - m_receiveBuf.reserve(MAX_ISOTP_MTU); + m_receiveBuf.reserve(DOIP_MAXIMUM_MTU); setLoopbackMode(m_config.loopback); From 267e585fdcfa6e953bc21dd4cb376a5b1b23cfd0 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 13:26:27 +0100 Subject: [PATCH 17/20] refactor: Adjust member names --- inc/DoIPClient.h | 16 ++++--- src/DoIPClient.cpp | 114 ++++++++++++++++++++++----------------------- 2 files changed, 66 insertions(+), 64 deletions(-) diff --git a/inc/DoIPClient.h b/inc/DoIPClient.h index 166bf83..dd38793 100644 --- a/inc/DoIPClient.h +++ b/inc/DoIPClient.h @@ -22,6 +22,8 @@ using DoIPRequest = std::pair; class DoIPClient { public: + DoIPClient() {m_receiveBuf.reserve(DOIP_MAXIMUM_MTU);} + void startTcpConnection(); void startUdpConnection(); void startAnnouncementListener(); @@ -56,17 +58,17 @@ class DoIPClient { int getConnected(); private: - uint8_t _receivedData[_maxDataSize] = {0}; - int _sockFd{-1}, _sockFd_udp{-1}, _sockFd_announcement{-1}, _connected{-1}; + ByteArray m_receiveBuf; + int m_tcpSocket{-1}, m_udpSocket{-1}, m_udpAnnouncementSocket{-1}, m_connected{-1}; int m_broadcast = 1; - struct sockaddr_in _serverAddr, _clientAddr, _announcementAddr; + struct sockaddr_in m_serverAddress, m_clientAddress, m_announcementAddress; DoIPAddress m_sourceAddress = DoIPAddress(0xE000); - uint8_t VINResult[17] = {0}; + uint8_t m_vin[17] = {0}; DoIPAddress m_logicalAddress = DoIPAddress::ZeroAddress; - uint8_t EIDResult[6] = {0}; - uint8_t GIDResult[6] = {0}; - uint8_t FurtherActionReqResult = 0x00; + uint8_t m_eid[6] = {0}; + uint8_t m_gid[6] = {0}; + uint8_t m_furtherActionReqResult = 0x00; void parseVIResponseInformation(const uint8_t *data); diff --git a/src/DoIPClient.cpp b/src/DoIPClient.cpp index b60ed06..c42b78b 100644 --- a/src/DoIPClient.cpp +++ b/src/DoIPClient.cpp @@ -12,20 +12,20 @@ using namespace doip; */ void DoIPClient::startTcpConnection() { - _sockFd = socket(AF_INET, SOCK_STREAM, 0); + m_tcpSocket = socket(AF_INET, SOCK_STREAM, 0); - if (_sockFd >= 0) { + if (m_tcpSocket >= 0) { LOG_TCP_INFO("Client TCP-Socket created successfully"); bool connectedFlag = false; const char *ipAddr = "127.0.0.1"; - _serverAddr.sin_family = AF_INET; - _serverAddr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); - inet_aton(ipAddr, &(_serverAddr.sin_addr)); + m_serverAddress.sin_family = AF_INET; + m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); + inet_aton(ipAddr, &(m_serverAddress.sin_addr)); while (!connectedFlag) { - _connected = connect(_sockFd, reinterpret_cast(&_serverAddr), sizeof(_serverAddr)); - if (_connected != -1) { + m_connected = connect(m_tcpSocket, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)); + if (m_connected != -1) { connectedFlag = true; LOG_TCP_INFO("Connection to server established"); } @@ -35,48 +35,48 @@ void DoIPClient::startTcpConnection() { void DoIPClient::startUdpConnection() { - _sockFd_udp = socket(AF_INET, SOCK_DGRAM, 0); + m_udpSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (_sockFd_udp >= 0) { + if (m_udpSocket >= 0) { LOG_UDP_INFO("Client-UDP-Socket created successfully"); - _serverAddr.sin_family = AF_INET; - _serverAddr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); // 13400 - _serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); + m_serverAddress.sin_family = AF_INET; + m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); // 13400 + m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); - _clientAddr.sin_family = AF_INET; - _clientAddr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); - _clientAddr.sin_addr.s_addr = htonl(INADDR_ANY); + m_clientAddress.sin_family = AF_INET; + m_clientAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); + m_clientAddress.sin_addr.s_addr = htonl(INADDR_ANY); // binds the socket to any IP DoIPAddress and the Port Number 13400 - bind(_sockFd_udp, reinterpret_cast(&_clientAddr), sizeof(_clientAddr)); + bind(m_udpSocket, reinterpret_cast(&m_clientAddress), sizeof(m_clientAddress)); } } void DoIPClient::startAnnouncementListener() { - _sockFd_announcement = socket(AF_INET, SOCK_DGRAM, 0); + m_udpAnnouncementSocket = socket(AF_INET, SOCK_DGRAM, 0); - if (_sockFd_announcement >= 0) { + if (m_udpAnnouncementSocket >= 0) { LOG_UDP_INFO("Client-Announcement-Socket created successfully"); // Allow socket reuse for broadcast int reuse = 1; - setsockopt(_sockFd_announcement, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + setsockopt(m_udpAnnouncementSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); // Enable broadcast reception int broadcast = 1; - if (setsockopt(_sockFd_announcement, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0) { + if (setsockopt(m_udpAnnouncementSocket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0) { LOG_UDP_ERROR("Failed to enable broadcast reception: {}", strerror(errno)); } else { LOG_UDP_INFO("Broadcast reception enabled for announcements"); } - _announcementAddr.sin_family = AF_INET; - _announcementAddr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); // Port 13401 - _announcementAddr.sin_addr.s_addr = htonl(INADDR_ANY); + m_announcementAddress.sin_family = AF_INET; + m_announcementAddress.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); // Port 13401 + m_announcementAddress.sin_addr.s_addr = htonl(INADDR_ANY); // Bind to port 13401 for Vehicle Announcements - if (bind(_sockFd_announcement, reinterpret_cast(&_announcementAddr), sizeof(_announcementAddr)) < 0) { + if (bind(m_udpAnnouncementSocket, reinterpret_cast(&m_announcementAddress), sizeof(m_announcementAddress)) < 0) { LOG_UDP_ERROR("Failed to bind announcement socket to port {}: {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT, strerror(errno)); } else { LOG_UDP_INFO("Announcement socket bound to port {} successfully", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); @@ -90,13 +90,13 @@ void DoIPClient::startAnnouncementListener() { * closes the client-socket */ void DoIPClient::closeTcpConnection() { - close(_sockFd); + close(m_tcpSocket); } void DoIPClient::closeUdpConnection() { - close(_sockFd_udp); - if (_sockFd_announcement >= 0) { - close(_sockFd_announcement); + close(m_udpSocket); + if (m_udpAnnouncementSocket >= 0) { + close(m_udpAnnouncementSocket); } } @@ -108,20 +108,20 @@ void DoIPClient::reconnectServer() { ssize_t DoIPClient::sendRoutingActivationRequest() { DoIPMessage routingActReq = message::makeRoutingActivationRequest(m_sourceAddress); LOG_DOIP_INFO("TX: {}", fmt::streamed(routingActReq)); - return write(_sockFd, routingActReq.data(), routingActReq.size()); + return write(m_tcpSocket, routingActReq.data(), routingActReq.size()); } ssize_t DoIPClient::sendDiagnosticMessage(const ByteArray &payload) { DoIPMessage msg = message::makeDiagnosticMessage(m_sourceAddress, m_logicalAddress, payload); LOG_DOIP_INFO("TX: {}", fmt::streamed(msg)); - return write(_sockFd, msg.data(), msg.size()); + return write(m_tcpSocket, msg.data(), msg.size()); } ssize_t DoIPClient::sendAliveCheckResponse() { DoIPMessage msg = message::makeAliveCheckResponse(m_sourceAddress); LOG_DOIP_INFO("TX: {}", fmt::streamed(msg)); - return write(_sockFd, msg.data(), msg.size()); + return write(m_tcpSocket, msg.data(), msg.size()); } /* @@ -129,7 +129,7 @@ ssize_t DoIPClient::sendAliveCheckResponse() { */ void DoIPClient::receiveMessage() { - ssize_t bytesRead = recv(_sockFd, _receivedData, _maxDataSize, 0); + ssize_t bytesRead = recv(m_tcpSocket, m_receiveBuf.data(), _maxDataSize, 0); if (bytesRead < 0) { LOG_DOIP_ERROR("Error receiving data from server"); @@ -148,7 +148,7 @@ void DoIPClient::receiveMessage() { return; } - auto optMmsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); + auto optMmsg = DoIPMessage::tryParse(m_receiveBuf.data(), static_cast(bytesRead)); if (!optMmsg.has_value()) { LOG_DOIP_ERROR("Failed to parse DoIP message from received data"); return; @@ -159,16 +159,16 @@ void DoIPClient::receiveMessage() { void DoIPClient::receiveUdpMessage() { - unsigned int length = sizeof(_clientAddr); + unsigned int length = sizeof(m_clientAddress); // Set socket to timeout after 3 seconds struct timeval timeout; timeout.tv_sec = 3; timeout.tv_usec = 0; - setsockopt(_sockFd_udp, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(m_udpSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); int bytesRead; - bytesRead = recvfrom(_sockFd_udp, _receivedData, _maxDataSize, 0, reinterpret_cast(&_clientAddr), &length); + bytesRead = recvfrom(m_udpSocket, m_receiveBuf.data(), _maxDataSize, 0, reinterpret_cast(&m_clientAddress), &length); if (bytesRead < 0) { if (errno == EAGAIN) { @@ -181,7 +181,7 @@ void DoIPClient::receiveUdpMessage() { LOG_UDP_INFO("Received {} bytes from UDP", bytesRead); - auto optMmsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); + auto optMmsg = DoIPMessage::tryParse(m_receiveBuf.data(), static_cast(bytesRead)); if (!optMmsg.has_value()) { LOG_UDP_ERROR("Failed to parse DoIP message from UDP data"); return; @@ -193,7 +193,7 @@ void DoIPClient::receiveUdpMessage() { } bool DoIPClient::receiveVehicleAnnouncement() { - unsigned int length = sizeof(_announcementAddr); + unsigned int length = sizeof(m_announcementAddress); int bytesRead; LOG_UDP_DEBUG("Listening for Vehicle Announcements on port {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); @@ -202,10 +202,10 @@ bool DoIPClient::receiveVehicleAnnouncement() { struct timeval timeout; timeout.tv_sec = 2; // 2 second timeout timeout.tv_usec = 0; - setsockopt(_sockFd_announcement, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + setsockopt(m_udpAnnouncementSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); - bytesRead = recvfrom(_sockFd_announcement, _receivedData, _maxDataSize, 0, - reinterpret_cast(&_announcementAddr), &length); + bytesRead = recvfrom(m_udpAnnouncementSocket, m_receiveBuf.data(), _maxDataSize, 0, + reinterpret_cast(&m_announcementAddress), &length); if (bytesRead < 0) { if (errno == EAGAIN) { LOG_UDP_WARN("Timeout waiting for Vehicle Announcement"); @@ -215,7 +215,7 @@ bool DoIPClient::receiveVehicleAnnouncement() { return false; } - auto optMsg = DoIPMessage::tryParse(_receivedData, static_cast(bytesRead)); + auto optMsg = DoIPMessage::tryParse(m_receiveBuf.data(), static_cast(bytesRead)); if (!optMsg.has_value()) { LOG_UDP_ERROR("Failed to parse Vehicle Announcement message"); return false; @@ -235,7 +235,7 @@ bool DoIPClient::receiveVehicleAnnouncement() { ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { - int setAddressError = inet_aton(inet_address, &(_serverAddr.sin_addr)); + int setAddressError = inet_aton(inet_address, &(m_serverAddress.sin_addr)); if (setAddressError != 0) { LOG_UDP_INFO("Address set successfully"); @@ -243,7 +243,7 @@ ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { LOG_UDP_ERROR("Could not set address. Try again"); } - int socketError = setsockopt(_sockFd_udp, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); + int socketError = setsockopt(m_udpSocket, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast)); if (socketError == 0) { LOG_UDP_INFO("Broadcast Option set successfully"); @@ -251,8 +251,8 @@ ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) { DoIPMessage vehicleIdReq = message::makeVehicleIdentificationRequest(); - ssize_t bytesSent = sendto(_sockFd_udp, vehicleIdReq.data(), vehicleIdReq.size(), 0, reinterpret_cast(&_serverAddr), sizeof(_serverAddr)); - LOG_UDP_INFO("Sent Vehicle Identification Request to {}:{}", inet_address, ntohs(_serverAddr.sin_port)); + ssize_t bytesSent = sendto(m_udpSocket, vehicleIdReq.data(), vehicleIdReq.size(), 0, reinterpret_cast(&m_serverAddress), sizeof(m_serverAddress)); + LOG_UDP_INFO("Sent Vehicle Identification Request to {}:{}", inet_address, ntohs(m_serverAddress.sin_port)); if (bytesSent > 0) { LOG_DOIP_INFO("Sending Vehicle Identification Request"); @@ -273,14 +273,14 @@ void DoIPClient::setSourceAddress(const DoIPAddress &address) { * Getter for _sockFD */ int DoIPClient::getSockFd() { - return _sockFd; + return m_tcpSocket; } /* - * Getter for _connected + * Getter for m_connected */ int DoIPClient::getConnected() { - return _connected; + return m_connected; } void DoIPClient::parseVIResponseInformation(const uint8_t *data) { @@ -288,7 +288,7 @@ void DoIPClient::parseVIResponseInformation(const uint8_t *data) { // VIN int j = 0; for (int i = 8; i <= 24; i++) { - VINResult[j] = data[i]; + m_vin[j] = data[i]; j++; } @@ -298,19 +298,19 @@ void DoIPClient::parseVIResponseInformation(const uint8_t *data) { // EID j = 0; for (int i = 27; i <= 32; i++) { - EIDResult[j] = data[i]; + m_eid[j] = data[i]; j++; } // GID j = 0; for (int i = 33; i <= 38; i++) { - GIDResult[j] = data[i]; + m_gid[j] = data[i]; j++; } // FurtherActionRequest - FurtherActionReqResult = data[39]; + m_furtherActionReqResult = data[39]; } void DoIPClient::displayVIResponseInformation() { @@ -321,7 +321,7 @@ void DoIPClient::displayVIResponseInformation() { ss << ansi::bold_green; } for (int i = 0; i < 17; i++) { - ss << VINResult[i]; + ss << m_vin[i]; } if (Logger::colorsSupported()) { ss << ansi::reset; @@ -341,7 +341,7 @@ void DoIPClient::displayVIResponseInformation() { ss << ansi::bold_green; } for (int i = 0; i < 6; i++) { - ss << std::hex << std::setfill('0') << std::setw(2) << +EIDResult[i] << std::dec; + ss << std::hex << std::setfill('0') << std::setw(2) << +m_eid[i] << std::dec; } if (Logger::colorsSupported()) { ss << ansi::reset; @@ -352,13 +352,13 @@ void DoIPClient::displayVIResponseInformation() { ss = std::ostringstream{}; ss << "GID: "; for (int i = 0; i < 6; i++) { - ss << std::hex << std::setfill('0') << std::setw(2) << +GIDResult[i] << std::dec; + ss << std::hex << std::setfill('0') << std::setw(2) << +m_gid[i] << std::dec; } LOG_DOIP_INFO(ss.str()); // output FurtherActionRequest ss = std::ostringstream{}; ss << "FurtherActionRequest: "; - ss << std::hex << std::setfill('0') << std::setw(2) << FurtherActionReqResult << std::dec; + ss << std::hex << std::setfill('0') << std::setw(2) << m_furtherActionReqResult << std::dec; LOG_DOIP_INFO(ss.str()); } From d2eaace2e02d3ba28a55486bd36300397e20c555 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 15:21:47 +0100 Subject: [PATCH 18/20] feat: Add accessors for VIn, EID, GID and logical address --- inc/DoIPMessage.h | 96 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/inc/DoIPMessage.h b/inc/DoIPMessage.h index 8271a58..046c0a7 100644 --- a/inc/DoIPMessage.h +++ b/inc/DoIPMessage.h @@ -250,6 +250,22 @@ class DoIPMessage { return m_data; } + /** + * @brief Check if the message has a Source Address field. + * + * @return Returns @c true in the case of success, @c false otherwise. + */ + bool hasSourceAddress() const { + auto payloadRef = getPayload(); + auto plType = getPayloadType(); + bool result = plType == DoIPPayloadType::DiagnosticMessage || + plType == DoIPPayloadType::RoutingActivationRequest || + plType == DoIPPayloadType::RoutingActivationResponse || + plType == DoIPPayloadType::AliveCheckResponse; + + return result && payloadRef.second >= 2; + } + /** * @brief Get the Source Address of the message (if message is a Diagnostic Message). * @@ -258,17 +274,21 @@ class DoIPMessage { std::optional getSourceAddress() const { auto payloadRef = getPayload(); // todo: Simplify - if (getPayloadType() == DoIPPayloadType::DiagnosticMessage && payloadRef.second >= 2) { + if (hasSourceAddress()) { return DoIPAddress(payloadRef.first, 0); } - if (getPayloadType() == DoIPPayloadType::RoutingActivationRequest && payloadRef.second >= 2) { - return DoIPAddress(payloadRef.first, 0); - } - if (getPayloadType() == DoIPPayloadType::RoutingActivationResponse && payloadRef.second >= 2) { - return DoIPAddress(payloadRef.first, 0); - } - if (getPayloadType() == DoIPPayloadType::AliveCheckResponse && payloadRef.second >= 2) { - return DoIPAddress(payloadRef.first, 0); + return std::nullopt; + } + + /** + * @brief Get the Logical Address of the message (if message is a Vehicle Identification Response). + * + * @return std::optional The logical address if present, std::nullopt otherwise + */ + std::optional getLogicalAddress() const { + auto payloadRef = getPayload(); + if (getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse && payloadRef.second >= 19) { + return DoIPAddress(payloadRef.first + 17); } return std::nullopt; } @@ -286,6 +306,58 @@ class DoIPMessage { return std::nullopt; } + /** + * @brief Get the vehicle identification number (VIN) if message is a Vehicle Identification Response. + * + * @return std::optional The VIN if present, std::nullopt otherwise + */ + std::optional getVin() const { + auto payloadRef = getPayload(); + if (getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse && payloadRef.second >= 17) { + return DoIpVin(payloadRef.first, 17); + } + return std::nullopt; + } + + /** + * @brief Get the entity id (EID) if message is a Vehicle Identification Response. + * + * @return std::optional The VIN if present, std::nullopt otherwise + */ + std::optional getEid() const { + auto payloadRef = getPayload(); + if (getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse && payloadRef.second >= 25) { + return DoIpEid(payloadRef.first + 19, 6); + } + return std::nullopt; + } + + /** + * @brief Get the group id (GID) if message is a Vehicle Identification Response. + * + * @return std::optional The VIN if present, std::nullopt otherwise + */ + std::optional getGid() const { + auto payloadRef = getPayload(); + if (getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse && payloadRef.second >= 31) { + return DoIpGid(payloadRef.first + 25, 6); + } + return std::nullopt; + } + + /** + * @brief Get the Further Action Request object if message is a Vehicle Identification Response. + * + * @return std::optional The Further Action Request if present, std::nullopt otherwise + */ + std::optional getFurtherActionRequest() const { + auto payloadRef = getPayload(); + if (getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse && payloadRef.second >= 31) { + return DoIPFurtherAction(payloadRef.first[31]); + } + return std::nullopt; + } + /** * @brief Checks if the message is valid. * @@ -454,10 +526,10 @@ inline DoIPMessage makeVehicleIdentificationRequest() { * @return DoIPMessage the vehicle identification response message */ inline DoIPMessage makeVehicleIdentificationResponse( - const DoIPVIN &vin, + const DoIpVin &vin, const DoIPAddress &logicalAddress, - const DoIPEID &entityType, - const DoIPGID &groupId, + const DoIpEid &entityType, + const DoIpGid &groupId, DoIPFurtherAction furtherAction = DoIPFurtherAction::NoFurtherAction, DoIPSyncStatus syncStatus = DoIPSyncStatus::GidVinSynchronized) { From 2147654686685dae46c2a4433ea8b1ca876e2c53 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 15:22:05 +0100 Subject: [PATCH 19/20] feat: Add stream operator --- inc/DoIPFurtherAction.h | 17 +++++++++++++++++ inc/DoIPIdentifiers.h | 31 ++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/inc/DoIPFurtherAction.h b/inc/DoIPFurtherAction.h index cb93ff5..04689bb 100644 --- a/inc/DoIPFurtherAction.h +++ b/inc/DoIPFurtherAction.h @@ -11,6 +11,23 @@ namespace doip { RoutingActivationForCentralSecurity = 0x10, // 0x11 to 0xFE: reserved for VM manufacturer specific use }; + + inline std::ostream &operator<<(std::ostream &os, const DoIPFurtherAction far) { + switch (far) { + case DoIPFurtherAction::NoFurtherAction: + os << "None"; + break; + case DoIPFurtherAction::RoutingActivationForCentralSecurity: + os << "Routing Activation for Central Security Required"; + break; + default: + os << "Reserved Further Action Code: 0x" << std::hex << static_cast(far) << std::dec; + break; + } + + return os; + } + } // namespace doip #endif /* DOIPFURTHERACTION_H */ diff --git a/inc/DoIPIdentifiers.h b/inc/DoIPIdentifiers.h index 905f90c..7db4ed4 100644 --- a/inc/DoIPIdentifiers.h +++ b/inc/DoIPIdentifiers.h @@ -346,17 +346,42 @@ inline const GenericFixedId GenericFixedId; +using DoIpVin = GenericFixedId<17, true, '0'>; /** * @brief Entity Identifier (EID) - 6 bytes for unique entity identification */ -using DoIPEID = GenericFixedId<6, false>; +using DoIpEid = GenericFixedId<6, false>; /** * @brief Group Identifier (GID) - 6 bytes for group identification */ -using DoIPGID = GenericFixedId<6, false>; +using DoIpGid = GenericFixedId<6, false>; + +/** + * @brief Stream output operator for DoIpVin, DoIpEid, and DoIpGid + * + * @param os the operation stream + * @param vin the DoIpVin to output + * @return std::ostream& the operation stream + */ +inline std::ostream &operator<<(std::ostream &os, const DoIpVin &vin) { + os << vin.toString(); + return os; +} + +/** + * @brief Stream output operator for DoIpEid/DoIpGid + * + * @param os the operation stream + * @param eid the DoIpEid/DoIpGid to output + * @return std::ostream& @ref {type} ["{type}"] // @return Returns @c true in the case of success, @c false otherwise. + */ +inline std::ostream &operator<<(std::ostream &os, const DoIpEid &eid) { + os << eid.toHexString(); + return os; +} + } // namespace doip From f2c152250c61120b3666a27a23ad7dd99f7eeab8 Mon Sep 17 00:00:00 2001 From: "Oliver Wieland (HC/XAG1)" Date: Tue, 9 Dec 2025 15:22:52 +0100 Subject: [PATCH 20/20] - refcator: Cleanup client code - refactor: Rename identifier types --- examples/exampleDoIPDiscover.cpp | 2 + examples/exampleDoIPServer.cpp | 6 +- inc/DoIPClient.h | 12 +- inc/DoIPServer.h | 16 +-- src/DoIPClient.cpp | 81 +++++------- src/DoIPServer.cpp | 12 +- test/DoIPMessage_Test.cpp | 43 +++++++ test/DoIPServer_Test.cpp | 6 +- test/Identifiers_Test.cpp | 188 ++++++++++++++-------------- test/VehicleIdentification_Test.cpp | 10 +- 10 files changed, 202 insertions(+), 174 deletions(-) diff --git a/examples/exampleDoIPDiscover.cpp b/examples/exampleDoIPDiscover.cpp index 76de9ab..c786657 100644 --- a/examples/exampleDoIPDiscover.cpp +++ b/examples/exampleDoIPDiscover.cpp @@ -56,6 +56,8 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } + client.printVehicleInformationResponse(); + // Send Vehicle Identification Request to configured address if (client.sendVehicleIdentificationRequest(serverAddress.c_str()) > 0) { LOG_DOIP_INFO("Vehicle Identification Request sent successfully"); diff --git a/examples/exampleDoIPServer.cpp b/examples/exampleDoIPServer.cpp index 15089bf..eb782d5 100644 --- a/examples/exampleDoIPServer.cpp +++ b/examples/exampleDoIPServer.cpp @@ -99,9 +99,9 @@ int main(int argc, char *argv[]) { cfg.loopback = useLoopback; cfg.daemonize = daemonize; // TODO: Use CLI11 or similar for argument parsing - if (!vin_str.empty()) cfg.vin = DoIPVIN(vin_str); - if (!eid_str.empty()) cfg.eid = DoIPEID(eid_str); - if (!gid_str.empty()) cfg.gid = DoIPGID(gid_str); + if (!vin_str.empty()) cfg.vin = DoIpVin(vin_str); + if (!eid_str.empty()) cfg.eid = DoIpEid(eid_str); + if (!gid_str.empty()) cfg.gid = DoIpGid(gid_str); if (!logical_addr_str.empty()) { // parse hex (0x...) or decimal try { diff --git a/inc/DoIPClient.h b/inc/DoIPClient.h index dd38793..ec651ab 100644 --- a/inc/DoIPClient.h +++ b/inc/DoIPClient.h @@ -49,7 +49,7 @@ class DoIPClient { */ ssize_t sendAliveCheckResponse(); void setSourceAddress(const DoIPAddress &address); - void displayVIResponseInformation(); + void printVehicleInformationResponse(); void closeTcpConnection(); void closeUdpConnection(); void reconnectServer(); @@ -64,13 +64,13 @@ class DoIPClient { struct sockaddr_in m_serverAddress, m_clientAddress, m_announcementAddress; DoIPAddress m_sourceAddress = DoIPAddress(0xE000); - uint8_t m_vin[17] = {0}; + DoIpVin m_vin{0}; DoIPAddress m_logicalAddress = DoIPAddress::ZeroAddress; - uint8_t m_eid[6] = {0}; - uint8_t m_gid[6] = {0}; - uint8_t m_furtherActionReqResult = 0x00; + DoIpEid m_eid{0}; + DoIpGid m_gid{0}; + DoIPFurtherAction m_furtherActionReqResult = DoIPFurtherAction::NoFurtherAction; - void parseVIResponseInformation(const uint8_t *data); + void parseVehicleIdentificationResponse(const DoIPMessage& msg); int emptyMessageCounter = 0; }; diff --git a/inc/DoIPServer.h b/inc/DoIPServer.h index fc7aa50..d7985ff 100644 --- a/inc/DoIPServer.h +++ b/inc/DoIPServer.h @@ -33,11 +33,11 @@ namespace doip { */ struct ServerConfig { // EID and GID as fixed identifiers (6 bytes). Default: zeros. - DoIPEID eid = DoIPEID::Zero; - DoIPGID gid = DoIPGID::Zero; + DoIpEid eid = DoIpEid::Zero; + DoIpGid gid = DoIpGid::Zero; // VIN as fixed identifier (17 bytes). Default: zeros. - DoIPVIN vin = DoIPVIN::Zero; + DoIpVin vin = DoIpVin::Zero; // Logical/server address (default 0x0E00) DoIPAddress logicalAddress = DoIPAddress(0x0028); @@ -159,15 +159,15 @@ class DoIPServer { */ void setVin(const std::string &VINString); /** - * @brief Set VIN from a `DoIPVIN` instance. + * @brief Set VIN from a `DoIpVin` instance. * @param vin VIN value. */ - void setVin(const DoIPVIN &vin); + void setVin(const DoIpVin &vin); /** * @brief Get current VIN. * @return Reference to configured VIN. */ - const DoIPVIN &getVin() const { return m_config.vin; } + const DoIpVin &getVin() const { return m_config.vin; } /** * @brief Set EID value. @@ -178,7 +178,7 @@ class DoIPServer { * @brief Get current EID. * @return Reference to configured EID. */ - const DoIPEID &getEid() const { return m_config.eid; } + const DoIpEid &getEid() const { return m_config.eid; } /** * @brief Set GID value. @@ -189,7 +189,7 @@ class DoIPServer { * @brief Get current GID. * @return Reference to configured GID. */ - const DoIPGID &getGid() const { return m_config.gid; } + const DoIpGid &getGid() const { return m_config.gid; } /** * @brief Get current further action requirement status. diff --git a/src/DoIPClient.cpp b/src/DoIPClient.cpp index c42b78b..743b32f 100644 --- a/src/DoIPClient.cpp +++ b/src/DoIPClient.cpp @@ -222,12 +222,10 @@ bool DoIPClient::receiveVehicleAnnouncement() { } DoIPMessage msg = optMsg.value(); - LOG_UDP_INFO("Vehicle Announcement received: {}", fmt::streamed(msg)); - // Parse and display the announcement information if (msg.getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse) { - parseVIResponseInformation(msg.data()); - displayVIResponseInformation(); + LOG_UDP_INFO("Vehicle Announcement received: {}", fmt::streamed(msg)); + parseVehicleIdentificationResponse(msg); return true; } return false; @@ -283,55 +281,41 @@ int DoIPClient::getConnected() { return m_connected; } -void DoIPClient::parseVIResponseInformation(const uint8_t *data) { - - // VIN - int j = 0; - for (int i = 8; i <= 24; i++) { - m_vin[j] = data[i]; - j++; - } - - // Logical Adress - m_logicalAddress.update(data, 25); - - // EID - j = 0; - for (int i = 27; i <= 32; i++) { - m_eid[j] = data[i]; - j++; - } +void DoIPClient::parseVehicleIdentificationResponse(const DoIPMessage &msg) { + auto optVin = msg.getVin(); + auto optEid = msg.getEid(); + auto optGid = msg.getGid(); + auto optLogicalAddress = msg.getLogicalAddress(); + auto optFurtherAction = msg.getFurtherActionRequest(); - // GID - j = 0; - for (int i = 33; i <= 38; i++) { - m_gid[j] = data[i]; - j++; + if (!optVin || !optEid || !optGid || !optLogicalAddress || !optFurtherAction) { + LOG_DOIP_WARN("Incomplete Vehicle Identification Response received: Missing VIN, EID, GID, Logical Address or Further Action Request"); } - // FurtherActionRequest - m_furtherActionReqResult = data[39]; + m_vin = optVin.value(); + m_eid = optEid.value(); + m_gid = optGid.value(); + m_logicalAddress = optLogicalAddress.value(); + m_furtherActionReqResult = optFurtherAction.value(); } -void DoIPClient::displayVIResponseInformation() { +void DoIPClient::printVehicleInformationResponse() { std::ostringstream ss; // output VIN - ss << "VIN: "; + ss << "VIN: " ; if (Logger::colorsSupported()) { ss << ansi::bold_green; } - for (int i = 0; i < 17; i++) { - ss << m_vin[i]; - } - if (Logger::colorsSupported()) { - ss << ansi::reset; - } + ss << m_vin << ansi::reset ; LOG_DOIP_INFO(ss.str()); // output LogicalAddress ss = std::ostringstream{}; - ss << "LogicalAddress: "; - ss << m_logicalAddress; + ss << "LA : "; + if (Logger::colorsSupported()) { + ss << ansi::bold_green; + } + ss << m_logicalAddress << ansi::reset; LOG_DOIP_INFO(ss.str()); // output EID @@ -340,25 +324,24 @@ void DoIPClient::displayVIResponseInformation() { if (Logger::colorsSupported()) { ss << ansi::bold_green; } - for (int i = 0; i < 6; i++) { - ss << std::hex << std::setfill('0') << std::setw(2) << +m_eid[i] << std::dec; - } - if (Logger::colorsSupported()) { - ss << ansi::reset; - } + ss << m_eid << ansi::reset; LOG_DOIP_INFO(ss.str()); // output GID ss = std::ostringstream{}; ss << "GID: "; - for (int i = 0; i < 6; i++) { - ss << std::hex << std::setfill('0') << std::setw(2) << +m_gid[i] << std::dec; + if (Logger::colorsSupported()) { + ss << ansi::bold_green; } + ss << m_gid << ansi::reset; LOG_DOIP_INFO(ss.str()); // output FurtherActionRequest ss = std::ostringstream{}; - ss << "FurtherActionRequest: "; - ss << std::hex << std::setfill('0') << std::setw(2) << m_furtherActionReqResult << std::dec; + ss << "FAR: "; + if (Logger::colorsSupported()) { + ss << ansi::bold_green; + } + ss << m_furtherActionReqResult << ansi::reset; LOG_DOIP_INFO(ss.str()); } diff --git a/src/DoIPServer.cpp b/src/DoIPServer.cpp index a33b800..a1470f7 100644 --- a/src/DoIPServer.cpp +++ b/src/DoIPServer.cpp @@ -231,20 +231,20 @@ bool DoIPServer::setDefaultEid() { MacAddress mac = {0}; if (!getFirstMacAddress(mac)) { LOG_DOIP_ERROR("Failed to get MAC address, using default EID"); - m_config.eid = DoIPEID::Zero; + m_config.eid = DoIpEid::Zero; return false; } // Set EID based on MAC address (last 6 bytes) - m_config.eid = DoIPEID(mac.data(), m_config.eid.ID_LENGTH); + m_config.eid = DoIpEid(mac.data(), m_config.eid.ID_LENGTH); return true; } void DoIPServer::setVin(const std::string &VINString) { - m_config.vin = DoIPVIN(VINString); + m_config.vin = DoIpVin(VINString); } -void DoIPServer::setVin(const DoIPVIN &vin) { +void DoIPServer::setVin(const DoIpVin &vin) { m_config.vin = vin; } @@ -253,11 +253,11 @@ void DoIPServer::setLogicalGatewayAddress(unsigned short logicalAddress) { } void DoIPServer::setEid(const uint64_t inputEID) { - m_config.eid = DoIPEID(inputEID); + m_config.eid = DoIpEid(inputEID); } void DoIPServer::setGid(const uint64_t inputGID) { - m_config.gid = DoIPGID(inputGID); + m_config.gid = DoIpGid(inputGID); } void DoIPServer::setFurtherActionRequired(DoIPFurtherAction furtherActionRequired) { diff --git a/test/DoIPMessage_Test.cpp b/test/DoIPMessage_Test.cpp index d861995..726af2b 100644 --- a/test/DoIPMessage_Test.cpp +++ b/test/DoIPMessage_Test.cpp @@ -137,6 +137,49 @@ TEST_SUITE("DoIPMessage") { } } + TEST_CASE("Message factory - makeVehicleIdentificationRequest") { + DoIPMessage msg = message::makeVehicleIdentificationRequest(); + + CHECK(msg.getPayloadSize() == 0); + CHECK(msg.getMessageSize() == 8); + CHECK(msg.getPayloadType() == DoIPPayloadType::VehicleIdentificationRequest); + } + + TEST_CASE("Message factory - makeVehicleIdentificationResponse") { + DoIpVin vin = DoIpVin("1HGCM82633A123456"); + DoIPAddress logicalAddress = DoIPAddress(1234); + DoIpEid entityType = DoIpEid("EID123"); + DoIpGid groupId = DoIpGid("GID456"); + DoIPFurtherAction furtherAction = DoIPFurtherAction::RoutingActivationForCentralSecurity; + DoIPMessage msg = message::makeVehicleIdentificationResponse(vin, logicalAddress, entityType, groupId, furtherAction); + + INFO(msg, "\n", logicalAddress); + CHECK(msg.getPayloadSize() >= 31); // 17 + 2 + 6 + 6 + 1 (+ 1 optional byte for sync status) + CHECK(msg.getMessageSize() >= 40); + CHECK(msg.getPayloadType() == DoIPPayloadType::VehicleIdentificationResponse); + + // Example: + // VIN: 45.58.41.4D.50.4C.45.53.45.52.56.45.52.30.30.30.30.00. + // LA: 28.00. + // EID: 00.00.00.00.00.00 + // GID: 00.00.00.00.00.00 + // FAR: 00 + // (sync status: optional) + + + CHECK(msg.getVin().has_value()); + CHECK(msg.getVin().value().toString() == vin.toString()); + CHECK(msg.getLogicalAddress().has_value()); + CHECK(msg.getLogicalAddress().value().toUint16() == logicalAddress.toUint16()); + CHECK(msg.getEid().has_value()); + CHECK(msg.getEid().value().toString() == entityType.toString()); + CHECK(msg.getGid().has_value()); + CHECK(msg.getGid().value().toString() == groupId.toString()); + CHECK(msg.getFurtherActionRequest().has_value()); + CHECK(msg.getFurtherActionRequest().value() == furtherAction); + } + + TEST_CASE("Init from raw bytes - invalid args") { const uint8_t short_msg[] = {PROTOCOL_VERSION, PROTOCOL_VERSION_INV, 0x80, 0x01}; const uint8_t inv_protocol[] = {PROTOCOL_VERSION - 1, PROTOCOL_VERSION_INV + 1, 0x80, 0x01}; diff --git a/test/DoIPServer_Test.cpp b/test/DoIPServer_Test.cpp index 1a27570..0111c29 100644 --- a/test/DoIPServer_Test.cpp +++ b/test/DoIPServer_Test.cpp @@ -29,7 +29,7 @@ TEST_SUITE("DoIPServer Tests") { TEST_CASE_FIXTURE(DoIPServerFixture, "Set VIN Test") { std::string testVIN = "TESTVIN1234567890"; server.setVin(testVIN); - DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); + DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIpGid::Zero, DoIPFurtherAction::NoFurtherAction); ByteArrayRef payload = msg.getPayload(); // Check that the VIN in the payload matches the set VIN @@ -41,7 +41,7 @@ TEST_SUITE("DoIPServer Tests") { TEST_CASE_FIXTURE(DoIPServerFixture, "Set EID Test") { uint64_t testEID = 0x123456789ABC; server.setEid(testEID); - DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); + DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIpGid::Zero, DoIPFurtherAction::NoFurtherAction); ByteArrayRef payload = msg.getPayload(); // Check that the EID in the payload matches the set EID @@ -54,7 +54,7 @@ TEST_SUITE("DoIPServer Tests") { bool result = server.setDefaultEid(); CHECK(result == true); - DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIPGID::Zero, DoIPFurtherAction::NoFurtherAction); + DoIPMessage msg = message::makeVehicleIdentificationResponse(server.getVin(), DoIPAddress::ZeroAddress, server.getEid(), DoIpGid::Zero, DoIPFurtherAction::NoFurtherAction); ByteArrayRef payload = msg.getPayload(); std::cerr << "EID set to: " << server.getEid().toHexString() << '\n'; diff --git a/test/Identifiers_Test.cpp b/test/Identifiers_Test.cpp index cc57d1e..8c3fbba 100644 --- a/test/Identifiers_Test.cpp +++ b/test/Identifiers_Test.cpp @@ -9,7 +9,7 @@ using namespace doip; TEST_SUITE("GenericFixedId") { TEST_CASE("Default constructor creates empty VIN") { - DoIPVIN vin; + DoIpVin vin; // Verify all bytes are '0' for (size_t i = 0; i < 17; ++i) { @@ -19,7 +19,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from string - exact length") { const std::string test_vin = "1HGBH41JXMN109186"; - DoIPVIN vin(test_vin); + DoIpVin vin(test_vin); CHECK_FALSE(vin.isEmpty()); CHECK(vin.toString() == test_vin); @@ -32,7 +32,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from string - shorter than 17 characters") { const std::string test_vin = "ABC12300000000000"; - DoIPVIN vin(test_vin); + DoIpVin vin(test_vin); CHECK_FALSE(vin.isEmpty()); CHECK(vin.toString() == test_vin); @@ -47,7 +47,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from string - longer than 17 characters") { const std::string test_vin = "1HGBH41JXMN109186TOOLONGSTRING"; - DoIPVIN vin(test_vin); + DoIpVin vin(test_vin); CHECK_FALSE(vin.isEmpty()); CHECK(vin.toString() == "1HGBH41JXMN109186"); @@ -59,16 +59,16 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("Construction from empty string") { - DoIPVIN vin(""); + DoIpVin vin(""); CHECK(vin.isEmpty()); CHECK(vin.toString() == "00000000000000000"); - CHECK_BYTE_ARRAY_REF_EQ(vin.asByteArray(), DoIPVIN::Zero.asByteArray()); + CHECK_BYTE_ARRAY_REF_EQ(vin.asByteArray(), DoIpVin::Zero.asByteArray()); } TEST_CASE("Construction from C-style string") { const char* test_vin = "WVWZZZ1JZYW123456"; - DoIPVIN vin(test_vin); + DoIpVin vin(test_vin); CHECK(vin.toString() == test_vin); CHECK(vin[0] == 'W'); @@ -77,15 +77,15 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from nullptr C-style string") { const char* null_ptr = nullptr; - DoIPVIN vin(null_ptr); + DoIpVin vin(null_ptr); CHECK(vin.isEmpty()); - CHECK_BYTE_ARRAY_REF_EQ(vin.asByteArray(), DoIPVIN::Zero.asByteArray()); + CHECK_BYTE_ARRAY_REF_EQ(vin.asByteArray(), DoIpVin::Zero.asByteArray()); } TEST_CASE("Construction from byte sequence") { const uint8_t bytes[] = {'T', 'E', 'S', 'T', 'V', 'I', 'N', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; - DoIPVIN vin(bytes, sizeof(bytes)); + DoIpVin vin(bytes, sizeof(bytes)); CHECK(vin.toString() == "TESTVIN1234567890"); CHECK(vin[0] == 'T'); @@ -94,7 +94,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from byte sequence - shorter") { const uint8_t bytes[] = {'S', 'H', 'O', 'R', 'T', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; - DoIPVIN vin(bytes, sizeof(bytes)); + DoIpVin vin(bytes, sizeof(bytes)); CHECK(vin.toString() == "SHORT000000000000"); CHECK(vin[0] == 'S'); @@ -105,22 +105,22 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from byte sequence - longer") { const uint8_t bytes[] = {'V', 'E', 'R', 'Y', 'L', 'O', 'N', 'G', 'V', 'I', 'N', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; - DoIPVIN vin(bytes, sizeof(bytes)); + DoIpVin vin(bytes, sizeof(bytes)); CHECK(vin.toString() == "VERYLONGVIN123456"); CHECK(vin[16] == '6'); } TEST_CASE("Construction from null byte sequence") { - DoIPVIN vin(nullptr, 10); + DoIpVin vin(nullptr, 10); CHECK(vin.isEmpty()); - CHECK(vin == DoIPVIN::Zero); + CHECK(vin == DoIpVin::Zero); } TEST_CASE("Construction from ByteArray - exact length") { ByteArray bytes = {'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'}; - DoIPVIN vin(bytes); + DoIpVin vin(bytes); CHECK(vin.toString() == "123456789ABCDEFGH"); CHECK(vin[0] == '1'); @@ -129,7 +129,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from ByteArray - shorter") { ByteArray bytes = {'X', 'Y', 'Z', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'}; - DoIPVIN vin(bytes); + DoIpVin vin(bytes); CHECK(vin.toString() == "XYZ00000000000000"); CHECK(vin[0] == 'X'); @@ -139,7 +139,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from ByteArray - longer") { ByteArray bytes = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T'}; - DoIPVIN vin(bytes); + DoIpVin vin(bytes); CHECK(vin.toString() == "ABCDEFGHIJKLMNOPQ"); CHECK(vin[16] == 'Q'); @@ -147,23 +147,23 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Construction from empty ByteArray") { ByteArray bytes; - DoIPVIN vin(bytes); + DoIpVin vin(bytes); CHECK(vin.isEmpty()); - CHECK_BYTE_ARRAY_REF_EQ(vin.asByteArray(), DoIPVIN::Zero.asByteArray()); + CHECK_BYTE_ARRAY_REF_EQ(vin.asByteArray(), DoIpVin::Zero.asByteArray()); } TEST_CASE("Copy constructor") { - DoIPVIN vin1("ORIGINALVIN123456"); - DoIPVIN vin2(vin1); + DoIpVin vin1("ORIGINALVIN123456"); + DoIpVin vin2(vin1); CHECK(vin1 == vin2); CHECK(vin2.toString() == "ORIGINALVIN123456"); } TEST_CASE("Move assignment") { - DoIPVIN vin1("MOVEASGN123456789"); - DoIPVIN vin2; + DoIpVin vin1("MOVEASGN123456789"); + DoIpVin vin2; vin2 = std::move(vin1); @@ -172,23 +172,23 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("toString method") { SUBCASE("Full VIN") { - DoIPVIN vin("FULLVIN1234567890"); + DoIpVin vin("FULLVIN1234567890"); CHECK(vin.toString() == "FULLVIN1234567890"); } SUBCASE("Partial VIN with padding") { - DoIPVIN vin("PART"); + DoIpVin vin("PART"); CHECK(vin.toString() == "PART0000000000000"); } SUBCASE("Empty VIN") { - DoIPVIN vin; + DoIpVin vin; CHECK(vin.toString() == "00000000000000000"); } } TEST_CASE("getArray method") { - DoIPVIN vin("ARRAYTEST12345678"); + DoIpVin vin("ARRAYTEST12345678"); const auto& arr = vin.getArray(); CHECK(arr.size() == 17); @@ -197,7 +197,7 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("data method") { - DoIPVIN vin("DATATEST123456789"); + DoIpVin vin("DATATEST123456789"); const uint8_t* ptr = vin.data(); CHECK(ptr != nullptr); @@ -206,9 +206,9 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("size method") { - DoIPVIN vin1; - DoIPVIN vin2("SHORT"); - DoIPVIN vin3("EXACTSEVENTEENVIN"); + DoIpVin vin1; + DoIpVin vin2("SHORT"); + DoIpVin vin3("EXACTSEVENTEENVIN"); CHECK(vin1.size() == 17); CHECK(vin2.size() == 17); @@ -217,58 +217,58 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("isEmpty method") { SUBCASE("Empty VIN") { - DoIPVIN vin; + DoIpVin vin; CHECK(vin.isEmpty()); } SUBCASE("Empty string") { - DoIPVIN vin(""); + DoIpVin vin(""); CHECK(vin.isEmpty()); } SUBCASE("Zero instance") { - CHECK(DoIPVIN::Zero.isEmpty()); + CHECK(DoIpVin::Zero.isEmpty()); } SUBCASE("Non-empty VIN") { - DoIPVIN vin("X"); + DoIpVin vin("X"); CHECK_FALSE(vin.isEmpty()); } SUBCASE("Full VIN") { - DoIPVIN vin("FULLVIN1234567890"); + DoIpVin vin("FULLVIN1234567890"); CHECK_FALSE(vin.isEmpty()); } } TEST_CASE("Equality operator") { - DoIPVIN vin1("SAMEVIN1234567890"); - DoIPVIN vin2("SAMEVIN1234567890"); - DoIPVIN vin3("DIFFVIN1234567890"); + DoIpVin vin1("SAMEVIN1234567890"); + DoIpVin vin2("SAMEVIN1234567890"); + DoIpVin vin3("DIFFVIN1234567890"); CHECK(vin1 == vin2); CHECK_FALSE(vin1 == vin3); - DoIPVIN vin4; - DoIPVIN vin5; + DoIpVin vin4; + DoIpVin vin5; CHECK(vin4 == vin5); - CHECK_BYTE_ARRAY_REF_EQ(vin4.asByteArray(), DoIPVIN::Zero.asByteArray()); + CHECK_BYTE_ARRAY_REF_EQ(vin4.asByteArray(), DoIpVin::Zero.asByteArray()); } TEST_CASE("Inequality operator") { - DoIPVIN vin1("VIN1_12345678901"); - DoIPVIN vin2("VIN2_12345678901"); - DoIPVIN vin3("VIN1_12345678901"); + DoIpVin vin1("VIN1_12345678901"); + DoIpVin vin2("VIN2_12345678901"); + DoIpVin vin3("VIN1_12345678901"); CHECK(vin1 != vin2); CHECK_FALSE(vin1 != vin3); - DoIPVIN vin4; + DoIpVin vin4; CHECK(vin1 != vin4); } TEST_CASE("Array subscript operator") { - DoIPVIN vin("SUBSCRIPT12345678"); + DoIpVin vin("SUBSCRIPT12345678"); CHECK(vin[0] == 'S'); CHECK(vin[1] == 'U'); @@ -277,7 +277,7 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("Array subscript with padding") { - DoIPVIN vin("PAD"); + DoIpVin vin("PAD"); CHECK(vin[0] == 'P'); CHECK(vin[1] == 'A'); @@ -287,7 +287,7 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("VIN with special characters") { - DoIPVIN vin("VIN-WITH_SPEC.IAL"); + DoIpVin vin("VIN-WITH_SPEC.IAL"); CHECK(vin.toString() == "VIN-WITH_SPEC.IAL"); CHECK(vin[3] == '-'); @@ -296,7 +296,7 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("VIN with numeric characters") { - DoIPVIN vin("12345678901234567"); + DoIpVin vin("12345678901234567"); CHECK(vin.toString() == "12345678901234567"); CHECK(vin[0] == '1'); @@ -304,39 +304,39 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("VIN with lowercase characters") { - DoIPVIN vin("lowercase12345678"); + DoIpVin vin("lowercase12345678"); CHECK(vin.toString() == "lowercase12345678"); CHECK(vin[0] == 'l'); } TEST_CASE("VIN with mixed case") { - DoIPVIN vin("MiXeDcAsE12345678"); + DoIpVin vin("MiXeDcAsE12345678"); CHECK(vin.toString() == "MiXeDcAsE12345678"); } TEST_CASE("Real-world VIN examples") { SUBCASE("Honda VIN") { - DoIPVIN vin("1HGBH41JXMN109186"); + DoIpVin vin("1HGBH41JXMN109186"); CHECK(vin.toString() == "1HGBH41JXMN109186"); CHECK_FALSE(vin.isEmpty()); } SUBCASE("Volkswagen VIN") { - DoIPVIN vin("WVWZZZ1JZYW123456"); + DoIpVin vin("WVWZZZ1JZYW123456"); CHECK(vin.toString() == "WVWZZZ1JZYW123456"); CHECK_FALSE(vin.isEmpty()); } SUBCASE("BMW VIN") { - DoIPVIN vin("WBA3B1G59DNP26082"); + DoIpVin vin("WBA3B1G59DNP26082"); CHECK(vin.toString() == "WBA3B1G59DNP26082"); CHECK_FALSE(vin.isEmpty()); } SUBCASE("Mercedes VIN") { - DoIPVIN vin("WDDUG8CB9DA123456"); + DoIpVin vin("WDDUG8CB9DA123456"); CHECK(vin.toString() == "WDDUG8CB9DA123456"); CHECK_FALSE(vin.isEmpty()); } @@ -345,9 +345,9 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("VIN conversion round-trip") { const std::string original = "ROUNDTRIP12345678"; - DoIPVIN vin1(original); + DoIpVin vin1(original); std::string str = vin1.toString(); - DoIPVIN vin2(str); + DoIpVin vin2(str); CHECK(vin1 == vin2); CHECK(vin2.toString() == original); @@ -356,9 +356,9 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("ByteArray conversion round-trip") { const std::string original = "BYTEARRAYTRIP1234"; - DoIPVIN vin1(original); + DoIpVin vin1(original); ByteArrayRef bytes = vin1.asByteArray(); - DoIPVIN vin2(bytes.first, bytes.second); + DoIpVin vin2(bytes.first, bytes.second); CHECK(vin1 == vin2); CHECK(bytes.second == 17); @@ -366,7 +366,7 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("VIN with null bytes in middle") { uint8_t data[17] = {'V', 'I', 'N', 0, 'N', 'U', 'L', 'L', 0, 'B', 'Y', 'T', 'E', 'S', '1', '2', '3'}; - DoIPVIN vin(data, 17); + DoIpVin vin(data, 17); // toString should stop at first null byte CHECK(vin.toString() == "VIN"); @@ -377,7 +377,7 @@ TEST_SUITE("GenericFixedId") { } TEST_CASE("Constant correctness") { - const DoIPVIN vin("CONSTVIN123456789"); + const DoIpVin vin("CONSTVIN123456789"); CHECK(vin.toString() == "CONSTVIN123456789"); CHECK(vin.size() == 17); @@ -391,28 +391,28 @@ TEST_SUITE("GenericFixedId") { CHECK(ptr[0] == 'C'); } - TEST_CASE("DoIPEID - Entity Identifier (6 bytes)") { + TEST_CASE("DoIpEid - Entity Identifier (6 bytes)") { SUBCASE("Default constructor") { - DoIPEID eid; + DoIpEid eid; CHECK(eid.isEmpty()); CHECK(eid.size() == 6); CHECK(eid.toString().empty()); } SUBCASE("Static Zero instance") { - CHECK(DoIPEID::Zero.isEmpty()); - CHECK(DoIPEID::Zero.size() == 6); + CHECK(DoIpEid::Zero.isEmpty()); + CHECK(DoIpEid::Zero.size() == 6); } SUBCASE("Construction from string - exact length") { - DoIPEID eid("ABC123"); + DoIpEid eid("ABC123"); CHECK(eid.toString() == "ABC123"); CHECK(eid.size() == 6); CHECK_FALSE(eid.isEmpty()); } SUBCASE("Construction from string - shorter") { - DoIPEID eid("EID"); + DoIpEid eid("EID"); CHECK(eid.toString() == "EID"); CHECK(eid.size() == 6); CHECK(eid[0] == 'E'); @@ -422,14 +422,14 @@ TEST_SUITE("GenericFixedId") { } SUBCASE("Construction from string - longer") { - DoIPEID eid("TOOLONGEID"); + DoIpEid eid("TOOLONGEID"); CHECK(eid.toString() == "TOOLON"); CHECK(eid.size() == 6); } SUBCASE("Construction from byte array") { const uint8_t bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06}; - DoIPEID eid(bytes, sizeof(bytes)); + DoIpEid eid(bytes, sizeof(bytes)); CHECK(eid.size() == 6); CHECK(eid[0] == 0x01); CHECK(eid[5] == 0x06); @@ -438,16 +438,16 @@ TEST_SUITE("GenericFixedId") { SUBCASE("Construction from ByteArray") { ByteArray bytes = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; - DoIPEID eid(bytes); + DoIpEid eid(bytes); CHECK(eid.size() == 6); CHECK(eid[0] == 0xAA); CHECK(eid[5] == 0xFF); } SUBCASE("Equality and comparison") { - DoIPEID eid1("EID001"); - DoIPEID eid2("EID001"); - DoIPEID eid3("EID002"); + DoIpEid eid1("EID001"); + DoIpEid eid2("EID001"); + DoIpEid eid3("EID002"); CHECK(eid1 == eid2); CHECK(eid1 != eid3); @@ -455,7 +455,7 @@ TEST_SUITE("GenericFixedId") { } SUBCASE("asByteArray method") { - DoIPEID eid("TEST12"); + DoIpEid eid("TEST12"); ByteArrayRef result = eid.asByteArray(); CHECK(result.second == 6); CHECK(result.first[0] == 'T'); @@ -463,28 +463,28 @@ TEST_SUITE("GenericFixedId") { } } - TEST_CASE("DoIPGID - Group Identifier (6 bytes)") { + TEST_CASE("DoIpGid - Group Identifier (6 bytes)") { SUBCASE("Default constructor") { - DoIPGID gid; + DoIpGid gid; CHECK(gid.isEmpty()); CHECK(gid.size() == 6); CHECK(gid.toString().empty()); } SUBCASE("Static Zero instance") { - CHECK(DoIPGID::Zero.isEmpty()); - CHECK(DoIPGID::Zero.size() == 6); + CHECK(DoIpGid::Zero.isEmpty()); + CHECK(DoIpGid::Zero.size() == 6); } SUBCASE("Construction from string - exact length") { - DoIPGID gid("GRP001"); + DoIpGid gid("GRP001"); CHECK(gid.toString() == "GRP001"); CHECK(gid.size() == 6); CHECK_FALSE(gid.isEmpty()); } SUBCASE("Construction from string - shorter") { - DoIPGID gid("GID"); + DoIpGid gid("GID"); CHECK(gid.toString() == "GID"); CHECK(gid.size() == 6); CHECK(gid[0] == 'G'); @@ -494,14 +494,14 @@ TEST_SUITE("GenericFixedId") { } SUBCASE("Construction from string - longer") { - DoIPGID gid("TOOLONGGID"); + DoIpGid gid("TOOLONGGID"); CHECK(gid.toString() == "TOOLON"); CHECK(gid.size() == 6); } SUBCASE("Construction from uint32_t - longer") { uint32_t long_value = 0x544F4F4C; // ASCII for "TOOL" - DoIPGID gid(long_value); + DoIpGid gid(long_value); CHECK(gid.toString() == "TOOL"); CHECK(gid.size() == 6); } @@ -509,14 +509,14 @@ TEST_SUITE("GenericFixedId") { SUBCASE("Construction from uint64_t - longer") { uint64_t long_value = 0x544F4F4C4F4E47; // ASCII for "TOOLONG" - DoIPGID gid(long_value); + DoIpGid gid(long_value); CHECK(gid.toString() == "OOLONG"); // CHECK(gid.size() == 6); } SUBCASE("Construction from byte array") { const uint8_t bytes[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60}; - DoIPGID gid(bytes, sizeof(bytes)); + DoIpGid gid(bytes, sizeof(bytes)); CHECK(gid.size() == 6); CHECK(gid[0] == 0x10); CHECK(gid[5] == 0x60); @@ -525,16 +525,16 @@ TEST_SUITE("GenericFixedId") { SUBCASE("Construction from ByteArray") { ByteArray bytes = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; - DoIPGID gid(bytes); + DoIpGid gid(bytes); CHECK(gid.size() == 6); CHECK(gid[0] == 0x11); CHECK(gid[5] == 0x66); } SUBCASE("Equality and comparison") { - DoIPGID gid1("GROUP1"); - DoIPGID gid2("GROUP1"); - DoIPGID gid3("GROUP2"); + DoIpGid gid1("GROUP1"); + DoIpGid gid2("GROUP1"); + DoIpGid gid3("GROUP2"); CHECK(gid1 == gid2); CHECK(gid1 != gid3); @@ -542,7 +542,7 @@ TEST_SUITE("GenericFixedId") { } SUBCASE("asByteArray method") { - DoIPGID gid("MYGRP1"); + DoIpGid gid("MYGRP1"); ByteArrayRef result = gid.asByteArray(); CHECK(result.second == 6); CHECK(result.first[0] == 'M'); @@ -552,16 +552,16 @@ TEST_SUITE("GenericFixedId") { TEST_CASE("Different identifier types are independent") { // Even though EID and GID have the same length, they're different types - DoIPEID eid("ABC123"); - DoIPGID gid("ABC123"); + DoIpEid eid("ABC123"); + DoIpGid gid("ABC123"); // They should have the same content but be different types CHECK(eid.toString() == gid.toString()); CHECK(eid.size() == gid.size()); // Verify they're truly independent instances - DoIPEID eid2(eid); - DoIPGID gid2(gid); + DoIpEid eid2(eid); + DoIpGid gid2(gid); CHECK(eid == eid2); CHECK(gid == gid2); } diff --git a/test/VehicleIdentification_Test.cpp b/test/VehicleIdentification_Test.cpp index ffd98b7..53a898d 100644 --- a/test/VehicleIdentification_Test.cpp +++ b/test/VehicleIdentification_Test.cpp @@ -10,11 +10,11 @@ using namespace std; TEST_SUITE("VehicleIdentificationHandler") { struct VehicleIdentificationHandlerFixture { - DoIPVIN matchingVIN = DoIPVIN("MatchingVin_12345"); - DoIPVIN shortVIN = DoIPVIN("shortVin"); - DoIPVIN shortVINPadded = DoIPVIN("shortVin000000000"); - DoIPEID EID = DoIPEID::Zero; - DoIPGID GID = DoIPGID::Zero; + DoIpVin matchingVIN = DoIpVin("MatchingVin_12345"); + DoIpVin shortVIN = DoIpVin("shortVin"); + DoIpVin shortVINPadded = DoIpVin("shortVin000000000"); + DoIpEid EID = DoIpEid::Zero; + DoIpGid GID = DoIpGid::Zero; DoIPFurtherAction furtherActionRequired = DoIPFurtherAction::NoFurtherAction; DoIPFurtherAction far_cs = DoIPFurtherAction::RoutingActivationForCentralSecurity;