19 if (m_running.load()) {
26 m_receiveBuf.reserve(DOIP_MAXIMUM_MTU);
35 void DoIPServer::daemonize() {
68 if (chdir(
"/") != 0) {
73 int fd = open(
"/dev/null", O_RDWR);
75 dup2(fd, STDIN_FILENO);
76 dup2(fd, STDOUT_FILENO);
77 dup2(fd, STDERR_FILENO);
78 if (fd > STDERR_FILENO) {
82 LOG_DOIP_WARN(
"Failed to open /dev/null: {}", strerror(errno));
91 void DoIPServer::stop() {
93 m_running.store(
false);
100 for (
auto &thread : m_workerThreads) {
101 if (thread.joinable()) {
105 m_workerThreads.clear();
113 void DoIPServer::connectionHandlerThread(std::unique_ptr<DoIPConnection> connection) {
116 while (m_running.load() && connection->isSocketActive()) {
117 int result = connection->receiveTcpMessage();
135 m_tcp_sock = socket(AF_INET, SOCK_STREAM, 0);
136 if (m_tcp_sock < 0) {
137 LOG_TCP_ERROR(
"Failed to create TCP socket: {}", strerror(errno));
143 if (setsockopt(m_tcp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse)) < 0) {
144 LOG_TCP_WARN(
"Failed to set SO_REUSEADDR: {}", strerror(errno));
147 m_serverAddress.sin_family = AF_INET;
148 m_serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
152 if (bind(m_tcp_sock,
reinterpret_cast<const struct sockaddr *
>(&m_serverAddress),
sizeof(m_serverAddress)) < 0) {
153 LOG_TCP_ERROR(
"Failed to bind TCP socket: {}", strerror(errno));
169 LOG_UDP_DEBUG(
"Setting up UDP socket on port {}", DOIP_UDP_DISCOVERY_PORT);
171 m_udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
172 if (m_udp_sock < 0) {
173 perror(
"Failed to create socket");
178 struct timeval timeout;
181 setsockopt(m_udp_sock, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof(timeout));
185 setsockopt(m_udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuse,
sizeof(reuse));
188 struct sockaddr_in server_addr;
189 memset(&server_addr, 0,
sizeof(server_addr));
190 server_addr.sin_family = AF_INET;
191 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
192 server_addr.sin_port = htons(DOIP_UDP_DISCOVERY_PORT);
194 if (bind(m_udp_sock,
reinterpret_cast<struct sockaddr *
>(&server_addr),
sizeof(server_addr)) < 0) {
195 perror(
"Failed to bind socket");
201 setMulticastGroup(
"224.0.0.2");
202 LOG_UDP_INFO(
"UDP socket successfully bound to port {} with multicast group", DOIP_UDP_DISCOVERY_PORT);
204 LOG_UDP_INFO(
"UDP socket successfully bound to port {} with broadcast", DOIP_UDP_DISCOVERY_PORT);
208 "Socket {} bound to {}:{}",
210 inet_ntoa(m_serverAddress.sin_addr),
211 ntohs(m_serverAddress.sin_port));
213 m_running.store(
true);
214 m_workerThreads.emplace_back([
this]() { udpListenerThread(); });
215 m_workerThreads.emplace_back([
this]() { udpAnnouncementThread(); });
221 m_running.store(
false);
222 for (
auto &thread : m_workerThreads) {
223 if (thread.joinable()) {
264 m_FurtherActionReq = furtherActionRequired;
278 LOG_DOIP_INFO(
"Vehicle announcements will use loopback (127.0.0.1)");
280 LOG_DOIP_INFO(
"Vehicle announcements will use broadcast (255.255.255.255)");
284 void DoIPServer::setMulticastGroup(
const char *address)
const {
288 int setPort = setsockopt(m_udp_sock, SOL_SOCKET, SO_REUSEADDR, &loop,
sizeof(loop));
296 mreq.imr_multiaddr.s_addr = inet_addr(address);
297 mreq.imr_interface.s_addr = htonl(INADDR_ANY);
300 int setGroup = setsockopt(m_udp_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
reinterpret_cast<const char *
>(&mreq),
sizeof(mreq));
303 LOG_UDP_ERROR(
"Setting address failed: {}", strerror(errno));
310 return sendUdpResponse(msg);
314 void DoIPServer::udpListenerThread() {
315 socklen_t client_len =
sizeof(m_clientAddress);
320 ssize_t received = recvfrom(m_udp_sock, m_receiveBuf.data(),
sizeof(m_receiveBuf), 0,
321 reinterpret_cast<struct sockaddr *
>(&m_clientAddress), &client_len);
324 if (errno == EAGAIN ) {
329 perror(
"recvfrom error");
335 std::scoped_lock lock(m_mutex);
336 char client_ip[INET_ADDRSTRLEN];
337 inet_ntop(AF_INET, &m_clientAddress.sin_addr, client_ip,
sizeof(client_ip));
338 m_clientIp = std::string(client_ip);
339 m_clientPort = ntohs(m_clientAddress.sin_port);
341 LOG_UDP_INFO(
"Received {} bytes from {}:{}", received, m_clientIp, m_clientPort);
344 if (!optHeader.has_value()) {
347 if (errno == EAGAIN ) {
354 auto plType = optHeader->first;
358 ssize_t sentBytes = 0;
362 sentBytes = sendUdpResponse(msg);
366 LOG_DOIP_ERROR(
"Invalid payload type 0x{:04X} received (receiveUdpMessage())",
static_cast<uint16_t
>(plType));
372 if (errno == EAGAIN ) {
384 void DoIPServer::udpAnnouncementThread() {
388 for (
int i = 0; i < m_config.
announceCount && m_running; i++) {
389 sendVehicleAnnouncement();
396 ssize_t DoIPServer::sendVehicleAnnouncement() {
399 struct sockaddr_in dest_addr;
400 memset(&dest_addr, 0,
sizeof(dest_addr));
401 dest_addr.sin_family = AF_INET;
402 dest_addr.sin_port = htons(DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT);
406 dest_ip =
"127.0.0.1";
407 inet_pton(AF_INET, dest_ip, &dest_addr.sin_addr);
409 dest_ip =
"255.255.255.255";
410 dest_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
414 setsockopt(m_udp_sock, SOL_SOCKET, SO_BROADCAST, &broadcast,
sizeof(broadcast));
417 ssize_t sentBytes = sendto(m_udp_sock, msg.
data(), msg.
size(), 0,
418 reinterpret_cast<struct sockaddr *
>(&dest_addr),
sizeof(dest_addr));
422 LOG_UDP_INFO(
"Sent Vehicle Announcement: {} bytes to {}:{}",
423 sentBytes, dest_ip, DOIP_UDP_TEST_EQUIPMENT_REQUEST_PORT);
425 LOG_UDP_ERROR(
"Failed to send announcement: {}", strerror(errno));
430 ssize_t DoIPServer::sendUdpResponse(
DoIPMessage msg) {
431 auto sentBytes = sendto(m_udp_sock, msg.
data(), msg.
size(), 0,
432 reinterpret_cast<struct sockaddr *
>(&m_clientAddress),
sizeof(m_clientAddress));
437 sentBytes, m_clientIp, ntohs(m_clientAddress.sin_port));
#define LOG_TCP_INFO(...)
#define LOG_UDP_ERROR(...)
#define LOG_TCP_ERROR(...)
#define LOG_UDP_INFO(...)
#define LOG_DOIP_WARN(...)
#define LOG_DOIP_ERROR(...)
#define LOG_TCP_WARN(...)
#define LOG_UDP_DEBUG(...)
#define LOG_DOIP_INFO(...)
#define LOG_DOIP_DEBUG(...)
Represents a complete DoIP message with internal ByteArray representation.
static std::optional< std::pair< DoIPPayloadType, uint32_t > > tryParseHeader(const uint8_t *data, size_t length)
Gets the payload type from raw data.
size_t size() const
Gets the size for use with legacy APIs.
const uint8_t * data() const
Gets direct pointer to the message data (for legacy APIs).
void setLoopbackMode(bool useLoopback)
Enable/disable loopback mode for announcements (no broadcast).
bool setupTcpSocket()
Initialize and bind the TCP socket for DoIP.
void setAnnounceNum(int Num)
Set the number of vehicle announcements to send.
void setAnnounceInterval(unsigned int Interval)
Set the interval between announcements in milliseconds.
void setVin(const std::string &VINString)
Set VIN from a 17-character string.
void setEid(uint64_t nputEID)
Set EID value.
void setGid(uint64_t inputGID)
Set GID value.
void closeTcpSocket()
Close the TCP socket if open.
void setFurtherActionRequired(DoIPFurtherAction furtherActionRequired)
Set further action requirement status.
DoIPServer(const ServerConfig &config=DefaultServerConfig)
Construct a DoIP server with the given configuration.
bool setDefaultEid()
Sets the EID to a default value based on the MAC address.
bool setupUdpSocket()
Initialize and bind the UDP socket for announcements and UDP messages.
void setLogicalGatewayAddress(DoIPAddress logicalAddress)
Set the logical DoIP gateway address.
void closeUdpSocket()
Close the UDP socket if open.
static const GenericFixedId Zero
Static instance containing only zeros.
static constexpr size_t ID_LENGTH
Length of the identifier in bytes.
DoIPMessage makeNegativeAckMessage(DoIPNegativeAck nack)
Creates a generic DoIP negative response (NACK).
DoIPMessage makeVehicleIdentificationResponse(const DoIpVin &vin, const DoIPAddress &logicalAddress, const DoIpEid &entityType, const DoIpGid &groupId, DoIPFurtherAction furtherAction=DoIPFurtherAction::NoFurtherAction, DoIPSyncStatus syncStatus=DoIPSyncStatus::GidVinSynchronized)
Creates a vehicle identification response message.
uint16_t DoIPAddress
Represents a 16-bit DoIP address consisting of high and low significant bytes.
constexpr size_t DOIP_HEADER_SIZE
Size of the DoIP header.
GenericFixedId< 17, true, '0'> DoIpVin
Vehicle Identification Number (VIN) - 17 bytes according to ISO 3779 Padded with ASCII '0' characters...
bool getFirstMacAddress(MacAddress &mac)
Retrieves the MAC address of the first available network interface.
@ VehicleIdentificationRequest
Vehicle Identification Request Request for vehicle identification.
GenericFixedId< 6, false > DoIpEid
Entity Identifier (EID) - 6 bytes for unique entity identification.
constexpr int DOIP_SERVER_TCP_PORT
std::array< uint8_t, 6 > MacAddress
Type alias for MAC address (6 bytes)
GenericFixedId< 6, false > DoIpGid
Group Identifier (GID) - 6 bytes for group identification.
streamed_t< T > streamed(const T &v)
Server configuration structure used to initialize a DoIP server.
unsigned int announceInterval
DoIPAddress logicalAddress