libdoip  0.1.0
DoIP (Diagnostics over Internet Protocol) ISO 13400 C++17 Library
DoIPClient.cpp
Go to the documentation of this file.
1 #include "DoIPClient.h"
2 #include "DoIPMessage.h"
3 #include "DoIPPayloadType.h"
4 #include "Logger.h"
5 #include <cerrno> // for errno
6 #include <cstring> // for strerror
7 
8 using namespace doip;
9 
10 /*
11  *Set up the connection between client and server
12  */
14 
15  m_tcpSocket = socket(AF_INET, SOCK_STREAM, 0);
16 
17  if (m_tcpSocket >= 0) {
18  LOG_TCP_INFO("Client TCP-Socket created successfully");
19 
20  bool connectedFlag = false;
21  const char *ipAddr = "127.0.0.1";
22  m_serverAddress.sin_family = AF_INET;
23  m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT);
24  inet_aton(ipAddr, &(m_serverAddress.sin_addr));
25 
26  while (!connectedFlag) {
27  m_connected = connect(m_tcpSocket, reinterpret_cast<struct sockaddr *>(&m_serverAddress), sizeof(m_serverAddress));
28  if (m_connected != -1) {
29  connectedFlag = true;
30  LOG_TCP_INFO("Connection to server established");
31  }
32  }
33  }
34 }
35 
37 
38  m_udpSocket = socket(AF_INET, SOCK_DGRAM, 0);
39 
40  if (m_udpSocket >= 0) {
41  LOG_UDP_INFO("Client-UDP-Socket created successfully");
42 
43  m_serverAddress.sin_family = AF_INET;
44  m_serverAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT); // 13400
45  m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
46 
47  m_clientAddress.sin_family = AF_INET;
48  m_clientAddress.sin_port = htons(DOIP_UDP_DISCOVERY_PORT);
49  m_clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
50 
51  // binds the socket to any IP DoIPAddress and the Port Number 13400
52  bind(m_udpSocket, reinterpret_cast<struct sockaddr *>(&m_clientAddress), sizeof(m_clientAddress));
53  }
54 }
55 
57  m_udpAnnouncementSocket = socket(AF_INET, SOCK_DGRAM, 0);
58 
59  if (m_udpAnnouncementSocket >= 0) {
60  LOG_UDP_INFO("Client-Announcement-Socket created successfully");
61 
62  // Allow socket reuse for broadcast
63  int reuse = 1;
64  setsockopt(m_udpAnnouncementSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
65 
66  // Enable broadcast reception
67  int broadcast = 1;
68  if (setsockopt(m_udpAnnouncementSocket, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast)) < 0) {
69  LOG_UDP_ERROR("Failed to enable broadcast reception: {}", strerror(errno));
70  } else {
71  LOG_UDP_INFO("Broadcast reception enabled for announcements");
72  }
73 
74  m_announcementAddress.sin_family = AF_INET;
75  m_announcementAddress.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT); // Port 13401
76  m_announcementAddress.sin_addr.s_addr = htonl(INADDR_ANY);
77 
78  // Bind to port 13401 for Vehicle Announcements
79  if (bind(m_udpAnnouncementSocket, reinterpret_cast<struct sockaddr *>(&m_announcementAddress), sizeof(m_announcementAddress)) < 0) {
80  LOG_UDP_ERROR("Failed to bind announcement socket to port {}: {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT, strerror(errno));
81  } else {
82  LOG_UDP_INFO("Announcement socket bound to port {} successfully", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT);
83  }
84  } else {
85  LOG_UDP_ERROR("Failed to create announcement socket: {}", strerror(errno));
86  }
87 }
88 
89 /*
90  * closes the client-socket
91  */
93  close(m_tcpSocket);
94 }
95 
97  close(m_udpSocket);
98  if (m_udpAnnouncementSocket >= 0) {
99  close(m_udpAnnouncementSocket);
100  }
101 }
102 
106 }
107 
109  DoIPMessage routingActReq = message::makeRoutingActivationRequest(m_sourceAddress);
110  LOG_DOIP_INFO("TX: {}", fmt::streamed(routingActReq));
111  return write(m_tcpSocket, routingActReq.data(), routingActReq.size());
112 }
113 
115  DoIPMessage msg = message::makeDiagnosticMessage(m_sourceAddress, m_logicalAddress, payload);
116  LOG_DOIP_INFO("TX: {}", fmt::streamed(msg));
117 
118  return write(m_tcpSocket, msg.data(), msg.size());
119 }
120 
122  DoIPMessage msg = message::makeAliveCheckResponse(m_sourceAddress);
123  LOG_DOIP_INFO("TX: {}", fmt::streamed(msg));
124  return write(m_tcpSocket, msg.data(), msg.size());
125 }
126 
127 /*
128  * Receive a message from server
129  */
131 
132  ssize_t bytesRead = recv(m_tcpSocket, m_receiveBuf.data(), _maxDataSize, 0);
133 
134  if (bytesRead < 0) {
135  LOG_DOIP_ERROR("Error receiving data from server");
136  return;
137  }
138 
139  if (!bytesRead) // if server is disconnected from client; client gets empty messages
140  {
141  emptyMessageCounter++;
142 
143  if (emptyMessageCounter == 5) {
144  LOG_DOIP_WARN("Received too many empty messages. Reconnect TCP connection");
145  emptyMessageCounter = 0;
146  reconnectServer();
147  }
148  return;
149  }
150 
151  auto optMmsg = DoIPMessage::tryParse(m_receiveBuf.data(), static_cast<size_t>(bytesRead));
152  if (!optMmsg.has_value()) {
153  LOG_DOIP_ERROR("Failed to parse DoIP message from received data");
154  return;
155  }
156  DoIPMessage msg = optMmsg.value();
157  LOG_TCP_INFO("RX: {}", fmt::streamed(msg));
158 }
159 
161 
162  unsigned int length = sizeof(m_clientAddress);
163 
164  // Set socket to timeout after 3 seconds
165  struct timeval timeout;
166  timeout.tv_sec = 3;
167  timeout.tv_usec = 0;
168  setsockopt(m_udpSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
169 
170  int bytesRead;
171  bytesRead = recvfrom(m_udpSocket, m_receiveBuf.data(), _maxDataSize, 0, reinterpret_cast<struct sockaddr *>(&m_clientAddress), &length);
172 
173  if (bytesRead < 0) {
174  if (errno == EAGAIN) {
175  LOG_UDP_WARN("Timeout waiting for UDP response");
176  } else {
177  LOG_UDP_ERROR("Error receiving UDP message: {}", strerror(errno));
178  }
179  return;
180  }
181 
182  LOG_UDP_INFO("Received {} bytes from UDP", bytesRead);
183 
184  auto optMmsg = DoIPMessage::tryParse(m_receiveBuf.data(), static_cast<size_t>(bytesRead));
185  if (!optMmsg.has_value()) {
186  LOG_UDP_ERROR("Failed to parse DoIP message from UDP data");
187  return;
188  }
189 
190  DoIPMessage msg = optMmsg.value();
191 
192  LOG_UDP_INFO("RX: {}", fmt::streamed(msg));
193 }
194 
196  unsigned int length = sizeof(m_announcementAddress);
197  int bytesRead;
198 
199  LOG_UDP_DEBUG("Listening for Vehicle Announcements on port {}", DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT);
200 
201  // Set socket to non-blocking mode for timeout
202  struct timeval timeout;
203  timeout.tv_sec = 2; // 2 second timeout
204  timeout.tv_usec = 0;
205  setsockopt(m_udpAnnouncementSocket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
206 
207  bytesRead = recvfrom(m_udpAnnouncementSocket, m_receiveBuf.data(), _maxDataSize, 0,
208  reinterpret_cast<struct sockaddr *>(&m_announcementAddress), &length);
209  if (bytesRead < 0) {
210  if (errno == EAGAIN) {
211  LOG_UDP_WARN("Timeout waiting for Vehicle Announcement");
212  } else {
213  LOG_UDP_ERROR("Error receiving Vehicle Announcement: {}", strerror(errno));
214  }
215  return false;
216  }
217 
218  auto optMsg = DoIPMessage::tryParse(m_receiveBuf.data(), static_cast<size_t>(bytesRead));
219  if (!optMsg.has_value()) {
220  LOG_UDP_ERROR("Failed to parse Vehicle Announcement message");
221  return false;
222  }
223 
224  DoIPMessage msg = optMsg.value();
225  // Parse and display the announcement information
227  LOG_UDP_INFO("Vehicle Announcement received: {}", fmt::streamed(msg));
228  parseVehicleIdentificationResponse(msg);
229  return true;
230  }
231  return false;
232 }
233 
234 ssize_t DoIPClient::sendVehicleIdentificationRequest(const char *inet_address) {
235 
236  int setAddressError = inet_aton(inet_address, &(m_serverAddress.sin_addr));
237 
238  if (setAddressError != 0) {
239  LOG_UDP_INFO("Address set successfully");
240  } else {
241  LOG_UDP_ERROR("Could not set address. Try again");
242  }
243 
244  int socketError = setsockopt(m_udpSocket, SOL_SOCKET, SO_BROADCAST, &m_broadcast, sizeof(m_broadcast));
245 
246  if (socketError == 0) {
247  LOG_UDP_INFO("Broadcast Option set successfully");
248  }
249 
251 
252  ssize_t bytesSent = sendto(m_udpSocket, vehicleIdReq.data(), vehicleIdReq.size(), 0, reinterpret_cast<struct sockaddr *>(&m_serverAddress), sizeof(m_serverAddress));
253  LOG_UDP_INFO("Sent Vehicle Identification Request to {}:{}", inet_address, ntohs(m_serverAddress.sin_port));
254 
255  if (bytesSent > 0) {
256  LOG_DOIP_INFO("Sending Vehicle Identification Request");
257  }
258 
259  return bytesSent;
260 }
261 
262 /**
263  * Sets the source address for this client
264  * @param address source address for the client
265  */
267  m_sourceAddress = address;
268 }
269 
270 /*
271  * Getter for _sockFD
272  */
274  return m_tcpSocket;
275 }
276 
277 /*
278  * Getter for m_connected
279  */
281  return m_connected;
282 }
283 
284 void DoIPClient::parseVehicleIdentificationResponse(const DoIPMessage &msg) {
285  auto optVin = msg.getVin();
286  auto optEid = msg.getEid();
287  auto optGid = msg.getGid();
288  auto optLogicalAddress = msg.getLogicalAddress();
289  auto optFurtherAction = msg.getFurtherActionRequest();
290 
291  if (!optVin || !optEid || !optGid || !optLogicalAddress || !optFurtherAction) {
292  LOG_DOIP_WARN("Incomplete Vehicle Identification Response received: Missing VIN, EID, GID, Logical Address or Further Action Request");
293  }
294 
295  m_vin = optVin.value();
296  m_eid = optEid.value();
297  m_gid = optGid.value();
298  m_logicalAddress = optLogicalAddress.value();
299  m_furtherActionReqResult = optFurtherAction.value();
300 }
301 
303  std::ostringstream ss;
304  // output VIN
305  ss << "VIN: " ;
306  if (Logger::colorsSupported()) {
307  ss << ansi::bold_green;
308  }
309  ss << m_vin << ansi::reset ;
310  LOG_DOIP_INFO(ss.str());
311 
312  // output LogicalAddress
313  ss = std::ostringstream{};
314  ss << "LA : ";
315  if (Logger::colorsSupported()) {
316  ss << ansi::bold_green;
317  }
318  ss << m_logicalAddress << ansi::reset;
319  LOG_DOIP_INFO(ss.str());
320 
321  // output EID
322  ss = std::ostringstream{};
323  ss << "EID: ";
324  if (Logger::colorsSupported()) {
325  ss << ansi::bold_green;
326  }
327  ss << m_eid << ansi::reset;
328  LOG_DOIP_INFO(ss.str());
329 
330  // output GID
331  ss = std::ostringstream{};
332  ss << "GID: ";
333  if (Logger::colorsSupported()) {
334  ss << ansi::bold_green;
335  }
336  ss << m_gid << ansi::reset;
337  LOG_DOIP_INFO(ss.str());
338 
339  // output FurtherActionRequest
340  ss = std::ostringstream{};
341  ss << "FAR: ";
342  if (Logger::colorsSupported()) {
343  ss << ansi::bold_green;
344  }
345  ss << m_furtherActionReqResult << ansi::reset;
346  LOG_DOIP_INFO(ss.str());
347 }
#define LOG_TCP_INFO(...)
Definition: Logger.h:142
#define LOG_UDP_ERROR(...)
Definition: Logger.h:136
#define LOG_UDP_WARN(...)
Definition: Logger.h:135
#define LOG_UDP_INFO(...)
Definition: Logger.h:134
#define LOG_DOIP_WARN(...)
Definition: Logger.h:127
#define LOG_DOIP_ERROR(...)
Definition: Logger.h:128
#define LOG_UDP_DEBUG(...)
Definition: Logger.h:133
#define LOG_DOIP_INFO(...)
Definition: Logger.h:126
bool receiveVehicleAnnouncement()
Definition: DoIPClient.cpp:195
void printVehicleInformationResponse()
Definition: DoIPClient.cpp:302
void startTcpConnection()
Definition: DoIPClient.cpp:13
void closeUdpConnection()
Definition: DoIPClient.cpp:96
void receiveUdpMessage()
Definition: DoIPClient.cpp:160
void startUdpConnection()
Definition: DoIPClient.cpp:36
ssize_t sendVehicleIdentificationRequest(const char *inet_address)
Definition: DoIPClient.cpp:234
ssize_t sendAliveCheckResponse()
Sends a alive check response containing the clients source address to the server.
Definition: DoIPClient.cpp:121
void startAnnouncementListener()
Definition: DoIPClient.cpp:56
void setSourceAddress(const DoIPAddress &address)
Sets the source address for this client.
Definition: DoIPClient.cpp:266
ssize_t sendDiagnosticMessage(const ByteArray &payload)
Sends a diagnostic message to the server.
Definition: DoIPClient.cpp:114
void reconnectServer()
Definition: DoIPClient.cpp:103
void closeTcpConnection()
Definition: DoIPClient.cpp:92
ssize_t sendRoutingActivationRequest()
Definition: DoIPClient.cpp:108
Represents a complete DoIP message with internal ByteArray representation.
Definition: DoIPMessage.h:82
static std::optional< DoIPMessage > tryParse(const uint8_t *data, size_t length)
Parse a DoIP message from raw data.
Definition: DoIPMessage.h:425
std::optional< DoIPAddress > getLogicalAddress() const
Get the Logical Address of the message (if message is a Vehicle Identification Response).
Definition: DoIPMessage.h:288
size_t size() const
Gets the size for use with legacy APIs.
Definition: DoIPMessage.h:231
std::optional< DoIpEid > getEid() const
Get the entity id (EID) if message is a Vehicle Identification Response.
Definition: DoIPMessage.h:327
std::optional< DoIpVin > getVin() const
Get the vehicle identification number (VIN) if message is a Vehicle Identification Response.
Definition: DoIPMessage.h:314
const uint8_t * data() const
Gets direct pointer to the message data (for legacy APIs).
Definition: DoIPMessage.h:222
std::optional< DoIPFurtherAction > getFurtherActionRequest() const
Get the Further Action Request object if message is a Vehicle Identification Response.
Definition: DoIPMessage.h:353
DoIPPayloadType getPayloadType() const
Gets the payload type of this message.
Definition: DoIPMessage.h:152
std::optional< DoIpGid > getGid() const
Get the group id (GID) if message is a Vehicle Identification Response.
Definition: DoIPMessage.h:340
static bool colorsSupported()
Definition: Logger.h:103
constexpr const char * reset
Definition: AnsiColors.h:7
constexpr const char * bold_green
Definition: AnsiColors.h:22
DoIPMessage makeAliveCheckResponse(const DoIPAddress &sa)
Create an 'alive check' response.
Definition: DoIPMessage.h:647
DoIPMessage makeVehicleIdentificationRequest()
Creates a vehicle identification request message.
Definition: DoIPMessage.h:513
DoIPMessage makeDiagnosticMessage(const DoIPAddress &sa, const DoIPAddress &ta, const ByteArray &msg_payload)
Creates a diagnostic message.
Definition: DoIPMessage.h:567
DoIPMessage makeRoutingActivationRequest(const DoIPAddress &ea, DoIPRoutingActivationType actType=DoIPRoutingActivationType::Default)
Creates a routing activation request message.
Definition: DoIPMessage.h:660
Definition: AnsiColors.h:3
uint16_t DoIPAddress
Represents a 16-bit DoIP address consisting of high and low significant bytes.
Definition: DoIPAddress.h:26
const int _maxDataSize
Definition: DoIPClient.h:18
@ VehicleIdentificationResponse
Vehicle Identification Response (Vehicle Announcement) Response to a Vehicle Identification Request o...
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