libdoip  0.1.0
DoIP (Diagnostics over Internet Protocol) ISO 13400 C++17 Library
DoIPDefaultConnection.cpp
Go to the documentation of this file.
2 #include "Logger.h"
3 
4 #include <execinfo.h>
5 #include <iostream>
6 
7 namespace doip {
8 
10  : m_serverModel(std::move(model)),
11  STATE_DESCRIPTORS{
12  StateDescriptor(
15  [this](std::optional<DoIPMessage> msg) { this->handleSocketInitialized(DoIPServerEvent{}, msg); }),
16  StateDescriptor(
19  [this](std::optional<DoIPMessage> msg) { this->handleWaitRoutingActivation(DoIPServerEvent{}, msg); },
21  StateDescriptor(
24  [this](std::optional<DoIPMessage> msg) { this->handleRoutingActivated(DoIPServerEvent{}, msg); },
26  [this]() noexcept { m_aliveCheckRetry = 0; }),
27  StateDescriptor(
30  [this](std::optional<DoIPMessage> msg) { this->handleWaitAliveCheckResponse(DoIPServerEvent{}, msg); },
32  [this]() { ++m_aliveCheckRetry; LOG_DOIP_WARN("Alive check #{}/{}", m_aliveCheckRetry, m_aliveCheckRetryCount); }),
33  StateDescriptor(
36  [this](std::optional<DoIPMessage> msg) { this->handleWaitDownstreamResponse(DoIPServerEvent{}, msg); },
38  nullptr,
39  nullptr,
40  2s),
41  StateDescriptor(
44  [this](std::optional<DoIPMessage> msg) { this->handleFinalize(DoIPServerEvent{}, msg); }),
45  StateDescriptor(
48  nullptr)} {
49  m_isOpen = true;
50  m_serverModel->onOpenConnection(*this);
51  m_state = &STATE_DESCRIPTORS[0];
52 
53  LOG_DOIP_INFO("Default connection created, transitioning to SocketInitialized state...");
54  transitionTo(DoIPServerState::WaitRoutingActivation);
55 }
56 
57 ssize_t DoIPDefaultConnection::sendProtocolMessage(const DoIPMessage &msg) {
58  LOG_DOIP_INFO("Default connection: Sending protocol message: {}", fmt::streamed(msg));
59  return static_cast<ssize_t>(msg.size()); // Simulate sending by returning the message size
60 }
61 
62 void DoIPDefaultConnection::closeConnection(DoIPCloseReason reason) {
63  try {
64  LOG_DOIP_INFO("Default connection: Closing connection, reason: {}", fmt::streamed(reason));
65  transitionTo(DoIPServerState::Closed);
66  m_closeReason = reason;
67  m_timerManager.stopAll();
68  notifyConnectionClosed(reason);
69  } catch (const std::exception &e) {
70  LOG_DOIP_ERROR("Error notifying connection closed: {}", e.what());
71 
72  void *callstack[128];
73  int frames = backtrace(callstack, 128);
74  char **strs = backtrace_symbols(callstack, frames);
75 
76  LOG_DOIP_ERROR("Exception during closeConnection: {}", e.what());
77  LOG_DOIP_ERROR("Stack trace:");
78  for (int i = 0; i < frames; ++i) {
79  LOG_DOIP_ERROR("{}", strs[i]);
80  }
81  free(strs);
82  }
83  m_isOpen = false;
84 }
85 
86 DoIPAddress DoIPDefaultConnection::getServerAddress() const {
87  return m_serverModel->serverAddress;
88 }
89 
90 DoIPAddress DoIPDefaultConnection::getClientAddress() const {
91  return m_routedClientAddress;
92 }
93 
94 void DoIPDefaultConnection::setClientAddress(const DoIPAddress &address) {
95  m_routedClientAddress = address;
96 }
97 
98 void DoIPDefaultConnection::handleMessage2(const DoIPMessage &message) {
99  m_state->messageHandler(message);
100 }
101 
102 void DoIPDefaultConnection::transitionTo(DoIPServerState newState) {
103  if (m_state->state == newState) {
104  return;
105  }
106 
107  auto it = std::find_if(
108  STATE_DESCRIPTORS.begin(),
109  STATE_DESCRIPTORS.end(),
110  [newState](const StateDescriptor &desc) {
111  return desc.state == newState;
112  });
113  if (it != STATE_DESCRIPTORS.end()) {
114  LOG_DOIP_INFO("-> Transitioning from state {} to state {}", fmt::streamed(m_state->state), fmt::streamed(newState));
115  m_state = &(*it);
116  startStateTimer(m_state);
117  if (m_state->enterStateHandler) {
118  LOG_DOIP_INFO("Calling enterState handler");
119  m_state->enterStateHandler();
120  }
121  } else {
122  LOG_DOIP_ERROR("Invalid state transition to {}", fmt::streamed(newState));
123  }
124 }
125 
126 std::chrono::milliseconds DoIPDefaultConnection::getTimerDuration(StateDescriptor const *stateDesc) {
127  assert(stateDesc);
128  switch (stateDesc->timer) {
129  case ConnectionTimers::AliveCheck:
130  return m_aliveCheckTimeout;
131  case ConnectionTimers::InitialInactivity:
132  return m_initialInactivityTimeout;
133  case ConnectionTimers::GeneralInactivity:
134  return m_generalInactivityTimeout;
135  case ConnectionTimers::DownstreamResponse:
136  return m_downstreamResponseTimeout;
137  case ConnectionTimers::UserDefined:
138  return stateDesc->timeoutDurationUser;
139  default:
140  return 0ms;
141  }
142 }
143 
144 void DoIPDefaultConnection::startStateTimer(StateDescriptor const *stateDesc) {
145  assert(stateDesc != nullptr);
146  if (stateDesc == nullptr) {
147  return;
148  }
149 
150  m_timerManager.stopAll();
151 
152  std::chrono::milliseconds duration = getTimerDuration(m_state);
153 
154  if (duration.count() == 0) {
155  LOG_DOIP_DEBUG("User-defined timer duration is zero, transitioning immediately to state {}", fmt::streamed(stateDesc->stateAfterTimeout));
156  transitionTo(stateDesc->stateAfterTimeout);
157  return;
158  }
159 
160  LOG_DOIP_DEBUG("Starting timer for state {}: Timer ID {}, duration {}ms", fmt::streamed(stateDesc->state), fmt::streamed(stateDesc->timer), duration.count());
161 
162  std::function<void(ConnectionTimers)> timeoutHandler = [this](ConnectionTimers timerId) { handleTimeout(timerId); };
163  if (stateDesc->timeoutHandler != nullptr) {
164  timeoutHandler = stateDesc->timeoutHandler;
165  }
166 
167  auto id = m_timerManager.addTimer(
168  m_state->timer, duration, timeoutHandler, false);
169 
170  if (id.has_value()) {
171  LOG_DOIP_DEBUG("Started timer {} for {}ms", fmt::streamed(m_state->timer), duration.count());
172  } else {
173  LOG_DOIP_ERROR("Failed to start timer {}", fmt::streamed(m_state->timer));
174  }
175 }
176 
177 void DoIPDefaultConnection::restartStateTimer() {
178  assert(m_state != nullptr);
179  if (!m_timerManager.restartTimer(m_state->timer)) {
180  LOG_DOIP_ERROR("Failed to restart timer {}", fmt::streamed(m_state->timer));
181  }
182 }
183 
184 void DoIPDefaultConnection::handleSocketInitialized(DoIPServerEvent event, OptDoIPMessage msg) {
185  (void)event; // Unused parameter
186  (void)msg; // Unused parameter
187 
188  transitionTo(DoIPServerState::WaitRoutingActivation);
189 }
190 
191 void DoIPDefaultConnection::handleWaitRoutingActivation(DoIPServerEvent event, OptDoIPMessage msg) {
192  (void)event; // Unused parameter
193  (void)msg; // Unused parameter
194 
195  // Implementation of handling routing activation would go here
196  if (!msg) {
197  closeConnection(DoIPCloseReason::SocketError);
198  return;
199  }
200 
201  auto sourceAddress = msg->getSourceAddress();
202  bool hasAddress = sourceAddress.has_value();
203  bool rightPayloadType = (msg->getPayloadType() == DoIPPayloadType::RoutingActivationRequest);
204 
205  if (!hasAddress || !rightPayloadType) {
206  LOG_DOIP_WARN("Invalid Routing Activation Request message");
207  closeConnection(DoIPCloseReason::InvalidMessage);
208  return;
209  }
210 
211  // Set client address in context
212  setClientAddress(sourceAddress.value());
213  // Send Routing Activation Response
214  sendRoutingActivationResponse(sourceAddress.value(), DoIPRoutingActivationResult::RouteActivated);
215  // Transition to Routing Activated state
216  transitionTo(DoIPServerState::RoutingActivated);
217 }
218 
219 void DoIPDefaultConnection::handleRoutingActivated(DoIPServerEvent event, OptDoIPMessage msg) {
220  (void)event; // Unused parameter
221 
222  if (!msg) {
223  closeConnection(DoIPCloseReason::SocketError);
224  return;
225  }
226 
227  auto message = msg.value();
228 
229  switch (message.getPayloadType()) {
230  case DoIPPayloadType::DiagnosticMessage:
231  break;
232  case DoIPPayloadType::AliveCheckResponse:
233  restartStateTimer();
234  return;
235  default:
236  LOG_DOIP_WARN("Received unsupported message type {} in Routing Activated state", fmt::streamed(message.getPayloadType()));
237  sendDiagnosticMessageResponse(ZERO_ADDRESS, DoIPNegativeDiagnosticAck::TransportProtocolError);
238  // closeConnection(DoIPCloseReason::InvalidMessage);
239  return;
240  }
241  auto sourceAddress = message.getSourceAddress();
242  if (!sourceAddress.has_value()) {
243  closeConnection(DoIPCloseReason::InvalidMessage);
244  return;
245  }
246  if (sourceAddress.value() != getClientAddress()) {
247  LOG_DOIP_WARN("Received diagnostic message from unexpected source address {}", fmt::streamed(sourceAddress.value()));
248  sendDiagnosticMessageResponse(sourceAddress.value(), DoIPNegativeDiagnosticAck::InvalidSourceAddress);
249  // closeConnection(DoIPCloseReason::InvalidMessage);
250  return;
251  }
252 
253  auto ack = notifyDiagnosticMessage(message);
254  sendDiagnosticMessageResponse(sourceAddress.value(), ack);
255 
256  // Reset general inactivity timer
257  restartStateTimer();
258 
259  // nack -> stop here
260  if (ack.has_value()) {
261  transitionTo(DoIPServerState::RoutingActivated);
262  return;
263  }
264 
265  if (hasDownstreamHandler()) {
266  auto result = notifyDownstreamRequest(message);
267  LOG_DOIP_DEBUG("Downstream req -> {}", fmt::streamed(result));
268  if (result == DoIPDownstreamResult::Pending) {
269  // wait for downstream response
270  transitionTo(DoIPServerState::WaitDownstreamResponse);
271  } else if (result == DoIPDownstreamResult::Handled) {
272  // no downstream response expected -> go back to "idle"
273  transitionTo(DoIPServerState::RoutingActivated);
274  } else if (result == DoIPDownstreamResult::Error) {
275  // request could not be handled -> issue error and back to idle
276  sendDiagnosticMessageResponse(sourceAddress.value(), DoIPNegativeDiagnosticAck::TargetUnreachable);
277  transitionTo(DoIPServerState::RoutingActivated);
278  }
279  }
280 }
281 
282 void DoIPDefaultConnection::handleWaitAliveCheckResponse(DoIPServerEvent event, OptDoIPMessage msg) {
283  (void)event; // Unused parameter
284  (void)msg; // Unused parameter
285 
286  // Implementation of handling wait alive check response would go here
287  (void)event; // Unused parameter
288 
289  if (!msg) {
290  closeConnection(DoIPCloseReason::SocketError);
291  return;
292  }
293 
294  auto message = msg.value();
295 
296  switch (message.getPayloadType()) {
297  case DoIPPayloadType::DiagnosticMessage: /* fall-through expected */
298  case DoIPPayloadType::AliveCheckResponse:
299  transitionTo(DoIPServerState::RoutingActivated);
300  return;
301  default:
302  LOG_DOIP_WARN("Received unsupported message type {} in Wait Alive Check Response state", fmt::streamed(message.getPayloadType()));
303  sendDiagnosticMessageResponse(ZERO_ADDRESS, DoIPNegativeDiagnosticAck::TransportProtocolError);
304  return;
305  }
306 }
307 
308 void DoIPDefaultConnection::handleWaitDownstreamResponse(DoIPServerEvent event, OptDoIPMessage msg) {
309  (void)event; // Unused parameter
310  (void)msg; // Unused parameter
311 
312  // Implementation of handling wait downstream response would go here
313  LOG_DOIP_CRITICAL("handleWaitDownstreamResponse NOT IMPL");
314 
315 }
316 
317 void DoIPDefaultConnection::handleFinalize(DoIPServerEvent event, OptDoIPMessage msg) {
318  (void)event; // Unused parameter
319  (void)msg; // Unused parameter
320 
321  // Implementation of handling finalize would go here
322  transitionTo(DoIPServerState::Closed);
323 }
324 
325 void DoIPDefaultConnection::handleTimeout(ConnectionTimers timer_id) {
326  LOG_DOIP_WARN("Timeout '{}'", fmt::streamed(timer_id));
327 
328  switch (timer_id) {
329  case ConnectionTimers::InitialInactivity:
331  break;
332  case ConnectionTimers::GeneralInactivity:
333  sendAliveCheckRequest();
334  transitionTo(DoIPServerState::WaitAliveCheckResponse);
335  break;
336  case ConnectionTimers::AliveCheck:
337  if (m_aliveCheckRetry < m_aliveCheckRetryCount) {
338  transitionTo(DoIPServerState::WaitAliveCheckResponse);
339  } else {
340  closeConnection(DoIPCloseReason::AliveCheckTimeout);
341  }
342  break;
343  case ConnectionTimers::DownstreamResponse:
344  LOG_DOIP_WARN("Downstream response timeout occurred");
345  transitionTo(DoIPServerState::RoutingActivated);
346  break;
347  case ConnectionTimers::UserDefined:
348  LOG_DOIP_WARN("User-defined timer -> must be handled separately");
349  break;
350  default:
351  LOG_DOIP_ERROR("Unhandled timeout for timer id {}", fmt::streamed(timer_id));
352  break;
353  }
354 }
355 
356 ssize_t DoIPDefaultConnection::sendRoutingActivationResponse(const DoIPAddress &source_address, DoIPRoutingActivationResult response_code) {
357  // Create routing activation response message
358  DoIPAddress serverAddr = getServerAddress();
359 
360  // Build response payload manually
361  ByteArray payload;
362  payload.writeU16BE(source_address);
363  payload.writeU16BE(serverAddr);
364  payload.push_back(static_cast<uint8_t>(response_code));
365  // Reserved 4 bytes
366  payload.insert(payload.end(), {0x00, 0x00, 0x00, 0x00});
367 
368  DoIPMessage response(DoIPPayloadType::RoutingActivationResponse, std::move(payload));
369  return sendProtocolMessage(response);
370 }
371 
372 ssize_t DoIPDefaultConnection::sendAliveCheckRequest() {
373  auto request = message::makeAliveCheckRequest();
374  return sendProtocolMessage(request);
375 }
376 
377 ssize_t DoIPDefaultConnection::sendDiagnosticMessageResponse(const DoIPAddress &sourceAddress, DoIPDiagnosticAck ack) {
378  ssize_t sentBytes;
379  DoIPAddress targetAddress = getServerAddress();
380  DoIPMessage message;
381 
382  if (ack.has_value()) {
384  sourceAddress,
385  targetAddress,
386  ack.value(),
387  ByteArray{} // Empty payload
388  );
389  } else {
391  sourceAddress,
392  targetAddress,
393  ByteArray{} // Empty payload for ACK
394  );
395  }
396  sentBytes = sendProtocolMessage(message);
397  notifyDiagnosticAckSent(ack);
398  return sentBytes;
399 }
400 
401 ssize_t DoIPDefaultConnection::sendDownstreamResponse(const DoIPAddress &sourceAddress, const ByteArray& payload) {
402  DoIPAddress targetAddress = getServerAddress();
403  DoIPMessage message = message::makeDiagnosticMessage(sourceAddress, targetAddress, payload);
404 
405  return sendProtocolMessage(message);
406 }
407 
408 DoIPDiagnosticAck DoIPDefaultConnection::notifyDiagnosticMessage(const DoIPMessage &msg) {
409  if (m_serverModel->onDiagnosticMessage) {
410  return m_serverModel->onDiagnosticMessage(*this, msg);
411  }
412  return std::nullopt;
413 }
414 
415 void DoIPDefaultConnection::notifyConnectionClosed(DoIPCloseReason reason) {
416  if (m_serverModel && m_serverModel->onCloseConnection) {
417  m_serverModel->onCloseConnection(*this, reason);
418  }
419 }
420 
421 void DoIPDefaultConnection::notifyDiagnosticAckSent(DoIPDiagnosticAck ack) {
422  if (m_serverModel->onDiagnosticNotification) {
423  m_serverModel->onDiagnosticNotification(*this, ack);
424  }
425 }
426 
427 bool DoIPDefaultConnection::hasDownstreamHandler() const {
428  return m_serverModel->hasDownstreamHandler();
429 }
430 
431 
432 
433 DoIPDownstreamResult DoIPDefaultConnection::notifyDownstreamRequest(const DoIPMessage &msg) {
434  if (m_serverModel->onDownstreamRequest) {
435  auto handler = [this](const ByteArray &response, DoIPDownstreamResult result) {
436  this->receiveDownstreamResponse(response, result);
437  };
438  return m_serverModel->onDownstreamRequest(*this, msg, handler);
439  }
440  return DoIPDownstreamResult::Error;
441 }
442 
443 void DoIPDefaultConnection::receiveDownstreamResponse(const ByteArray &response, DoIPDownstreamResult result) {
444  DoIPAddress sa = getServerAddress();
445  DoIPAddress ta = getClientAddress();
446  LOG_DOIP_INFO("Downstream rsp: {} ({})", fmt::streamed(response), fmt::streamed(result));
447  if (result == DoIPDownstreamResult::Handled) {
448  sendProtocolMessage(message::makeDiagnosticMessage(sa, ta, response));
449  } else {
450  sendProtocolMessage(message::makeDiagnosticNegativeResponse(sa, ta, DoIPNegativeDiagnosticAck::TargetUnreachable, {}));
451  }
452  transitionTo(DoIPServerState::RoutingActivated);
453 }
454 
455 } // namespace doip
#define LOG_DOIP_CRITICAL(...)
Definition: Logger.h:129
#define LOG_DOIP_WARN(...)
Definition: Logger.h:127
#define LOG_DOIP_ERROR(...)
Definition: Logger.h:128
#define LOG_DOIP_INFO(...)
Definition: Logger.h:126
#define LOG_DOIP_DEBUG(...)
Definition: Logger.h:125
void handleWaitAliveCheckResponse(DoIPServerEvent event, OptDoIPMessage msg)
void handleRoutingActivated(DoIPServerEvent event, OptDoIPMessage msg)
void handleSocketInitialized(DoIPServerEvent event, OptDoIPMessage msg)
void handleWaitRoutingActivation(DoIPServerEvent event, OptDoIPMessage msg)
void handleWaitDownstreamResponse(DoIPServerEvent event, OptDoIPMessage msg)
DoIPDefaultConnection(UniqueServerModelPtr model)
Constructs a DoIPDefaultConnection.
void handleFinalize(DoIPServerEvent event, OptDoIPMessage msg)
Represents a complete DoIP message with internal ByteArray representation.
Definition: DoIPMessage.h:82
size_t size() const
Gets the size for use with legacy APIs.
Definition: DoIPMessage.h:231
DoIPMessage makeDiagnosticPositiveResponse(const DoIPAddress &sa, const DoIPAddress &ta, const ByteArray &msg_payload)
void b
Definition: DoIPMessage.h:590
DoIPMessage makeAliveCheckRequest()
Create an 'alive check' request.
Definition: DoIPMessage.h:637
DoIPMessage makeDiagnosticNegativeResponse(const DoIPAddress &sa, const DoIPAddress &ta, DoIPNegativeDiagnosticAck nack, const ByteArray &msg_payload)
Creates a diagnostic negative ACK message (NACK).
Definition: DoIPMessage.h:615
DoIPMessage makeDiagnosticMessage(const DoIPAddress &sa, const DoIPAddress &ta, const ByteArray &msg_payload)
Creates a diagnostic message.
Definition: DoIPMessage.h:567
constexpr std::chrono::milliseconds InitialInactivityTimeout(200)
Maximum inactivity time in ms after a TCP connection was accepted.
Definition: AnsiColors.h:3
uint16_t DoIPAddress
Represents a 16-bit DoIP address consisting of high and low significant bytes.
Definition: DoIPAddress.h:26
DoIPServerState
std::optional< DoIPNegativeDiagnosticAck > DoIPDiagnosticAck
Alias for diagnostic acknowledgment type.
constexpr DoIPAddress ZERO_ADDRESS
Definition: DoIPAddress.h:28
DoIPServerEvent
DoIPDownstreamResult
Result of a downstream request initiation.
std::optional< DoIPMessage > OptDoIPMessage
Definition: DoIPMessage.h:65
DoIPCloseReason
Reason for connection closure.
std::unique_ptr< DoIPServerModel > UniqueServerModelPtr
streamed_t< T > streamed(const T &v)
Definition: Logger.h:24
A dynamic array of bytes with utility methods for network protocol handling.
Definition: ByteArray.h:60
void writeU16BE(uint16_t value)
Appends a 16-bit unsigned integer in big-endian format to the end.
Definition: ByteArray.h:110